mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Bye bye openapi (#1081)
* add DateTime sans mgo * change all uses of strfmt.DateTime to common.DateTime, remove test strfmt usage * remove api tests, system-test dep on api test multiple reasons to remove the api tests: * awkward dependency with fn_go meant generating bindings on a branched fn to vendor those to test new stuff. this is at a minimum not at all intuitive, worth it, nor a fun way to spend the finite amount of time we have to live. * api tests only tested a subset of functionality that the server/ api tests already test, and we risk having tests where one tests some thing and the other doesn't. let's not. we have too many test suites as it is, and these pretty much only test that we updated the fn_go bindings, which is actually a hassle as noted above and the cli will pretty quickly figure out anyway. * fn_go relies on openapi, which relies on mgo, which is deprecated and we'd like to remove as a dependency. openapi is a _huge_ dep built in a NIH fashion, that cannot simply remove the mgo dep as users may be using it. we've now stolen their date time and otherwise killed usage of it in fn core, for fn_go it still exists but that's less of a problem. * update deps removals: * easyjson * mgo * go-openapi * mapstructure * fn_go * purell * go-validator also, had to lock docker. we shouldn't use docker on master anyway, they strongly advise against that. had no luck with latest version rev, so i locked it to what we were using before. until next time. the rest is just playing dep roulette, those end up removing a ton tho * fix exec test to work * account for john le cache
This commit is contained in:
203
Gopkg.lock
generated
203
Gopkg.lock
generated
@@ -13,24 +13,6 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
|
revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/PuerkitoBio/purell"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/PuerkitoBio/urlesc"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/asaskevich/govalidator"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f"
|
|
||||||
version = "v9"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/aws/aws-sdk-go"
|
name = "github.com/aws/aws-sdk-go"
|
||||||
packages = [
|
packages = [
|
||||||
@@ -81,7 +63,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/containerd/continuity"
|
name = "github.com/containerd/continuity"
|
||||||
packages = ["pathdriver"]
|
packages = ["pathdriver"]
|
||||||
revision = "d3c23511c1bf5851696cba83143d9cbcd666869b"
|
revision = "246e49050efdf45e8f17fbbcf1547ee376f9939e"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/coreos/go-semver"
|
name = "github.com/coreos/go-semver"
|
||||||
@@ -96,7 +78,6 @@
|
|||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
name = "github.com/docker/docker"
|
name = "github.com/docker/docker"
|
||||||
packages = [
|
packages = [
|
||||||
"api/types",
|
"api/types",
|
||||||
@@ -136,8 +117,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/docker/go-units"
|
name = "github.com/docker/go-units"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "0dadbb0345b35ec7ef35e228dabb8de89a65bf52"
|
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
|
||||||
version = "v0.3.2"
|
version = "v0.3.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -146,20 +127,7 @@
|
|||||||
".",
|
".",
|
||||||
"utils"
|
"utils"
|
||||||
]
|
]
|
||||||
revision = "0ae900cf56643afe316fcc87323c5845da0531c1"
|
revision = "1eb29530716f262bad5b83eb9a5b3f7483636949"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/fnproject/fn_go"
|
|
||||||
packages = [
|
|
||||||
"client",
|
|
||||||
"client/apps",
|
|
||||||
"client/call",
|
|
||||||
"client/operations",
|
|
||||||
"client/routes",
|
|
||||||
"models"
|
|
||||||
]
|
|
||||||
revision = "e2f92e36625a4b93c596ac3b912a4994ae574f64"
|
|
||||||
version = "0.2.6"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/fsouza/go-dockerclient"
|
name = "github.com/fsouza/go-dockerclient"
|
||||||
@@ -200,77 +168,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/go-ini/ini"
|
name = "github.com/go-ini/ini"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "6333e38ac20b8949a8dd68baa3650f4dee8f39f0"
|
revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5"
|
||||||
version = "v1.33.0"
|
version = "v1.37.0"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/analysis"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "f59a71f0ece6f9dfb438be7f45148f006cbad88e"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/errors"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "7bcb96a367bac6b76e6e42fa84155bb5581dcff8"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/jsonpointer"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/jsonreference"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/loads"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "2a2b323bab96e6b1fdee110e57d959322446e9c9"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/runtime"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"client",
|
|
||||||
"logger",
|
|
||||||
"middleware",
|
|
||||||
"middleware/denco",
|
|
||||||
"middleware/header",
|
|
||||||
"middleware/untyped",
|
|
||||||
"security"
|
|
||||||
]
|
|
||||||
revision = "62281b694b396a17fe3e4313ee8b0ca2c3cca719"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/spec"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "a3092263d8b39f66ff6fe87b0109668eca1e24ff"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/strfmt"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "6ba31556a6c60db8615afb9d8eddae7aae15eb48"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/swag"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "ceb469cb0fdf2d792f28d771bc05da6c606f55e5"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/validate"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "180bba53b98899f743a112e568bed9e2ef31aa20"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/go-sql-driver/mysql"
|
name = "github.com/go-sql-driver/mysql"
|
||||||
@@ -293,8 +192,8 @@
|
|||||||
"ptypes/empty",
|
"ptypes/empty",
|
||||||
"ptypes/timestamp"
|
"ptypes/timestamp"
|
||||||
]
|
]
|
||||||
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||||
version = "v1.0.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -302,18 +201,6 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/gorilla/context"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
|
||||||
version = "v1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/gorilla/mux"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
|
|
||||||
version = "v1.6.1"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/grpc-ecosystem/go-grpc-middleware"
|
name = "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -332,7 +219,7 @@
|
|||||||
".",
|
".",
|
||||||
"reflectx"
|
"reflectx"
|
||||||
]
|
]
|
||||||
revision = "cf35089a197953c69420c8d0cecda90809764b1d"
|
revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -343,16 +230,6 @@
|
|||||||
]
|
]
|
||||||
revision = "90697d60dd844d5ef6ff15135d0203f65d2f53b8"
|
revision = "90697d60dd844d5ef6ff15135d0203f65d2f53b8"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/mailru/easyjson"
|
|
||||||
packages = [
|
|
||||||
"buffer",
|
|
||||||
"jlexer",
|
|
||||||
"jwriter"
|
|
||||||
]
|
|
||||||
revision = "8b799c424f57fa123fc63a99d6383bc6e4c02578"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/mattn/go-isatty"
|
name = "github.com/mattn/go-isatty"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@@ -362,20 +239,14 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/mattn/go-sqlite3"
|
name = "github.com/mattn/go-sqlite3"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "6c771bb9887719704b210e87e934f08be014bdb1"
|
revision = "25ecb14adfc7543176f7d85291ec7dba82c6f7e4"
|
||||||
version = "v1.6.0"
|
version = "v1.9.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||||
packages = ["pbutil"]
|
packages = ["pbutil"]
|
||||||
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
|
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||||
version = "v1.0.0"
|
version = "v1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/mitchellh/mapstructure"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/opencontainers/go-digest"
|
name = "github.com/opencontainers/go-digest"
|
||||||
@@ -452,7 +323,7 @@
|
|||||||
"internal/bitbucket.org/ww/goautoneg",
|
"internal/bitbucket.org/ww/goautoneg",
|
||||||
"model"
|
"model"
|
||||||
]
|
]
|
||||||
revision = "38c53a9f4bfcd932d1b00bfc65e256a7fba6b37a"
|
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -463,7 +334,7 @@
|
|||||||
"nfs",
|
"nfs",
|
||||||
"xfs"
|
"xfs"
|
||||||
]
|
]
|
||||||
revision = "780932d4fbbe0e69b84c34c20f5c8d0981e109ea"
|
revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/sirupsen/logrus"
|
name = "github.com/sirupsen/logrus"
|
||||||
@@ -476,8 +347,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/ugorji/go"
|
name = "github.com/ugorji/go"
|
||||||
packages = ["codec"]
|
packages = ["codec"]
|
||||||
revision = "9831f2c3ac1068a78f50999a30db84270f647af6"
|
revision = "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab"
|
||||||
version = "v1.1"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "go.opencensus.io"
|
name = "go.opencensus.io"
|
||||||
@@ -505,7 +376,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = ["ssh/terminal"]
|
packages = ["ssh/terminal"]
|
||||||
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
|
revision = "7f39a6fea4fe9364fb61e1def6a268a51b4f3a06"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -513,14 +384,14 @@
|
|||||||
packages = [
|
packages = [
|
||||||
"context",
|
"context",
|
||||||
"context/ctxhttp",
|
"context/ctxhttp",
|
||||||
|
"http/httpguts",
|
||||||
"http2",
|
"http2",
|
||||||
"http2/hpack",
|
"http2/hpack",
|
||||||
"idna",
|
"idna",
|
||||||
"internal/timeseries",
|
"internal/timeseries",
|
||||||
"lex/httplex",
|
|
||||||
"trace"
|
"trace"
|
||||||
]
|
]
|
||||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -535,7 +406,7 @@
|
|||||||
"unix",
|
"unix",
|
||||||
"windows"
|
"windows"
|
||||||
]
|
]
|
||||||
revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835"
|
revision = "ad87a3a340fa7f3bed189293fbfa7a9b7e021ae1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "golang.org/x/text"
|
name = "golang.org/x/text"
|
||||||
@@ -553,8 +424,7 @@
|
|||||||
"unicode/bidi",
|
"unicode/bidi",
|
||||||
"unicode/cldr",
|
"unicode/cldr",
|
||||||
"unicode/norm",
|
"unicode/norm",
|
||||||
"unicode/rangetable",
|
"unicode/rangetable"
|
||||||
"width"
|
|
||||||
]
|
]
|
||||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
@@ -563,19 +433,19 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/time"
|
name = "golang.org/x/time"
|
||||||
packages = ["rate"]
|
packages = ["rate"]
|
||||||
revision = "26559e0f760e39c24d730d3224364aef164ee23f"
|
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/api"
|
name = "google.golang.org/api"
|
||||||
packages = ["support/bundler"]
|
packages = ["support/bundler"]
|
||||||
revision = "e4126357c891acdef6dcd7805daa4c6533be6544"
|
revision = "2eea9ba0a3d94f6ab46508083e299a00bbbc65f6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2"
|
revision = "32ee49c4dd805befd833990acba36cb75042378c"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
@@ -589,9 +459,11 @@
|
|||||||
"credentials",
|
"credentials",
|
||||||
"encoding",
|
"encoding",
|
||||||
"encoding/proto",
|
"encoding/proto",
|
||||||
"grpclb/grpc_lb_v1/messages",
|
|
||||||
"grpclog",
|
"grpclog",
|
||||||
"internal",
|
"internal",
|
||||||
|
"internal/backoff",
|
||||||
|
"internal/channelz",
|
||||||
|
"internal/grpcrand",
|
||||||
"keepalive",
|
"keepalive",
|
||||||
"metadata",
|
"metadata",
|
||||||
"naming",
|
"naming",
|
||||||
@@ -604,8 +476,8 @@
|
|||||||
"tap",
|
"tap",
|
||||||
"transport"
|
"transport"
|
||||||
]
|
]
|
||||||
revision = "8e4536a86ab602859c20df5ebfd0bd4228d08655"
|
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
||||||
version = "v1.10.0"
|
version = "v1.13.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/go-playground/validator.v8"
|
name = "gopkg.in/go-playground/validator.v8"
|
||||||
@@ -613,24 +485,15 @@
|
|||||||
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
|
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
|
||||||
version = "v8.18.2"
|
version = "v8.18.2"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "v2"
|
|
||||||
name = "gopkg.in/mgo.v2"
|
|
||||||
packages = [
|
|
||||||
"bson",
|
|
||||||
"internal/json"
|
|
||||||
]
|
|
||||||
revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb"
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
version = "v2.2.0"
|
version = "v2.2.1"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "c988af213ce5e034443bccfd948933a7f8ac9caf98a7c8a4730202dbf32372e7"
|
inputs-digest = "643d4a3862aeaa6f1115edc26fa3f1f3a6822bb17c01d60fcbdf9ce98bf183ac"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|||||||
11
Gopkg.toml
11
Gopkg.toml
@@ -28,18 +28,10 @@ ignored = ["github.com/fnproject/fn/cli",
|
|||||||
name = "github.com/boltdb/bolt"
|
name = "github.com/boltdb/bolt"
|
||||||
revision = "fa5367d20c994db73282594be0146ab221657943"
|
revision = "fa5367d20c994db73282594be0146ab221657943"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/fnproject/fn_go"
|
|
||||||
version = "0.2.6"
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/gin-gonic/gin"
|
name = "github.com/gin-gonic/gin"
|
||||||
version = "v1.2"
|
version = "v1.2"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/strfmt"
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/google/btree"
|
name = "github.com/google/btree"
|
||||||
@@ -82,3 +74,6 @@ ignored = ["github.com/fnproject/fn/cli",
|
|||||||
name = "github.com/dchest/siphash"
|
name = "github.com/dchest/siphash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
||||||
|
[[override]]
|
||||||
|
name = "github.com/docker/docker"
|
||||||
|
revision = "29fc64b590badcb1c3f5beff7563ffd31eb58974"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/fnproject/fn/api/id"
|
"github.com/fnproject/fn/api/id"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/fnproject/fn/fnext"
|
"github.com/fnproject/fn/fnext"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"go.opencensus.io/stats"
|
"go.opencensus.io/stats"
|
||||||
"go.opencensus.io/trace"
|
"go.opencensus.io/trace"
|
||||||
@@ -804,7 +803,7 @@ func (a *agent) prepCold(ctx context.Context, call *call, tok ResourceToken, ch
|
|||||||
deadline := time.Now().Add(time.Duration(call.Timeout) * time.Second)
|
deadline := time.Now().Add(time.Duration(call.Timeout) * time.Second)
|
||||||
|
|
||||||
// add Fn-specific information to the config to shove everything into env vars for cold
|
// add Fn-specific information to the config to shove everything into env vars for cold
|
||||||
call.Config["FN_DEADLINE"] = strfmt.DateTime(deadline).String()
|
call.Config["FN_DEADLINE"] = common.DateTime(deadline).String()
|
||||||
call.Config["FN_METHOD"] = call.Model().Method
|
call.Config["FN_METHOD"] = call.Model().Method
|
||||||
call.Config["FN_REQUEST_URL"] = call.Model().URL
|
call.Config["FN_REQUEST_URL"] = call.Model().URL
|
||||||
call.Config["FN_CALL_ID"] = call.Model().ID
|
call.Config["FN_CALL_ID"] = call.Model().ID
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/fnproject/fn/api/common"
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/id"
|
"github.com/fnproject/fn/api/id"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -128,7 +127,7 @@ func FromRequest(a Agent, app *models.App, path string, req *http.Request) CallO
|
|||||||
Config: buildConfig(app, route),
|
Config: buildConfig(app, route),
|
||||||
Annotations: buildAnnotations(app, route),
|
Annotations: buildAnnotations(app, route),
|
||||||
Headers: req.Header,
|
Headers: req.Header,
|
||||||
CreatedAt: strfmt.DateTime(time.Now()),
|
CreatedAt: common.DateTime(time.Now()),
|
||||||
URL: reqURL(req),
|
URL: reqURL(req),
|
||||||
Method: req.Method,
|
Method: req.Method,
|
||||||
AppID: app.ID,
|
AppID: app.ID,
|
||||||
@@ -373,7 +372,7 @@ func (c *call) Start(ctx context.Context) error {
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.StartedAt = strfmt.DateTime(time.Now())
|
c.StartedAt = common.DateTime(time.Now())
|
||||||
c.Status = "running"
|
c.Status = "running"
|
||||||
|
|
||||||
if !c.isLB {
|
if !c.isLB {
|
||||||
@@ -411,7 +410,7 @@ func (c *call) End(ctx context.Context, errIn error) error {
|
|||||||
ctx, span := trace.StartSpan(ctx, "agent_call_end")
|
ctx, span := trace.StartSpan(ctx, "agent_call_end")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
c.CompletedAt = strfmt.DateTime(time.Now())
|
c.CompletedAt = common.DateTime(time.Now())
|
||||||
|
|
||||||
switch errIn {
|
switch errIn {
|
||||||
case nil:
|
case nil:
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/fnproject/fn/api/common"
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/fsouza/go-dockerclient"
|
"github.com/fsouza/go-dockerclient"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -536,7 +535,7 @@ func cherryPick(ds *docker.Stats) drivers.Stat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return drivers.Stat{
|
return drivers.Stat{
|
||||||
Timestamp: strfmt.DateTime(ds.Read),
|
Timestamp: common.DateTime(ds.Read),
|
||||||
Metrics: map[string]uint64{
|
Metrics: map[string]uint64{
|
||||||
// source: https://godoc.org/github.com/fsouza/go-dockerclient#Stats
|
// source: https://godoc.org/github.com/fsouza/go-dockerclient#Stats
|
||||||
// ex (for future expansion): {"read":"2016-08-03T18:08:05Z","pids_stats":{},"network":{},"networks":{"eth0":{"rx_bytes":508,"tx_packets":6,"rx_packets":6,"tx_bytes":508}},"memory_stats":{"stats":{"cache":16384,"pgpgout":281,"rss":8826880,"pgpgin":2440,"total_rss":8826880,"hierarchical_memory_limit":536870912,"total_pgfault":3809,"active_anon":8843264,"total_active_anon":8843264,"total_pgpgout":281,"total_cache":16384,"pgfault":3809,"total_pgpgin":2440},"max_usage":8953856,"usage":8953856,"limit":536870912},"blkio_stats":{"io_service_bytes_recursive":[{"major":202,"op":"Read"},{"major":202,"op":"Write"},{"major":202,"op":"Sync"},{"major":202,"op":"Async"},{"major":202,"op":"Total"}],"io_serviced_recursive":[{"major":202,"op":"Read"},{"major":202,"op":"Write"},{"major":202,"op":"Sync"},{"major":202,"op":"Async"},{"major":202,"op":"Total"}]},"cpu_stats":{"cpu_usage":{"percpu_usage":[47641874],"usage_in_usermode":30000000,"total_usage":47641874},"system_cpu_usage":8880800500000000,"throttling_data":{}},"precpu_stats":{"cpu_usage":{"percpu_usage":[44946186],"usage_in_usermode":30000000,"total_usage":44946186},"system_cpu_usage":8880799510000000,"throttling_data":{}}}
|
// ex (for future expansion): {"read":"2016-08-03T18:08:05Z","pids_stats":{},"network":{},"networks":{"eth0":{"rx_bytes":508,"tx_packets":6,"rx_packets":6,"tx_bytes":508}},"memory_stats":{"stats":{"cache":16384,"pgpgout":281,"rss":8826880,"pgpgin":2440,"total_rss":8826880,"hierarchical_memory_limit":536870912,"total_pgfault":3809,"active_anon":8843264,"total_active_anon":8843264,"total_pgpgout":281,"total_cache":16384,"pgfault":3809,"total_pgpgin":2440},"max_usage":8953856,"usage":8953856,"limit":536870912},"blkio_stats":{"io_service_bytes_recursive":[{"major":202,"op":"Read"},{"major":202,"op":"Write"},{"major":202,"op":"Sync"},{"major":202,"op":"Async"},{"major":202,"op":"Total"}],"io_serviced_recursive":[{"major":202,"op":"Read"},{"major":202,"op":"Write"},{"major":202,"op":"Sync"},{"major":202,"op":"Async"},{"major":202,"op":"Total"}]},"cpu_stats":{"cpu_usage":{"percpu_usage":[47641874],"usage_in_usermode":30000000,"total_usage":47641874},"system_cpu_usage":8880800500000000,"throttling_data":{}},"precpu_stats":{"cpu_usage":{"percpu_usage":[44946186],"usage_in_usermode":30000000,"total_usage":44946186},"system_cpu_usage":8880799510000000,"throttling_data":{}}}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/fnproject/fn/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A DriverCookie identifies a unique request to run a task.
|
// A DriverCookie identifies a unique request to run a task.
|
||||||
@@ -146,7 +146,7 @@ type ContainerTask interface {
|
|||||||
|
|
||||||
// Stat is a bucket of stats from a driver at a point in time for a certain task.
|
// Stat is a bucket of stats from a driver at a point in time for a certain task.
|
||||||
type Stat struct {
|
type Stat struct {
|
||||||
Timestamp strfmt.DateTime `json:"timestamp"`
|
Timestamp common.DateTime `json:"timestamp"`
|
||||||
Metrics map[string]uint64 `json:"metrics"`
|
Metrics map[string]uint64 `json:"metrics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ func average(samples []Stat) (Stat, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Timestamp = strfmt.DateTime(time.Unix(0, t))
|
s.Timestamp = common.DateTime(time.Unix(0, t))
|
||||||
for k, v := range s.Metrics {
|
for k, v := range s.Metrics {
|
||||||
s.Metrics[k] = v / uint64(l)
|
s.Metrics[k] = v / uint64(l)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/fnproject/fn/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAverage(t *testing.T) {
|
func TestAverage(t *testing.T) {
|
||||||
@@ -12,7 +12,7 @@ func TestAverage(t *testing.T) {
|
|||||||
stats := make([]Stat, 10)
|
stats := make([]Stat, 10)
|
||||||
for i := 0; i < len(stats); i++ {
|
for i := 0; i < len(stats); i++ {
|
||||||
stats[i] = Stat{
|
stats[i] = Stat{
|
||||||
Timestamp: strfmt.DateTime(start.Add(time.Duration(i) * time.Minute)),
|
Timestamp: common.DateTime(start.Add(time.Duration(i) * time.Minute)),
|
||||||
Metrics: map[string]uint64{"x": uint64(i)},
|
Metrics: map[string]uint64{"x": uint64(i)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ func TestDecimate(t *testing.T) {
|
|||||||
stats := make([]Stat, 480)
|
stats := make([]Stat, 480)
|
||||||
for i := range stats {
|
for i := range stats {
|
||||||
stats[i] = Stat{
|
stats[i] = Stat{
|
||||||
Timestamp: strfmt.DateTime(start.Add(time.Duration(i) * time.Second)),
|
Timestamp: common.DateTime(start.Add(time.Duration(i) * time.Second)),
|
||||||
Metrics: map[string]uint64{"x": uint64(i)},
|
Metrics: map[string]uint64{"x": uint64(i)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ func TestDecimate(t *testing.T) {
|
|||||||
stats = make([]Stat, 700)
|
stats = make([]Stat, 700)
|
||||||
for i := range stats {
|
for i := range stats {
|
||||||
stats[i] = Stat{
|
stats[i] = Stat{
|
||||||
Timestamp: strfmt.DateTime(start.Add(time.Duration(i) * time.Second)),
|
Timestamp: common.DateTime(start.Add(time.Duration(i) * time.Second)),
|
||||||
Metrics: map[string]uint64{"x": uint64(i)},
|
Metrics: map[string]uint64{"x": uint64(i)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ func TestDecimate(t *testing.T) {
|
|||||||
stats = make([]Stat, 300)
|
stats = make([]Stat, 300)
|
||||||
for i := range stats {
|
for i := range stats {
|
||||||
stats[i] = Stat{
|
stats[i] = Stat{
|
||||||
Timestamp: strfmt.DateTime(start.Add(time.Duration(i) * time.Second)),
|
Timestamp: common.DateTime(start.Add(time.Duration(i) * time.Second)),
|
||||||
Metrics: map[string]uint64{"x": uint64(i)},
|
Metrics: map[string]uint64{"x": uint64(i)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ func TestDecimate(t *testing.T) {
|
|||||||
start = start.Add(20 * time.Minute)
|
start = start.Add(20 * time.Minute)
|
||||||
}
|
}
|
||||||
stats[i] = Stat{
|
stats[i] = Stat{
|
||||||
Timestamp: strfmt.DateTime(start.Add(time.Duration(i) * time.Second)),
|
Timestamp: common.DateTime(start.Add(time.Duration(i) * time.Second)),
|
||||||
Metrics: map[string]uint64{"x": uint64(i)},
|
Metrics: map[string]uint64{"x": uint64(i)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/fnproject/fn/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// implements CallInfo, modify as needed
|
// implements CallInfo, modify as needed
|
||||||
@@ -23,8 +23,8 @@ func (t *testCall) IsCloudEvent() bool { return t.cloud }
|
|||||||
func (t *testCall) CallID() string { return "foo" }
|
func (t *testCall) CallID() string { return "foo" }
|
||||||
func (t *testCall) ContentType() string { return t.contentType }
|
func (t *testCall) ContentType() string { return t.contentType }
|
||||||
func (t *testCall) Input() io.Reader { return t.input }
|
func (t *testCall) Input() io.Reader { return t.input }
|
||||||
func (t *testCall) Deadline() strfmt.DateTime {
|
func (t *testCall) Deadline() common.DateTime {
|
||||||
return strfmt.DateTime(time.Now().Add(30 * time.Second))
|
return common.DateTime(time.Now().Add(30 * time.Second))
|
||||||
}
|
}
|
||||||
func (t *testCall) CallType() string { return "sync" }
|
func (t *testCall) CallType() string { return "sync" }
|
||||||
func (t *testCall) ProtocolType() string { return "http" }
|
func (t *testCall) ProtocolType() string { return "http" }
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidProtocol = errors.New("Invalid Protocol")
|
var errInvalidProtocol = errors.New("Invalid Protocol")
|
||||||
@@ -39,7 +39,7 @@ type CallInfo interface {
|
|||||||
CallID() string
|
CallID() string
|
||||||
ContentType() string
|
ContentType() string
|
||||||
Input() io.Reader
|
Input() io.Reader
|
||||||
Deadline() strfmt.DateTime
|
Deadline() common.DateTime
|
||||||
CallType() string
|
CallType() string
|
||||||
|
|
||||||
// ProtocolType let's function/fdk's know what type original request is. Only 'http' for now.
|
// ProtocolType let's function/fdk's know what type original request is. Only 'http' for now.
|
||||||
@@ -75,13 +75,13 @@ func (ci callInfoImpl) Input() io.Reader {
|
|||||||
return ci.req.Body
|
return ci.req.Body
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ci callInfoImpl) Deadline() strfmt.DateTime {
|
func (ci callInfoImpl) Deadline() common.DateTime {
|
||||||
deadline, ok := ci.req.Context().Deadline()
|
deadline, ok := ci.req.Context().Deadline()
|
||||||
if !ok {
|
if !ok {
|
||||||
// In theory deadline must have been set here
|
// In theory deadline must have been set here
|
||||||
panic("No context deadline is set in protocol, should never happen")
|
panic("No context deadline is set in protocol, should never happen")
|
||||||
}
|
}
|
||||||
return strfmt.DateTime(deadline)
|
return common.DateTime(deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallType returns whether the function call was "sync" or "async".
|
// CallType returns whether the function call was "sync" or "async".
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/fnproject/fn/fnext"
|
"github.com/fnproject/fn/fnext"
|
||||||
"github.com/fnproject/fn/grpcutil"
|
"github.com/fnproject/fn/grpcutil"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -69,7 +68,7 @@ type callHandle struct {
|
|||||||
c *call // the agent's version of call
|
c *call // the agent's version of call
|
||||||
|
|
||||||
// Timings, for metrics:
|
// Timings, for metrics:
|
||||||
receivedTime strfmt.DateTime // When was the call received?
|
receivedTime common.DateTime // When was the call received?
|
||||||
|
|
||||||
// For implementing http.ResponseWriter:
|
// For implementing http.ResponseWriter:
|
||||||
headers http.Header
|
headers http.Header
|
||||||
@@ -525,7 +524,7 @@ func (pr *pureRunner) spawnSubmit(state *callHandle) {
|
|||||||
|
|
||||||
// handleTryCall based on the TryCall message, tries to place the call on NBIO Agent
|
// handleTryCall based on the TryCall message, tries to place the call on NBIO Agent
|
||||||
func (pr *pureRunner) handleTryCall(tc *runner.TryCall, state *callHandle) error {
|
func (pr *pureRunner) handleTryCall(tc *runner.TryCall, state *callHandle) error {
|
||||||
state.receivedTime = strfmt.DateTime(time.Now())
|
state.receivedTime = common.DateTime(time.Now())
|
||||||
var c models.Call
|
var c models.Call
|
||||||
err := json.Unmarshal([]byte(tc.ModelsCallJson), &c)
|
err := json.Unmarshal([]byte(tc.ModelsCallJson), &c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -12,25 +12,23 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package strfmt
|
// ^ we took this from then, and got rid of mgo since it's not a dep of ours.
|
||||||
|
// we can also change resolution to not be milliseconds. there's your attribution.
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
|
|
||||||
"github.com/mailru/easyjson/jlexer"
|
|
||||||
"github.com/mailru/easyjson/jwriter"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
// IsDate returns true when the string is a valid date
|
||||||
dt := DateTime{}
|
func IsDate(str string) bool {
|
||||||
Default.Add("datetime", &dt, IsDateTime)
|
_, err := time.Parse(RFC3339FullDate, str)
|
||||||
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDateTime returns true when the string is a valid date-time
|
// IsDateTime returns true when the string is a valid date-time
|
||||||
@@ -59,6 +57,10 @@ const (
|
|||||||
RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
|
RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
|
||||||
// DateTimePattern pattern to match for the date-time format from http://tools.ietf.org/html/rfc3339#section-5.6
|
// DateTimePattern pattern to match for the date-time format from http://tools.ietf.org/html/rfc3339#section-5.6
|
||||||
DateTimePattern = `^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$`
|
DateTimePattern = `^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$`
|
||||||
|
|
||||||
|
// RFC3339FullDate represents a full-date as specified by RFC3339
|
||||||
|
// See: http://goo.gl/xXOvVd
|
||||||
|
RFC3339FullDate = "2006-01-02"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -90,8 +92,6 @@ func ParseDateTime(data string) (DateTime, error) {
|
|||||||
// It knows how to read 3 different variations of a RFC3339 date time.
|
// It knows how to read 3 different variations of a RFC3339 date time.
|
||||||
// Most APIs we encounter want either millisecond or second precision times.
|
// Most APIs we encounter want either millisecond or second precision times.
|
||||||
// This just tries to make it worry-free.
|
// This just tries to make it worry-free.
|
||||||
//
|
|
||||||
// swagger:strfmt date-time
|
|
||||||
type DateTime time.Time
|
type DateTime time.Time
|
||||||
|
|
||||||
// NewDateTime is a representation of zero value for DateTime type
|
// NewDateTime is a representation of zero value for DateTime type
|
||||||
@@ -132,7 +132,7 @@ func (t *DateTime) Scan(raw interface{}) error {
|
|||||||
case nil:
|
case nil:
|
||||||
*t = DateTime{}
|
*t = DateTime{}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("cannot sql.Scan() strfmt.DateTime from: %#v", v)
|
return fmt.Errorf("cannot sql.Scan() common.DateTime from: %#v", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -142,55 +142,3 @@ func (t *DateTime) Scan(raw interface{}) error {
|
|||||||
func (t DateTime) Value() (driver.Value, error) {
|
func (t DateTime) Value() (driver.Value, error) {
|
||||||
return driver.Value(t.String()), nil
|
return driver.Value(t.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON returns the DateTime as JSON
|
|
||||||
func (t DateTime) MarshalJSON() ([]byte, error) {
|
|
||||||
var w jwriter.Writer
|
|
||||||
t.MarshalEasyJSON(&w)
|
|
||||||
return w.BuildBytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalEasyJSON writes the DateTime to a easyjson.Writer
|
|
||||||
func (t DateTime) MarshalEasyJSON(w *jwriter.Writer) {
|
|
||||||
w.String(time.Time(t).Format(MarshalFormat))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON sets the DateTime from JSON
|
|
||||||
func (t *DateTime) UnmarshalJSON(data []byte) error {
|
|
||||||
l := jlexer.Lexer{Data: data}
|
|
||||||
t.UnmarshalEasyJSON(&l)
|
|
||||||
return l.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalEasyJSON sets the DateTime from a easyjson.Lexer
|
|
||||||
func (t *DateTime) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|
||||||
if data := in.String(); in.Ok() {
|
|
||||||
tt, err := ParseDateTime(data)
|
|
||||||
if err != nil {
|
|
||||||
in.AddError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*t = tt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBSON returns the DateTime as a bson.M{} map.
|
|
||||||
func (t *DateTime) GetBSON() (interface{}, error) {
|
|
||||||
return bson.M{"data": t.String()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBSON sets the DateTime from raw bson data
|
|
||||||
func (t *DateTime) SetBSON(raw bson.Raw) error {
|
|
||||||
var m bson.M
|
|
||||||
if err := raw.Unmarshal(&m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data, ok := m["data"].(string); ok {
|
|
||||||
var err error
|
|
||||||
*t, err = ParseDateTime(data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("couldn't unmarshal bson raw value as Duration")
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/id"
|
"github.com/fnproject/fn/api/id"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testApp = &models.App{
|
var testApp = &models.App{
|
||||||
@@ -30,10 +30,10 @@ func SetupTestCall(t *testing.T, ctx context.Context, ls models.LogStore) *model
|
|||||||
|
|
||||||
var call models.Call
|
var call models.Call
|
||||||
call.AppID = testApp.ID
|
call.AppID = testApp.ID
|
||||||
call.CreatedAt = strfmt.DateTime(time.Now())
|
call.CreatedAt = common.DateTime(time.Now())
|
||||||
call.Status = "success"
|
call.Status = "success"
|
||||||
call.StartedAt = strfmt.DateTime(time.Now())
|
call.StartedAt = common.DateTime(time.Now())
|
||||||
call.CompletedAt = strfmt.DateTime(time.Now())
|
call.CompletedAt = common.DateTime(time.Now())
|
||||||
call.Path = testRoute.Path
|
call.Path = testRoute.Path
|
||||||
return &call
|
return &call
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ func Test(t *testing.T, fnl models.LogStore) {
|
|||||||
t.Run("calls-get", func(t *testing.T) {
|
t.Run("calls-get", func(t *testing.T) {
|
||||||
filter := &models.CallFilter{AppID: call.AppID, Path: call.Path, PerPage: 100}
|
filter := &models.CallFilter{AppID: call.AppID, Path: call.Path, PerPage: 100}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
call.CreatedAt = strfmt.DateTime(now)
|
call.CreatedAt = common.DateTime(now)
|
||||||
call.ID = id.New().String()
|
call.ID = id.New().String()
|
||||||
err := fnl.InsertCall(ctx, call)
|
err := fnl.InsertCall(ctx, call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -65,11 +65,11 @@ func Test(t *testing.T, fnl models.LogStore) {
|
|||||||
c2 := *call
|
c2 := *call
|
||||||
c3 := *call
|
c3 := *call
|
||||||
now = time.Now().Add(100 * time.Millisecond)
|
now = time.Now().Add(100 * time.Millisecond)
|
||||||
c2.CreatedAt = strfmt.DateTime(now) // add ms cuz db uses it for sort
|
c2.CreatedAt = common.DateTime(now) // add ms cuz db uses it for sort
|
||||||
c2.ID = id.New().String()
|
c2.ID = id.New().String()
|
||||||
|
|
||||||
now = time.Now().Add(200 * time.Millisecond)
|
now = time.Now().Add(200 * time.Millisecond)
|
||||||
c3.CreatedAt = strfmt.DateTime(now)
|
c3.CreatedAt = common.DateTime(now)
|
||||||
c3.ID = id.New().String()
|
c3.ID = id.New().String()
|
||||||
|
|
||||||
err = fnl.InsertCall(ctx, &c2)
|
err = fnl.InsertCall(ctx, &c2)
|
||||||
@@ -177,11 +177,11 @@ func Test(t *testing.T, fnl models.LogStore) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
call = new(models.Call)
|
call = new(models.Call)
|
||||||
call.CreatedAt = strfmt.DateTime(time.Now())
|
call.CreatedAt = common.DateTime(time.Now())
|
||||||
call.Status = "error"
|
call.Status = "error"
|
||||||
call.Error = "ya dun goofed"
|
call.Error = "ya dun goofed"
|
||||||
call.StartedAt = strfmt.DateTime(time.Now())
|
call.StartedAt = common.DateTime(time.Now())
|
||||||
call.CompletedAt = strfmt.DateTime(time.Now())
|
call.CompletedAt = common.DateTime(time.Now())
|
||||||
call.AppID = testApp.Name
|
call.AppID = testApp.Name
|
||||||
call.Path = testRoute.Path
|
call.Path = testRoute.Path
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/id"
|
"github.com/fnproject/fn/api/id"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
@@ -18,16 +18,16 @@ type App struct {
|
|||||||
Config Config `json:"config,omitempty" db:"config"`
|
Config Config `json:"config,omitempty" db:"config"`
|
||||||
Annotations Annotations `json:"annotations,omitempty" db:"annotations"`
|
Annotations Annotations `json:"annotations,omitempty" db:"annotations"`
|
||||||
SyslogURL *string `json:"syslog_url,omitempty" db:"syslog_url"`
|
SyslogURL *string `json:"syslog_url,omitempty" db:"syslog_url"`
|
||||||
CreatedAt strfmt.DateTime `json:"created_at,omitempty" db:"created_at"`
|
CreatedAt common.DateTime `json:"created_at,omitempty" db:"created_at"`
|
||||||
UpdatedAt strfmt.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
UpdatedAt common.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SetDefaults() {
|
func (a *App) SetDefaults() {
|
||||||
if time.Time(a.CreatedAt).IsZero() {
|
if time.Time(a.CreatedAt).IsZero() {
|
||||||
a.CreatedAt = strfmt.DateTime(time.Now())
|
a.CreatedAt = common.DateTime(time.Now())
|
||||||
}
|
}
|
||||||
if time.Time(a.UpdatedAt).IsZero() {
|
if time.Time(a.UpdatedAt).IsZero() {
|
||||||
a.UpdatedAt = strfmt.DateTime(time.Now())
|
a.UpdatedAt = common.DateTime(time.Now())
|
||||||
}
|
}
|
||||||
if a.Config == nil {
|
if a.Config == nil {
|
||||||
// keeps the json from being nil
|
// keeps the json from being nil
|
||||||
@@ -134,7 +134,7 @@ func (a *App) Update(patch *App) {
|
|||||||
a.Annotations = a.Annotations.MergeChange(patch.Annotations)
|
a.Annotations = a.Annotations.MergeChange(patch.Annotations)
|
||||||
|
|
||||||
if !a.Equals(original) {
|
if !a.Equals(original) {
|
||||||
a.UpdatedAt = strfmt.DateTime(time.Now())
|
a.UpdatedAt = common.DateTime(time.Now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/fnproject/fn/api/agent/drivers"
|
"github.com/fnproject/fn/api/agent/drivers"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/fnproject/fn/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -132,13 +132,13 @@ type Call struct {
|
|||||||
SyslogURL string `json:"syslog_url,omitempty" db:"-"`
|
SyslogURL string `json:"syslog_url,omitempty" db:"-"`
|
||||||
|
|
||||||
// Time when call completed, whether it was successul or failed. Always in UTC.
|
// Time when call completed, whether it was successul or failed. Always in UTC.
|
||||||
CompletedAt strfmt.DateTime `json:"completed_at,omitempty" db:"completed_at"`
|
CompletedAt common.DateTime `json:"completed_at,omitempty" db:"completed_at"`
|
||||||
|
|
||||||
// Time when call was submitted. Always in UTC.
|
// Time when call was submitted. Always in UTC.
|
||||||
CreatedAt strfmt.DateTime `json:"created_at,omitempty" db:"created_at"`
|
CreatedAt common.DateTime `json:"created_at,omitempty" db:"created_at"`
|
||||||
|
|
||||||
// Time when call started execution. Always in UTC.
|
// Time when call started execution. Always in UTC.
|
||||||
StartedAt strfmt.DateTime `json:"started_at,omitempty" db:"started_at"`
|
StartedAt common.DateTime `json:"started_at,omitempty" db:"started_at"`
|
||||||
|
|
||||||
// Stats is a list of metrics from this call's execution, possibly empty.
|
// Stats is a list of metrics from this call's execution, possibly empty.
|
||||||
Stats drivers.Stats `json:"stats,omitempty" db:"stats"`
|
Stats drivers.Stats `json:"stats,omitempty" db:"stats"`
|
||||||
@@ -154,8 +154,8 @@ type Call struct {
|
|||||||
type CallFilter struct {
|
type CallFilter struct {
|
||||||
Path string // match
|
Path string // match
|
||||||
AppID string // match
|
AppID string // match
|
||||||
FromTime strfmt.DateTime
|
FromTime common.DateTime
|
||||||
ToTime strfmt.DateTime
|
ToTime common.DateTime
|
||||||
Cursor string
|
Cursor string
|
||||||
PerPage int
|
PerPage int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/fnproject/fn/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -38,8 +38,8 @@ type Route struct {
|
|||||||
TmpFsSize uint32 `json:"tmpfs_size" db:"tmpfs_size"`
|
TmpFsSize uint32 `json:"tmpfs_size" db:"tmpfs_size"`
|
||||||
Config Config `json:"config,omitempty" db:"config"`
|
Config Config `json:"config,omitempty" db:"config"`
|
||||||
Annotations Annotations `json:"annotations,omitempty" db:"annotations"`
|
Annotations Annotations `json:"annotations,omitempty" db:"annotations"`
|
||||||
CreatedAt strfmt.DateTime `json:"created_at,omitempty" db:"created_at"`
|
CreatedAt common.DateTime `json:"created_at,omitempty" db:"created_at"`
|
||||||
UpdatedAt strfmt.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
UpdatedAt common.DateTime `json:"updated_at,omitempty" db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets zeroed field to defaults.
|
// SetDefaults sets zeroed field to defaults.
|
||||||
@@ -74,11 +74,11 @@ func (r *Route) SetDefaults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if time.Time(r.CreatedAt).IsZero() {
|
if time.Time(r.CreatedAt).IsZero() {
|
||||||
r.CreatedAt = strfmt.DateTime(time.Now())
|
r.CreatedAt = common.DateTime(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Time(r.UpdatedAt).IsZero() {
|
if time.Time(r.UpdatedAt).IsZero() {
|
||||||
r.UpdatedAt = strfmt.DateTime(time.Now())
|
r.UpdatedAt = common.DateTime(time.Now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ func (r *Route) Update(patch *Route) {
|
|||||||
r.Annotations = r.Annotations.MergeChange(patch.Annotations)
|
r.Annotations = r.Annotations.MergeChange(patch.Annotations)
|
||||||
|
|
||||||
if !r.Equals(original) {
|
if !r.Equals(original) {
|
||||||
r.UpdatedAt = strfmt.DateTime(time.Now())
|
r.UpdatedAt = common.DateTime(time.Now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fnproject/fn/api"
|
"github.com/fnproject/fn/api"
|
||||||
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) handleCallList(c *gin.Context) {
|
func (s *Server) handleCallList(c *gin.Context) {
|
||||||
@@ -42,7 +42,7 @@ func (s *Server) handleCallList(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// "" gets parsed to a zero time, which is fine (ignored in query)
|
// "" gets parsed to a zero time, which is fine (ignored in query)
|
||||||
func timeParams(c *gin.Context) (fromTime, toTime strfmt.DateTime, err error) {
|
func timeParams(c *gin.Context) (fromTime, toTime common.DateTime, err error) {
|
||||||
fromStr := c.Query("from_time")
|
fromStr := c.Query("from_time")
|
||||||
toStr := c.Query("to_time")
|
toStr := c.Query("to_time")
|
||||||
var ok bool
|
var ok bool
|
||||||
@@ -61,10 +61,10 @@ func timeParams(c *gin.Context) (fromTime, toTime strfmt.DateTime, err error) {
|
|||||||
return fromTime, toTime, nil
|
return fromTime, toTime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func strToTime(str string) (strfmt.DateTime, bool) {
|
func strToTime(str string) (common.DateTime, bool) {
|
||||||
sec, err := strconv.ParseInt(str, 10, 64)
|
sec, err := strconv.ParseInt(str, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return strfmt.DateTime(time.Time{}), false
|
return common.DateTime(time.Time{}), false
|
||||||
}
|
}
|
||||||
return strfmt.DateTime(time.Unix(sec, 0)), true
|
return common.DateTime(time.Unix(sec, 0)), true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fnproject/fn/api/common"
|
||||||
"github.com/fnproject/fn/api/datastore"
|
"github.com/fnproject/fn/api/datastore"
|
||||||
"github.com/fnproject/fn/api/id"
|
"github.com/fnproject/fn/api/id"
|
||||||
"github.com/fnproject/fn/api/logs"
|
"github.com/fnproject/fn/api/logs"
|
||||||
"github.com/fnproject/fn/api/models"
|
"github.com/fnproject/fn/api/models"
|
||||||
"github.com/fnproject/fn/api/mqs"
|
"github.com/fnproject/fn/api/mqs"
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCallGet(t *testing.T) {
|
func TestCallGet(t *testing.T) {
|
||||||
@@ -39,7 +39,7 @@ func TestCallGet(t *testing.T) {
|
|||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
IdleTimeout: 30,
|
IdleTimeout: 30,
|
||||||
Memory: 256,
|
Memory: 256,
|
||||||
CreatedAt: strfmt.DateTime(time.Now()),
|
CreatedAt: common.DateTime(time.Now()),
|
||||||
URL: "http://localhost:8080/r/myapp/thisisatest",
|
URL: "http://localhost:8080/r/myapp/thisisatest",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
}
|
}
|
||||||
@@ -110,16 +110,16 @@ func TestCallList(t *testing.T) {
|
|||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
IdleTimeout: 30,
|
IdleTimeout: 30,
|
||||||
Memory: 256,
|
Memory: 256,
|
||||||
CreatedAt: strfmt.DateTime(time.Now()),
|
CreatedAt: common.DateTime(time.Now()),
|
||||||
URL: "http://localhost:8080/r/myapp/thisisatest",
|
URL: "http://localhost:8080/r/myapp/thisisatest",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
}
|
}
|
||||||
c2 := *call
|
c2 := *call
|
||||||
c3 := *call
|
c3 := *call
|
||||||
c2.CreatedAt = strfmt.DateTime(time.Now().Add(100 * time.Second))
|
c2.CreatedAt = common.DateTime(time.Now().Add(100 * time.Second))
|
||||||
c2.ID = id.New().String()
|
c2.ID = id.New().String()
|
||||||
c2.Path = "test2"
|
c2.Path = "test2"
|
||||||
c3.CreatedAt = strfmt.DateTime(time.Now().Add(200 * time.Second))
|
c3.CreatedAt = common.DateTime(time.Now().Add(200 * time.Second))
|
||||||
c3.ID = id.New().String()
|
c3.ID = id.New().String()
|
||||||
c3.Path = "/test3"
|
c3.Path = "/test3"
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func (s *Server) handleRunnerStart(c *gin.Context) {
|
|||||||
// cover our tracks since if the db is down we can't run anything anyway (treat as such).
|
// cover our tracks since if the db is down we can't run anything anyway (treat as such).
|
||||||
// TODO do this client side and validate it here?
|
// TODO do this client side and validate it here?
|
||||||
//call.Status = "running"
|
//call.Status = "running"
|
||||||
//call.StartedAt = strfmt.DateTime(time.Now())
|
//call.StartedAt = common.DateTime(time.Now())
|
||||||
//err := s.datastore.UpdateCall(c.Request.Context(), &call)
|
//err := s.datastore.UpdateCall(c.Request.Context(), &call)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
//if err == InvalidStatusChange {
|
//if err == InvalidStatusChange {
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
FN integration API tests
|
|
||||||
======================================
|
|
||||||
|
|
||||||
|
|
||||||
These are tests that can either run locally against the current codebase (e.g. in an IDE) or remotely against a running Fn instance.
|
|
||||||
|
|
||||||
|
|
||||||
Test dependencies
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
```bash
|
|
||||||
DOCKER_HOST - for building images
|
|
||||||
FN_API_URL - Fn API endpoint - leave this unset to test using the local codebase
|
|
||||||
```
|
|
||||||
|
|
||||||
How to run tests?
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export FN_API_URL=http://localhost:8080
|
|
||||||
go test -v ./...
|
|
||||||
```
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// common test cases around annotations (shared by any objects that support it)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxAnnotationKeys = 100
|
|
||||||
maxAnnotationValueSize = 512
|
|
||||||
maxAnnotationKeySize = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
var emptyAnnMap = map[string]interface{}{}
|
|
||||||
|
|
||||||
func makeAnnMap(size int) map[string]interface{} {
|
|
||||||
md := make(map[string]interface{}, size)
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
md[fmt.Sprintf("k-%d", i)] = "val"
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
var createAnnotationsValidCases = []struct {
|
|
||||||
name string
|
|
||||||
annotations map[string]interface{}
|
|
||||||
}{
|
|
||||||
{"valid_string", map[string]interface{}{"key": "value"}},
|
|
||||||
{"valid_array", map[string]interface{}{"key": []interface{}{"value1", "value2"}}},
|
|
||||||
{"valid_object", map[string]interface{}{"key": map[string]interface{}{"foo": "bar"}}},
|
|
||||||
{"max_value_size", map[string]interface{}{"key": strings.Repeat("a", maxAnnotationValueSize-2)}},
|
|
||||||
{"max_key_size", map[string]interface{}{strings.Repeat("a", maxAnnotationKeySize): "val"}},
|
|
||||||
{"max_map_size", makeAnnMap(maxAnnotationKeys)},
|
|
||||||
}
|
|
||||||
|
|
||||||
var createAnnotationsErrorCases = []struct {
|
|
||||||
name string
|
|
||||||
annotations map[string]interface{}
|
|
||||||
}{
|
|
||||||
{"value_too_long", map[string]interface{}{"key": strings.Repeat("a", maxAnnotationValueSize-1)}},
|
|
||||||
{"key_too_long", map[string]interface{}{strings.Repeat("a", maxAnnotationKeySize+1): "value"}},
|
|
||||||
{"whitespace_in_key", map[string]interface{}{" bad key ": "value"}},
|
|
||||||
{"too_many_keys", makeAnnMap(maxAnnotationKeys + 1)},
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateAnnotationsValidCases = []struct {
|
|
||||||
name string
|
|
||||||
initial map[string]interface{}
|
|
||||||
change map[string]interface{}
|
|
||||||
expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{"overwrite_existing_annotation_keys", map[string]interface{}{"key": "value1"}, map[string]interface{}{"key": "value2"}, map[string]interface{}{"key": "value2"}},
|
|
||||||
{"delete_annotation_key", map[string]interface{}{"key": "value1"}, map[string]interface{}{"key": ""}, map[string]interface{}{}},
|
|
||||||
{"set_to_max_size_with_deletes", map[string]interface{}{"key": "value1"}, func() map[string]interface{} {
|
|
||||||
md := makeAnnMap(100)
|
|
||||||
md["key"] = ""
|
|
||||||
return md
|
|
||||||
}(), makeAnnMap(100)},
|
|
||||||
{"noop_with_max_keys", makeAnnMap(maxAnnotationKeys), emptyAnnMap, makeAnnMap(maxAnnotationKeys)},
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateAnnotationsErrorCases = []struct {
|
|
||||||
name string
|
|
||||||
initial map[string]interface{}
|
|
||||||
change map[string]interface{}
|
|
||||||
}{
|
|
||||||
{"too_many_key_after_update", makeAnnMap(100), map[string]interface{}{"key": "value1"}},
|
|
||||||
{"value_too_long", map[string]interface{}{}, map[string]interface{}{"key": strings.Repeat("a", maxAnnotationValueSize-1)}},
|
|
||||||
{"key_too_long", map[string]interface{}{}, map[string]interface{}{strings.Repeat("a", maxAnnotationKeySize+1): "value"}},
|
|
||||||
{"whitespace_in_key", map[string]interface{}{}, map[string]interface{}{" bad key ": "value"}},
|
|
||||||
{"too_many_keys_in_update", map[string]interface{}{}, makeAnnMap(maxAnnotationKeys + 1)},
|
|
||||||
}
|
|
||||||
|
|
||||||
//AnnotationsEquivalent checks if two annotations maps are semantically equivalent, including nil == empty map
|
|
||||||
func AnnotationsEquivalent(md1, md2 map[string]interface{}) bool {
|
|
||||||
|
|
||||||
if len(md1) == 0 && len(md2) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return reflect.DeepEqual(md1, md2)
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn_go/client"
|
|
||||||
"github.com/fnproject/fn_go/client/apps"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PostApp creates an app and esures it is deleted on teardown if it was created
|
|
||||||
func (s *TestHarness) PostApp(app *models.App) (*apps.PostAppsOK, error) {
|
|
||||||
cfg := &apps.PostAppsParams{
|
|
||||||
Body: &models.AppWrapper{
|
|
||||||
App: app,
|
|
||||||
},
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
ok, err := s.Client.Apps.PostApps(cfg)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
s.createdApps[ok.Payload.App.Name] = true
|
|
||||||
}
|
|
||||||
return ok, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GivenAppExists creates an app and ensures it is deleted on teardown, this fatals if the app is not created
|
|
||||||
func (s *TestHarness) GivenAppExists(t *testing.T, app *models.App) {
|
|
||||||
|
|
||||||
appPayload, err := s.PostApp(app)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create app %v", app)
|
|
||||||
|
|
||||||
}
|
|
||||||
if !strings.Contains(app.Name, appPayload.Payload.App.Name) {
|
|
||||||
t.Fatalf("App name mismatch.\nExpected: %v\nActual: %v",
|
|
||||||
app.Name, appPayload.Payload.App.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppMustExist fails the test if the specified app does not exist
|
|
||||||
func (s *TestHarness) AppMustExist(t *testing.T, appName string) *models.App {
|
|
||||||
app, err := s.Client.Apps.GetAppsApp(&apps.GetAppsAppParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected new route to create app got %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return app.Payload.App
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeDeleteApp(ctx context.Context, fnclient *client.Fn, appName string) {
|
|
||||||
cfg := &apps.DeleteAppsAppParams{
|
|
||||||
App: appName,
|
|
||||||
Context: ctx,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
_, err := fnclient.Apps.DeleteAppsApp(cfg)
|
|
||||||
if _, ok := err.(*apps.DeleteAppsAppNotFound); err != nil && !ok {
|
|
||||||
log.Printf("Error cleaning up app %s: %v", appName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/fnproject/fn_go/client/apps"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAppDeleteNotFound(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
cfg := &apps.DeleteAppsAppParams{
|
|
||||||
App: "missing-app",
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := s.Client.Apps.DeleteAppsApp(cfg)
|
|
||||||
|
|
||||||
if _, ok := err.(*apps.DeleteAppsAppNotFound); !ok {
|
|
||||||
t.Errorf("Error during app delete: we should get HTTP 404, but got: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppGetNotFound(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
cfg := &apps.GetAppsAppParams{
|
|
||||||
App: "missing-app",
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
_, err := s.Client.Apps.GetAppsApp(cfg)
|
|
||||||
|
|
||||||
if _, ok := err.(*apps.GetAppsAppNotFound); !ok {
|
|
||||||
t.Errorf("Error during get: we should get HTTP 404, but got: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(err.(*apps.GetAppsAppNotFound).Payload.Error.Message, "App not found") {
|
|
||||||
t.Errorf("Error during app delete: unexpeted error `%s`, wanted `App not found`", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppCreateNoConfigSuccess(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
resp, err := s.PostApp(&models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to create simple app %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Payload.App.Name != s.AppName {
|
|
||||||
t.Errorf("app name in response %s does not match new app %s ", resp.Payload.App.Name, s.AppName)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetAppAnnotationsOnCreate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
for _, tci := range createAnnotationsValidCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("valid_"+tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
app, err := s.PostApp(&models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Annotations: tc.annotations,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create app with valid annotations %v got error %v", tc.annotations, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gotMd := app.Payload.App.Annotations
|
|
||||||
if !AnnotationsEquivalent(gotMd, tc.annotations) {
|
|
||||||
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
getApp := s.AppMustExist(t, s.AppName)
|
|
||||||
|
|
||||||
if !AnnotationsEquivalent(getApp.Annotations, tc.annotations) {
|
|
||||||
t.Errorf("GET annotations '%v' does not match set annotations %v", getApp.Annotations, tc.annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tci := range createAnnotationsErrorCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("invalid_"+tc.name, func(ti *testing.T) {
|
|
||||||
ti.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
_, err := s.PostApp(&models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Annotations: tc.annotations,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Created app with invalid annotations %v but expected error", tc.annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := err.(*apps.PostAppsBadRequest); !ok {
|
|
||||||
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateAppAnnotationsOnPatch(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tci := range updateAnnotationsValidCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("valid_"+tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Annotations: tc.initial,
|
|
||||||
})
|
|
||||||
|
|
||||||
res, err := s.Client.Apps.PatchAppsApp(&apps.PatchAppsAppParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
Body: &models.AppWrapper{
|
|
||||||
App: &models.App{
|
|
||||||
Annotations: tc.change,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to patch annotations with %v on app: %v", tc.change, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gotMd := res.Payload.App.Annotations
|
|
||||||
if !AnnotationsEquivalent(gotMd, tc.expected) {
|
|
||||||
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
getApp := s.AppMustExist(t, s.AppName)
|
|
||||||
|
|
||||||
if !AnnotationsEquivalent(getApp.Annotations, tc.expected) {
|
|
||||||
t.Errorf("GET annotations '%v' does not match set annotations %v", getApp.Annotations, tc.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tci := range updateAnnotationsErrorCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("invalid_"+tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Annotations: tc.initial,
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := s.Client.Apps.PatchAppsApp(&apps.PatchAppsAppParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
Body: &models.AppWrapper{
|
|
||||||
App: &models.App{
|
|
||||||
Annotations: tc.change,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("patched app with invalid annotations %v but expected error", tc.change)
|
|
||||||
}
|
|
||||||
if _, ok := err.(*apps.PatchAppsAppBadRequest); !ok {
|
|
||||||
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppCreateWithConfigSuccess(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
validConfig := map[string]string{"A": "a"}
|
|
||||||
appPayload, err := s.PostApp(&models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Config: validConfig,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create app with valid config got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(validConfig, appPayload.Payload.App.Config) {
|
|
||||||
t.Errorf("Expecting config %v but got %v in response", validConfig, appPayload.Payload.App.Config)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppInsect(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
validConfig := map[string]string{"A": "a"}
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName,
|
|
||||||
Config: validConfig})
|
|
||||||
|
|
||||||
appOk, err := s.Client.Apps.GetAppsApp(&apps.GetAppsAppParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected valid response to get app, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(validConfig, appOk.Payload.App.Config) {
|
|
||||||
t.Errorf("Returned config %v does not match requested config %v", appOk.Payload.App.Config, validConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppPatchConfig(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tci := range updateConfigCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Config: tc.intialConfig,
|
|
||||||
})
|
|
||||||
|
|
||||||
patch, err := s.Client.Apps.PatchAppsApp(&apps.PatchAppsAppParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Body: &models.AppWrapper{
|
|
||||||
App: &models.App{
|
|
||||||
Config: tc.change,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Context: s.Context,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to patch app with valid value %v, %v", tc.change, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ConfigEquivalent(patch.Payload.App.Config, tc.expected) {
|
|
||||||
t.Errorf("Expected returned app config to be %v, but was %v", tc.expected, patch.Payload.App.Config)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppDuplicate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
|
|
||||||
_, err := s.PostApp(&models.App{Name: s.AppName})
|
|
||||||
|
|
||||||
if _, ok := err.(*apps.PostAppsConflict); !ok {
|
|
||||||
t.Errorf("Expecting conflict response on duplicate app, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn_go/client/call"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCallsMissingApp(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
cfg := &call.GetAppsAppCallsParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Path: &s.RoutePath,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
_, err := s.Client.Call.GetAppsAppCalls(cfg)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Must fail with missing app error, but got %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCallsDummy(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
|
||||||
Call: "dummy",
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
_, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Must fail because `dummy` call does not exist.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetExactCall(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
callID := CallAsync(t, s.Context, u, &bytes.Buffer{})
|
|
||||||
|
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
|
|
||||||
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
|
||||||
_, err = s.Client.Call.GetAppsAppCallsCall(cfg)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if retryErr != nil {
|
|
||||||
t.Error(retryErr.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import "reflect"
|
|
||||||
|
|
||||||
// common test cases around config for apps/routes
|
|
||||||
|
|
||||||
var updateConfigCases = []struct {
|
|
||||||
name string
|
|
||||||
intialConfig map[string]string
|
|
||||||
change map[string]string
|
|
||||||
expected map[string]string
|
|
||||||
}{
|
|
||||||
{"preserve existing config keys with nop", map[string]string{"key": "value1"}, map[string]string{}, map[string]string{"key": "value1"}},
|
|
||||||
|
|
||||||
{"preserve existing config keys with change", map[string]string{"key": "value1"}, map[string]string{"key": "value1"}, map[string]string{"key": "value1"}},
|
|
||||||
|
|
||||||
{"overwrite existing config keys", map[string]string{"key": "value1"}, map[string]string{"key": "value2"}, map[string]string{"key": "value2"}},
|
|
||||||
|
|
||||||
{"delete config key", map[string]string{"key": "value1"}, map[string]string{"key": ""}, map[string]string{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
//ConfigEquivalent checks if two config objects are semantically equivalent (including nils)
|
|
||||||
func ConfigEquivalent(a map[string]string, b map[string]string) bool {
|
|
||||||
if len(a) == 0 && len(b) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return reflect.DeepEqual(a, b)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,383 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn_go/client/call"
|
|
||||||
"github.com/fnproject/fn_go/client/operations"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CallAsync(t *testing.T, ctx context.Context, u url.URL, content io.Reader) string {
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
_, err := CallFN(ctx, u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedOutput := "call_id"
|
|
||||||
if !strings.Contains(output.String(), expectedOutput) {
|
|
||||||
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
type CallID struct {
|
|
||||||
CallID string `json:"call_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
callID := &CallID{}
|
|
||||||
json.NewDecoder(output).Decode(callID)
|
|
||||||
|
|
||||||
if callID.CallID == "" {
|
|
||||||
t.Errorf("`call_id` not suppose to be empty string")
|
|
||||||
}
|
|
||||||
t.Logf("Async execution call ID: %v", callID.CallID)
|
|
||||||
return callID.CallID
|
|
||||||
}
|
|
||||||
|
|
||||||
func CallSync(t *testing.T, ctx context.Context, u url.URL, content io.Reader) string {
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
resp, err := CallFN(ctx, u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callId := resp.Header.Get("FN_CALL_ID")
|
|
||||||
if callId == "" {
|
|
||||||
t.Errorf("Assertion error.\n\tExpected call id header in response, got: %v", resp.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Sync execution call ID: %v", callId)
|
|
||||||
return callId
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanCallfunction(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Type = "sync"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
_, err := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
expectedOutput := "Hello World!\n"
|
|
||||||
if !strings.Contains(expectedOutput, output.String()) {
|
|
||||||
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCallOutputMatch(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Type = "sync"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Name string
|
|
||||||
}{Name: "John"})
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
_, err := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
expectedOutput := "Hello John!\n"
|
|
||||||
if !strings.Contains(expectedOutput, output.String()) {
|
|
||||||
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanCallAsync(t *testing.T) {
|
|
||||||
newRouteType := "async"
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
s := SetupHarness()
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Type = "sync"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
s.GivenRoutePatched(t, s.AppName, s.RoutePath, &models.Route{
|
|
||||||
Type: newRouteType,
|
|
||||||
})
|
|
||||||
|
|
||||||
CallAsync(t, s.Context, u, &bytes.Buffer{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanGetAsyncState(t *testing.T) {
|
|
||||||
newRouteType := "async"
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Type = "sync"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
s.GivenRoutePatched(t, s.AppName, rt.Path, &models.Route{
|
|
||||||
Type: newRouteType,
|
|
||||||
})
|
|
||||||
|
|
||||||
callID := CallAsync(t, s.Context, u, &bytes.Buffer{})
|
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
|
|
||||||
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
|
||||||
_, err = s.Client.Call.GetAppsAppCallsCall(cfg)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if retryErr != nil {
|
|
||||||
t.Error(retryErr.Error())
|
|
||||||
} else {
|
|
||||||
callResponse, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
|
||||||
if err != nil {
|
|
||||||
switch err.(type) {
|
|
||||||
case *call.GetAppsAppCallsCallNotFound:
|
|
||||||
msg := err.(*call.GetAppsAppCallsCallNotFound).Payload.Error.Message
|
|
||||||
t.Errorf("Unexpected error occurred: %v.", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callObject := callResponse.Payload.Call
|
|
||||||
|
|
||||||
if callObject.ID != callID {
|
|
||||||
t.Errorf("Call object ID mismatch.\n\tExpected: %v\n\tActual:%v", callID, callObject.ID)
|
|
||||||
}
|
|
||||||
if callObject.Path != s.RoutePath {
|
|
||||||
t.Errorf("Call object route path mismatch.\n\tExpected: %v\n\tActual:%v", s.RoutePath, callObject.Path)
|
|
||||||
}
|
|
||||||
if callObject.Status != "success" {
|
|
||||||
t.Errorf("Call object status mismatch.\n\tExpected: %v\n\tActual:%v", "success", callObject.Status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanCauseTimeout(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
timeout := int32(10)
|
|
||||||
rt.Timeout = &timeout
|
|
||||||
rt.Type = "sync"
|
|
||||||
rt.Image = "funcy/timeout:0.0.1"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
|
|
||||||
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Seconds int64 `json:"seconds"`
|
|
||||||
}{Seconds: 11})
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
|
|
||||||
resp, _ := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
|
||||||
|
|
||||||
if !strings.Contains(output.String(), "Timed out") {
|
|
||||||
t.Errorf("Must fail because of timeout, but got error message: %v", output.String())
|
|
||||||
}
|
|
||||||
cfg := &call.GetAppsAppCallsCallParams{
|
|
||||||
Call: resp.Header.Get("FN_CALL_ID"),
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
cfg.WithTimeout(time.Second * 60)
|
|
||||||
|
|
||||||
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
|
||||||
_, err = s.Client.Call.GetAppsAppCallsCall(cfg)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if retryErr != nil {
|
|
||||||
t.Error(retryErr.Error())
|
|
||||||
} else {
|
|
||||||
callObj, err := s.Client.Call.GetAppsAppCallsCall(cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %s", err)
|
|
||||||
}
|
|
||||||
if !strings.Contains("timeout", callObj.Payload.Call.Status) {
|
|
||||||
t.Errorf("Call status mismatch.\n\tExpected: %v\n\tActual: %v",
|
|
||||||
"output", "callObj.Payload.Call.Status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCallResponseHeadersMatch(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "denismakogon/os.environ"
|
|
||||||
rt.Type = "sync"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
CallFN(s.Context, u.String(), content, output, "POST",
|
|
||||||
[]string{
|
|
||||||
"ACCEPT: application/xml",
|
|
||||||
"ACCEPT: application/json; q=0.2",
|
|
||||||
})
|
|
||||||
res := output.String()
|
|
||||||
if !strings.Contains("application/xml, application/json; q=0.2", res) {
|
|
||||||
t.Errorf("HEADER_ACCEPT='application/xml, application/json; q=0.2' "+
|
|
||||||
"should be in output, have:%s\n", res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanWriteLogs(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Path = "/log"
|
|
||||||
rt.Image = "funcy/log:0.0.1"
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Size int
|
|
||||||
}{Size: 20})
|
|
||||||
|
|
||||||
callID := CallSync(t, s.Context, u, content)
|
|
||||||
|
|
||||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this test is redundant we have 3 tests for this?
|
|
||||||
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
|
||||||
_, err = s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if retryErr != nil {
|
|
||||||
t.Error(retryErr.Error())
|
|
||||||
} else {
|
|
||||||
_, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOversizedLog(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Path = "/log"
|
|
||||||
rt.Image = "funcy/log:0.0.1"
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
size := 1 * 1024 * 1024 * 1024
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, rt.Path)
|
|
||||||
content := &bytes.Buffer{}
|
|
||||||
json.NewEncoder(content).Encode(struct {
|
|
||||||
Size int
|
|
||||||
}{Size: size}) //exceeding log by 1 symbol
|
|
||||||
|
|
||||||
callID := CallSync(t, s.Context, u, content)
|
|
||||||
|
|
||||||
cfg := &operations.GetAppsAppCallsCallLogParams{
|
|
||||||
Call: callID,
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
retryErr := APICallWithRetry(t, 10, time.Second*2, func() (err error) {
|
|
||||||
_, err = s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
if retryErr != nil {
|
|
||||||
t.Error(retryErr.Error())
|
|
||||||
} else {
|
|
||||||
logObj, err := s.Client.Operations.GetAppsAppCallsCallLog(cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
log := logObj.Payload.Log.Log
|
|
||||||
if len(log) >= size {
|
|
||||||
t.Errorf("Log entry suppose to be truncated up to expected size %v, got %v",
|
|
||||||
size/1024, len(log))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
FROM fnproject/go:dev as build-stage
|
|
||||||
WORKDIR /function
|
|
||||||
ADD . /src
|
|
||||||
RUN cd /src && go build -o func
|
|
||||||
FROM fnproject/go
|
|
||||||
WORKDIR /function
|
|
||||||
COPY --from=build-stage /src/func /function/
|
|
||||||
ENTRYPOINT ["./func"]
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Person struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JSON struct {
|
|
||||||
Headers http.Header `json:"headers"`
|
|
||||||
Body string `json:"body,omitempty"`
|
|
||||||
StatusCode int `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
stdin := json.NewDecoder(os.Stdin)
|
|
||||||
stdout := json.NewEncoder(os.Stdout)
|
|
||||||
stderr := json.NewEncoder(os.Stderr)
|
|
||||||
for {
|
|
||||||
in := &JSON{}
|
|
||||||
|
|
||||||
err := stdin.Decode(in)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to decode incoming data: %s", err.Error())
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
}
|
|
||||||
person := Person{}
|
|
||||||
stderr.Encode(in.Body)
|
|
||||||
if len(in.Body) != 0 {
|
|
||||||
if err := json.NewDecoder(bytes.NewReader([]byte(in.Body))).Decode(&person); err != nil {
|
|
||||||
log.Fatalf("Unable to decode Person object data: %s", err.Error())
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if person.Name == "" {
|
|
||||||
person.Name = "World"
|
|
||||||
}
|
|
||||||
|
|
||||||
mapResult := map[string]string{"message": fmt.Sprintf("Hello %s", person.Name)}
|
|
||||||
b, err := json.Marshal(mapResult)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to marshal JSON response body: %s", err.Error())
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
}
|
|
||||||
h := http.Header{}
|
|
||||||
h.Set("Content-Type", "application/json")
|
|
||||||
h.Set("Content-Length", strconv.Itoa(len(b)))
|
|
||||||
out := &JSON{
|
|
||||||
StatusCode: http.StatusOK,
|
|
||||||
Body: string(b),
|
|
||||||
Headers: h,
|
|
||||||
}
|
|
||||||
stderr.Encode(out)
|
|
||||||
if err := stdout.Encode(out); err != nil {
|
|
||||||
log.Fatalf("Unable to encode JSON response: %s", err.Error())
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
FROM fnproject/go:dev as build-stage
|
|
||||||
WORKDIR /function
|
|
||||||
ADD . /src
|
|
||||||
RUN cd /src && go build -o func
|
|
||||||
FROM fnproject/go
|
|
||||||
WORKDIR /function
|
|
||||||
COPY --from=build-stage /src/func /function/
|
|
||||||
ENTRYPOINT ["./func"]
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
name: funcy/log
|
|
||||||
version: 0.0.1
|
|
||||||
runtime: go
|
|
||||||
entrypoint: ./func
|
|
||||||
path: /log
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
const lBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
|
|
||||||
type OutputSize struct {
|
|
||||||
Size int `json:"size"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandStringBytes(n int) string {
|
|
||||||
b := make([]byte, n)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = lBytes[rand.Intn(len(lBytes))]
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
out := &OutputSize{}
|
|
||||||
json.NewDecoder(os.Stdin).Decode(out)
|
|
||||||
fmt.Fprintln(os.Stderr, RandStringBytes(out.Size))
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"size": 1048576
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
FROM fnproject/go:dev as build-stage
|
|
||||||
WORKDIR /function
|
|
||||||
ADD . /src
|
|
||||||
RUN cd /src && go build -o func
|
|
||||||
FROM fnproject/go
|
|
||||||
WORKDIR /function
|
|
||||||
COPY --from=build-stage /src/func /function/
|
|
||||||
ENTRYPOINT ["./func"]
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
name: funcy/timeout
|
|
||||||
version: 0.0.1
|
|
||||||
runtime: go
|
|
||||||
entrypoint: ./func
|
|
||||||
path: /timeouter
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
time.Sleep(32 * time.Second)
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSONResponse struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFnJSONFormats(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
// TODO(treeder): put image in fnproject @ dockerhub
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "denismakogon/test-hot-json-go:0.0.1"
|
|
||||||
rt.Format = "json"
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
u := url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: Host(),
|
|
||||||
}
|
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
b, _ := json.Marshal(&struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}{
|
|
||||||
Name: "Jimmy",
|
|
||||||
})
|
|
||||||
content := bytes.NewBuffer(b)
|
|
||||||
output := &bytes.Buffer{}
|
|
||||||
resp, err := CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Got unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &JSONResponse{}
|
|
||||||
json.Unmarshal(output.Bytes(), msg)
|
|
||||||
expectedOutput := "Hello Jimmy"
|
|
||||||
if !strings.Contains(expectedOutput, msg.Message) {
|
|
||||||
t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedHeaderNames := []string{"Content-Type", "Content-Length"}
|
|
||||||
expectedHeaderValues := []string{"application/json; charset=utf-8", strconv.Itoa(output.Len())}
|
|
||||||
for i, name := range expectedHeaderNames {
|
|
||||||
actual := resp.Header.Get(name)
|
|
||||||
expected := expectedHeaderValues[i]
|
|
||||||
if !strings.Contains(expected, actual) {
|
|
||||||
t.Errorf("HTTP header assertion error for %v."+
|
|
||||||
"\n\tExpected: %v\n\tActual: %v", name, expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn/api/server"
|
|
||||||
_ "github.com/fnproject/fn/api/server/defaultexts"
|
|
||||||
)
|
|
||||||
|
|
||||||
func stopServer(done chan struct{}, stop func()) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
stop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
case <-ctx.Done():
|
|
||||||
log.Panic("Server Cleanup failed, timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func startServer() (chan struct{}, func()) {
|
|
||||||
|
|
||||||
log.Print("Starting server")
|
|
||||||
|
|
||||||
ctx, srvCancel := context.WithCancel(context.Background())
|
|
||||||
srvDone := make(chan struct{})
|
|
||||||
|
|
||||||
timeString := time.Now().Format("2006_01_02_15_04_05")
|
|
||||||
dbURL := os.Getenv(server.EnvDBURL)
|
|
||||||
tmpDir := os.TempDir()
|
|
||||||
tmpMq := fmt.Sprintf("%s/fn_integration_test_%s_worker_mq.db", tmpDir, timeString)
|
|
||||||
tmpDb := fmt.Sprintf("%s/fn_integration_test_%s_fn.db", tmpDir, timeString)
|
|
||||||
mqURL := fmt.Sprintf("bolt://%s", tmpMq)
|
|
||||||
if dbURL == "" {
|
|
||||||
dbURL = fmt.Sprintf("sqlite3://%s", tmpDb)
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := server.New(ctx,
|
|
||||||
server.WithLogLevel(getEnv(server.EnvLogLevel, server.DefaultLogLevel)),
|
|
||||||
server.WithDBURL(dbURL),
|
|
||||||
server.WithMQURL(mqURL),
|
|
||||||
server.WithFullAgent(),
|
|
||||||
)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
srv.Start(ctx)
|
|
||||||
log.Print("Stopped server")
|
|
||||||
os.Remove(tmpMq)
|
|
||||||
os.Remove(tmpDb)
|
|
||||||
close(srvDone)
|
|
||||||
}()
|
|
||||||
|
|
||||||
startCtx, startCancel := context.WithDeadline(ctx, time.Now().Add(time.Duration(10)*time.Second))
|
|
||||||
defer startCancel()
|
|
||||||
for {
|
|
||||||
err := checkServer(startCtx)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Second * 1):
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
log.Panic("Server check failed, timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return srvDone, srvCancel
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
done, cancel := startServer()
|
|
||||||
// call flag.Parse() here if TestMain uses flags
|
|
||||||
result := m.Run()
|
|
||||||
stopServer(done, cancel)
|
|
||||||
|
|
||||||
if result == 0 {
|
|
||||||
fmt.Fprintln(os.Stdout, "😀 👍 🎗")
|
|
||||||
}
|
|
||||||
os.Exit(result)
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn_go/client/routes"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AssertRouteMatches(t *testing.T, expected *models.Route, got *models.Route) {
|
|
||||||
|
|
||||||
if expected.Path != got.Path {
|
|
||||||
t.Errorf("Route path mismatch. Expected: %v. Actual: %v", expected.Path, got.Path)
|
|
||||||
}
|
|
||||||
if expected.Image != got.Image {
|
|
||||||
t.Errorf("Route image mismatch. Expected: %v. Actual: %v", expected.Image, got.Image)
|
|
||||||
}
|
|
||||||
if expected.Image != got.Image {
|
|
||||||
t.Errorf("Route type mismatch. Expected: %v. Actual: %v", expected.Image, got.Image)
|
|
||||||
}
|
|
||||||
if expected.Format != got.Format {
|
|
||||||
t.Errorf("Route format mismatch. Expected: %v. Actual: %v", expected.Format, got.Format)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostRoute Creates a route and deletes the corresponding app (if created) on teardown
|
|
||||||
func (s *TestHarness) PostRoute(appName string, route *models.Route) (*routes.PostAppsAppRoutesOK, error) {
|
|
||||||
cfg := &routes.PostAppsAppRoutesParams{
|
|
||||||
App: appName,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: route,
|
|
||||||
},
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
ok, err := s.Client.Routes.PostAppsAppRoutes(cfg)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
s.createdApps[appName] = true
|
|
||||||
}
|
|
||||||
return ok, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TestHarness) BasicRoute() *models.Route {
|
|
||||||
return &models.Route{
|
|
||||||
Format: s.Format,
|
|
||||||
Path: s.RoutePath,
|
|
||||||
Image: s.Image,
|
|
||||||
Type: s.RouteType,
|
|
||||||
Timeout: &s.Timeout,
|
|
||||||
IDLETimeout: &s.IdleTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//GivenRouteExists creates a route using the specified arguments, failing the test if the creation fails, this tears down any apps that are created when the test is complete
|
|
||||||
func (s *TestHarness) GivenRouteExists(t *testing.T, appName string, route *models.Route) {
|
|
||||||
_, err := s.PostRoute(appName, route)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected route to be created, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//RouteMustExist checks that a route exists, failing the test if it doesn't, returns the route
|
|
||||||
func (s *TestHarness) RouteMustExist(t *testing.T, appName string, routePath string) *models.Route {
|
|
||||||
cfg := &routes.GetAppsAppRoutesRouteParams{
|
|
||||||
App: appName,
|
|
||||||
Route: routePath[1:],
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
routeResponse, err := s.Client.Routes.GetAppsAppRoutesRoute(cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected route %s %s to exist but got %v", appName, routePath, err)
|
|
||||||
}
|
|
||||||
return routeResponse.Payload.Route
|
|
||||||
}
|
|
||||||
|
|
||||||
//GivenRoutePatched applies a patch to a route, failing the test if this fails.
|
|
||||||
func (s *TestHarness) GivenRoutePatched(t *testing.T, appName, routeName string, rt *models.Route) {
|
|
||||||
|
|
||||||
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
|
|
||||||
App: appName,
|
|
||||||
Route: routeName,
|
|
||||||
Context: s.Context,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: rt,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to patch route %s %s : %v", appName, routeName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertContainsRoute(routeModels []*models.Route, expectedRoute string) bool {
|
|
||||||
for _, r := range routeModels {
|
|
||||||
if r.Path == expectedRoute {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//PutRoute creates a route via PUT, tearing down any apps that are created when the test is complete
|
|
||||||
func (s *TestHarness) PutRoute(appName string, routePath string, route *models.Route) (*routes.PutAppsAppRoutesRouteOK, error) {
|
|
||||||
cfg := &routes.PutAppsAppRoutesRouteParams{
|
|
||||||
App: appName,
|
|
||||||
Context: s.Context,
|
|
||||||
Route: routePath,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: route,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := s.Client.Routes.PutAppsAppRoutesRoute(cfg)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
s.createdApps[appName] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
@@ -1,502 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn/api/id"
|
|
||||||
"github.com/fnproject/fn_go/client/apps"
|
|
||||||
"github.com/fnproject/fn_go/client/routes"
|
|
||||||
"github.com/fnproject/fn_go/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestShouldRejectEmptyRouteType(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
|
|
||||||
_, err := s.PostRoute(s.AppName, &models.Route{
|
|
||||||
Path: s.RoutePath,
|
|
||||||
Image: s.Image,
|
|
||||||
Type: "v",
|
|
||||||
Format: s.Format,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Should fail with Invalid route Type.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanCreateRoute(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
_, err := s.PostRoute(s.AppName, &models.Route{
|
|
||||||
Path: s.RoutePath,
|
|
||||||
Image: s.Image,
|
|
||||||
Format: s.Format,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected route success, got %v", err)
|
|
||||||
}
|
|
||||||
// TODO validate route returned matches request
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListRoutes(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
cfg := &routes.GetAppsAppRoutesParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
routesResponse, err := s.Client.Routes.GetAppsAppRoutes(cfg)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expecting list routes to be successful, got %v", err)
|
|
||||||
}
|
|
||||||
if !assertContainsRoute(routesResponse.Payload.Routes, s.RoutePath) {
|
|
||||||
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInspectRoute(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
newRt := s.BasicRoute()
|
|
||||||
s.GivenRouteExists(t, s.AppName, newRt)
|
|
||||||
|
|
||||||
resp, err := s.Client.Routes.GetAppsAppRoutesRoute(&routes.GetAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: newRt.Path[1:],
|
|
||||||
Context: s.Context,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to get route %s, %v", s.RoutePath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gotRt := resp.Payload.Route
|
|
||||||
|
|
||||||
AssertRouteMatches(t, newRt, gotRt)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var validRouteUpdates = []struct {
|
|
||||||
name string
|
|
||||||
update *models.Route
|
|
||||||
extract func(*models.Route) interface{}
|
|
||||||
}{
|
|
||||||
{"route type (sync)", &models.Route{Type: "sync"}, func(m *models.Route) interface{} { return m.Type }},
|
|
||||||
{"route type (async)", &models.Route{Type: "async"}, func(m *models.Route) interface{} { return m.Type }},
|
|
||||||
{"format (json)", &models.Route{Format: "json"}, func(m *models.Route) interface{} { return m.Format }},
|
|
||||||
{"format (default)", &models.Route{Format: "default"}, func(m *models.Route) interface{} { return m.Format }},
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanUpdateRouteAttributes(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tci := range validRouteUpdates {
|
|
||||||
tc := tci
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
routeResp, err := s.Client.Routes.PatchAppsAppRoutesRoute(
|
|
||||||
&routes.PatchAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
Route: s.RoutePath,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: tc.update,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to patch route, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
got := tc.extract(routeResp.Payload.Route)
|
|
||||||
change := tc.extract(tc.update)
|
|
||||||
if !reflect.DeepEqual(got, change) {
|
|
||||||
t.Errorf("Expected value in response tobe %v but was %v", change, got)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRoutePatchConfig(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
for _, tci := range updateConfigCases {
|
|
||||||
tc := tci
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
route := s.BasicRoute()
|
|
||||||
route.Config = tc.intialConfig
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, route)
|
|
||||||
|
|
||||||
routeResp, err := s.Client.Routes.PatchAppsAppRoutesRoute(
|
|
||||||
&routes.PatchAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: s.RoutePath,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: &models.Route{
|
|
||||||
Config: tc.change,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Context: s.Context,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to patch route, got %v", err)
|
|
||||||
}
|
|
||||||
actual := routeResp.Payload.Route.Config
|
|
||||||
if !ConfigEquivalent(actual, tc.expected) {
|
|
||||||
t.Errorf("Expected config : %v after update, got %v", tc.expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetRouteAnnotationsOnCreate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
for _, tci := range createAnnotationsValidCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("valid_"+tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Annotations = tc.annotations
|
|
||||||
|
|
||||||
route, err := s.Client.Routes.PostAppsAppRoutes(&routes.PostAppsAppRoutesParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Context: s.Context,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: rt,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create route with valid annotations %v got error %v", tc.annotations, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gotMd := route.Payload.Route.Annotations
|
|
||||||
if !AnnotationsEquivalent(gotMd, tc.annotations) {
|
|
||||||
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
getRoute := s.RouteMustExist(t, s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
if !AnnotationsEquivalent(getRoute.Annotations, tc.annotations) {
|
|
||||||
t.Errorf("GET annotations '%v' does not match set annotations %v", getRoute.Annotations, tc.annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tci := range createAnnotationsErrorCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("invalid_"+tc.name, func(ti *testing.T) {
|
|
||||||
ti.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
_, err := s.PostApp(&models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
Annotations: tc.annotations,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Created app with invalid annotations %v but expected error", tc.annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := err.(*apps.PostAppsBadRequest); !ok {
|
|
||||||
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetRouteMetadataOnPatch(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tci := range updateAnnotationsValidCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("valid_"+tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Annotations = tc.initial
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
res, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: s.RoutePath[1:],
|
|
||||||
Context: s.Context,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: &models.Route{
|
|
||||||
Annotations: tc.change,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to patch annotations with %v on route: %v", tc.change, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gotMd := res.Payload.Route.Annotations
|
|
||||||
if !AnnotationsEquivalent(gotMd, tc.expected) {
|
|
||||||
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
getRoute := s.RouteMustExist(t, s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
if !AnnotationsEquivalent(getRoute.Annotations, tc.expected) {
|
|
||||||
t.Errorf("GET annotations '%v' does not match set annotations %v", getRoute.Annotations, tc.expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tci := range updateAnnotationsErrorCases {
|
|
||||||
// iterator mutation meets parallelism... pfft
|
|
||||||
tc := tci
|
|
||||||
t.Run("invalid_"+tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{
|
|
||||||
Name: s.AppName,
|
|
||||||
})
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Annotations = tc.initial
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: s.RoutePath[1:],
|
|
||||||
Context: s.Context,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: &models.Route{
|
|
||||||
Annotations: tc.change,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("patched route with invalid annotations %v but expected error", tc.change)
|
|
||||||
}
|
|
||||||
if _, ok := err.(*routes.PatchAppsAppRoutesRouteBadRequest); !ok {
|
|
||||||
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCantUpdateRoutePath(t *testing.T) {
|
|
||||||
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(
|
|
||||||
&routes.PatchAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: s.RoutePath,
|
|
||||||
Body: &models.RouteWrapper{
|
|
||||||
Route: &models.Route{
|
|
||||||
Path: id.New().String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected error when patching route")
|
|
||||||
}
|
|
||||||
if _, ok := err.(*routes.PatchAppsAppRoutesRouteBadRequest); ok {
|
|
||||||
t.Errorf("Error should be bad request when updating route path ")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRoutePreventsDuplicate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
_, err := s.PostRoute(s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Route duplicate error should appear, but it didn't")
|
|
||||||
}
|
|
||||||
if _, ok := err.(*routes.PostAppsAppRoutesConflict); !ok {
|
|
||||||
t.Errorf("Error should be a conflict when creating a new route, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanDeleteRoute(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
_, err := s.Client.Routes.DeleteAppsAppRoutesRoute(&routes.DeleteAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: s.RoutePath,
|
|
||||||
Context: s.Context,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected success when deleting existing route, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCantDeleteMissingRoute(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
|
|
||||||
_, err := s.Client.Routes.DeleteAppsAppRoutesRoute(&routes.DeleteAppsAppRoutesRouteParams{
|
|
||||||
App: s.AppName,
|
|
||||||
Route: s.RoutePath,
|
|
||||||
Context: s.Context,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected error when deleting non-existing route, got none")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := err.(*routes.DeleteAppsAppRoutesRouteNotFound); !ok {
|
|
||||||
t.Fatalf("Expected not-found when deleting non-existing route, got %v", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutRouteCreatesNewApp(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
_, err := s.PutRoute(s.AppName, s.RoutePath, s.BasicRoute())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected new route to be created, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.AppMustExist(t, s.AppName)
|
|
||||||
s.RouteMustExist(t, s.AppName, s.RoutePath)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutRouteToExistingApp(t *testing.T) {
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
_, err := s.PutRoute(s.AppName, s.RoutePath, s.BasicRoute())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create route, got error %v", err)
|
|
||||||
}
|
|
||||||
s.AppMustExist(t, s.AppName)
|
|
||||||
s.RouteMustExist(t, s.AppName, s.RoutePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutRouteUpdatesRoute(t *testing.T) {
|
|
||||||
newRouteType := "sync"
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
|
|
||||||
|
|
||||||
changed := s.BasicRoute()
|
|
||||||
changed.Type = newRouteType
|
|
||||||
|
|
||||||
updatedRoute, err := s.PutRoute(s.AppName, s.RoutePath, changed)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to update route, got %v", err)
|
|
||||||
}
|
|
||||||
got := updatedRoute.Payload.Route.Type
|
|
||||||
if got != newRouteType {
|
|
||||||
t.Errorf("expected type to be %v after update, got %v", newRouteType, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutIsIdempotentForHeaders(t *testing.T) {
|
|
||||||
s := SetupHarness()
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
s.GivenAppExists(t, &models.App{Name: s.AppName})
|
|
||||||
|
|
||||||
routeHeaders := map[string][]string{}
|
|
||||||
routeHeaders["A"] = []string{"a"}
|
|
||||||
routeHeaders["B"] = []string{"b"}
|
|
||||||
|
|
||||||
r1 := s.BasicRoute()
|
|
||||||
r1.Headers = routeHeaders
|
|
||||||
|
|
||||||
updatedRoute1, err := s.PutRoute(s.AppName, s.RoutePath, r1)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to update route, got %v", err)
|
|
||||||
}
|
|
||||||
if firstMatches := reflect.DeepEqual(routeHeaders, updatedRoute1.Payload.Route.Headers); !firstMatches {
|
|
||||||
t.Errorf("Route headers should remain the same after multiple deploys with exact the same parameters '%v' != '%v'", routeHeaders, updatedRoute1.Payload.Route.Headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedRoute2, err := s.PutRoute(s.AppName, s.RoutePath, r1)
|
|
||||||
|
|
||||||
if bothmatch := reflect.DeepEqual(updatedRoute1.Payload.Route.Headers, updatedRoute2.Payload.Route.Headers); !bothmatch {
|
|
||||||
t.Error("Route headers should remain the same after multiple deploys with exact the same parameters")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fnproject/fn_go/client"
|
|
||||||
httptransport "github.com/go-openapi/runtime/client"
|
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const lBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
|
|
||||||
func GetAPIURL() (string, *url.URL) {
|
|
||||||
apiURL := getEnv("FN_API_URL", "http://localhost:8080")
|
|
||||||
u, err := url.Parse(apiURL)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Couldn't parse API URL: %s error: %s", apiURL, err)
|
|
||||||
}
|
|
||||||
return apiURL, u
|
|
||||||
}
|
|
||||||
|
|
||||||
func Host() string {
|
|
||||||
_, u := GetAPIURL()
|
|
||||||
return u.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
func APIClient() *client.Fn {
|
|
||||||
transport := httptransport.New(Host(), "/v1", []string{"http"})
|
|
||||||
if os.Getenv("FN_TOKEN") != "" {
|
|
||||||
transport.DefaultAuthentication = httptransport.BearerToken(os.Getenv("FN_TOKEN"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the API client, with the transport
|
|
||||||
return client.New(transport, strfmt.Default)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkServer(ctx context.Context) error {
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
log.Print("Server check failed, timeout")
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
apiURL, _ := GetAPIURL()
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequest("GET", apiURL+"/version", nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Server check new request failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
_, err = client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Server is not up... err: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEnv(key, fallback string) string {
|
|
||||||
if value, ok := os.LookupEnv(key); ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHarness provides context and pre-configured clients to an individual test, it has some helper functions to create Apps and Routes that mirror the underlying client operations and clean them up after the test is complete
|
|
||||||
// This is not goroutine safe and each test case should use its own harness.
|
|
||||||
type TestHarness struct {
|
|
||||||
Context context.Context
|
|
||||||
Cancel func()
|
|
||||||
Client *client.Fn
|
|
||||||
AppName string
|
|
||||||
RoutePath string
|
|
||||||
Image string
|
|
||||||
RouteType string
|
|
||||||
Format string
|
|
||||||
Memory uint64
|
|
||||||
Timeout int32
|
|
||||||
IdleTimeout int32
|
|
||||||
RouteConfig map[string]string
|
|
||||||
RouteHeaders map[string][]string
|
|
||||||
|
|
||||||
createdApps map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandStringBytes(n int) string {
|
|
||||||
b := make([]byte, n)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = lBytes[rand.Intn(len(lBytes))]
|
|
||||||
}
|
|
||||||
return strings.ToLower(string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetupHarness creates a test harness for a test case - this picks up external options and
|
|
||||||
func SetupHarness() *TestHarness {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
||||||
|
|
||||||
ss := &TestHarness{
|
|
||||||
Context: ctx,
|
|
||||||
Cancel: cancel,
|
|
||||||
Client: APIClient(),
|
|
||||||
AppName: "fnintegrationtestapp" + RandStringBytes(10),
|
|
||||||
RoutePath: "/fnintegrationtestroute" + RandStringBytes(10),
|
|
||||||
Image: "fnproject/hello",
|
|
||||||
Format: "default",
|
|
||||||
RouteType: "async",
|
|
||||||
RouteConfig: map[string]string{},
|
|
||||||
RouteHeaders: map[string][]string{},
|
|
||||||
Memory: uint64(256),
|
|
||||||
Timeout: int32(30),
|
|
||||||
IdleTimeout: int32(30),
|
|
||||||
createdApps: make(map[string]bool),
|
|
||||||
}
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TestHarness) Cleanup() {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
//for _,ar := range s.createdRoutes {
|
|
||||||
// deleteRoute(ctx, s.Client, ar.appName, ar.routeName)
|
|
||||||
//}
|
|
||||||
|
|
||||||
for app, _ := range s.createdApps {
|
|
||||||
safeDeleteApp(ctx, s.Client, app)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnvAsHeader(req *http.Request, selectedEnv []string) {
|
|
||||||
detectedEnv := os.Environ()
|
|
||||||
if len(selectedEnv) > 0 {
|
|
||||||
detectedEnv = selectedEnv
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range detectedEnv {
|
|
||||||
kv := strings.Split(e, "=")
|
|
||||||
name := kv[0]
|
|
||||||
req.Header.Set(name, os.Getenv(name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CallFN(ctx context.Context, u string, content io.Reader, output io.Writer, method string, env []string) (*http.Response, error) {
|
|
||||||
if method == "" {
|
|
||||||
if content == nil {
|
|
||||||
method = "GET"
|
|
||||||
} else {
|
|
||||||
method = "POST"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, u, content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error running route: %s", err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
|
|
||||||
if len(env) > 0 {
|
|
||||||
EnvAsHeader(req, env)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error running route: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
io.Copy(output, resp.Body)
|
|
||||||
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
}
|
|
||||||
|
|
||||||
func APICallWithRetry(t *testing.T, attempts int, sleep time.Duration, callback func() error) (err error) {
|
|
||||||
for i := 0; i < attempts; i++ {
|
|
||||||
err = callback()
|
|
||||||
if err == nil {
|
|
||||||
t.Log("Exiting retry loop, API call was successful")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t.Logf("[%v] - Retrying API call after unsuccessful attempt with error: %v", i, err.Error())
|
|
||||||
time.Sleep(sleep)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
@@ -13,8 +15,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
apiutils "github.com/fnproject/fn/test/fn-api-tests"
|
"github.com/fnproject/fn/api/models"
|
||||||
sdkmodels "github.com/fnproject/fn_go/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// See fn-test-utils for json response
|
// See fn-test-utils for json response
|
||||||
@@ -64,17 +65,9 @@ func getConfigContent(key string, respBytes []byte) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCanExecuteFunction(t *testing.T) {
|
func TestCanExecuteFunction(t *testing.T) {
|
||||||
s := apiutils.SetupHarness()
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
defer cancel()
|
||||||
defer s.Cleanup()
|
rt := ensureRoute(t)
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "fnproject/fn-test-utils"
|
|
||||||
rt.Format = "json"
|
|
||||||
rt.Memory = 64
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
lb, err := LB()
|
lb, err := LB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,13 +77,13 @@ func TestCanExecuteFunction(t *testing.T) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: lb,
|
Host: lb,
|
||||||
}
|
}
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
u.Path = path.Join(u.Path, "r", appName, rt.Path)
|
||||||
|
|
||||||
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
||||||
content := bytes.NewBuffer([]byte(body))
|
content := bytes.NewBuffer([]byte(body))
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
|
|
||||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
resp, err := callFN(ctx, u.String(), content, output, "POST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Got unexpected error: %v", err)
|
t.Fatalf("Got unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -119,17 +112,9 @@ func TestCanExecuteFunction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCanExecuteBigOutput(t *testing.T) {
|
func TestCanExecuteBigOutput(t *testing.T) {
|
||||||
s := apiutils.SetupHarness()
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
defer cancel()
|
||||||
defer s.Cleanup()
|
rt := ensureRoute(t)
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "fnproject/fn-test-utils"
|
|
||||||
rt.Format = "json"
|
|
||||||
rt.Memory = 64
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
lb, err := LB()
|
lb, err := LB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -139,14 +124,14 @@ func TestCanExecuteBigOutput(t *testing.T) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: lb,
|
Host: lb,
|
||||||
}
|
}
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
u.Path = path.Join(u.Path, "r", appName, rt.Path)
|
||||||
|
|
||||||
// Approx 5.3MB output
|
// Approx 5.3MB output
|
||||||
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true, "trailerRepeat": 410000}`
|
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true, "trailerRepeat": 410000}`
|
||||||
content := bytes.NewBuffer([]byte(body))
|
content := bytes.NewBuffer([]byte(body))
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
|
|
||||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
resp, err := callFN(ctx, u.String(), content, output, "POST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Got unexpected error: %v", err)
|
t.Fatalf("Got unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -164,17 +149,9 @@ func TestCanExecuteBigOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCanExecuteTooBigOutput(t *testing.T) {
|
func TestCanExecuteTooBigOutput(t *testing.T) {
|
||||||
s := apiutils.SetupHarness()
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
defer cancel()
|
||||||
defer s.Cleanup()
|
rt := ensureRoute(t)
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "fnproject/fn-test-utils"
|
|
||||||
rt.Format = "json"
|
|
||||||
rt.Memory = 64
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
lb, err := LB()
|
lb, err := LB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -184,14 +161,14 @@ func TestCanExecuteTooBigOutput(t *testing.T) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: lb,
|
Host: lb,
|
||||||
}
|
}
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
u.Path = path.Join(u.Path, "r", appName, rt.Path)
|
||||||
|
|
||||||
// > 6MB output
|
// > 6MB output
|
||||||
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true, "trailerRepeat": 600000}`
|
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true, "trailerRepeat": 600000}`
|
||||||
content := bytes.NewBuffer([]byte(body))
|
content := bytes.NewBuffer([]byte(body))
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
|
|
||||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
resp, err := callFN(ctx, u.String(), content, output, "POST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Got unexpected error: %v", err)
|
t.Fatalf("Got unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -209,17 +186,9 @@ func TestCanExecuteTooBigOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCanExecuteEmptyOutput(t *testing.T) {
|
func TestCanExecuteEmptyOutput(t *testing.T) {
|
||||||
s := apiutils.SetupHarness()
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
defer cancel()
|
||||||
defer s.Cleanup()
|
rt := ensureRoute(t)
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "fnproject/fn-test-utils"
|
|
||||||
rt.Format = "json"
|
|
||||||
rt.Memory = 64
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
lb, err := LB()
|
lb, err := LB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -229,14 +198,14 @@ func TestCanExecuteEmptyOutput(t *testing.T) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: lb,
|
Host: lb,
|
||||||
}
|
}
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
u.Path = path.Join(u.Path, "r", appName, rt.Path)
|
||||||
|
|
||||||
// empty body output
|
// empty body output
|
||||||
body := `{"sleepTime": 0, "isDebug": true, "isEmptyBody": true}`
|
body := `{"sleepTime": 0, "isDebug": true, "isEmptyBody": true}`
|
||||||
content := bytes.NewBuffer([]byte(body))
|
content := bytes.NewBuffer([]byte(body))
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
|
|
||||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
resp, err := callFN(ctx, u.String(), content, output, "POST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Got unexpected error: %v", err)
|
t.Fatalf("Got unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -253,19 +222,9 @@ func TestCanExecuteEmptyOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicConcurrentExecution(t *testing.T) {
|
func TestBasicConcurrentExecution(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
s := apiutils.SetupHarness()
|
defer cancel()
|
||||||
|
rt := ensureRoute(t)
|
||||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
|
||||||
defer s.Cleanup()
|
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "fnproject/fn-test-utils"
|
|
||||||
rt.Format = "json"
|
|
||||||
rt.Memory = 32
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
lb, err := LB()
|
lb, err := LB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -275,7 +234,7 @@ func TestBasicConcurrentExecution(t *testing.T) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: lb,
|
Host: lb,
|
||||||
}
|
}
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
u.Path = path.Join(u.Path, "r", appName, rt.Path)
|
||||||
|
|
||||||
results := make(chan error)
|
results := make(chan error)
|
||||||
concurrentFuncs := 10
|
concurrentFuncs := 10
|
||||||
@@ -284,7 +243,7 @@ func TestBasicConcurrentExecution(t *testing.T) {
|
|||||||
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
||||||
content := bytes.NewBuffer([]byte(body))
|
content := bytes.NewBuffer([]byte(body))
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
resp, err := callFN(ctx, u.String(), content, output, "POST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results <- fmt.Errorf("Got unexpected error: %v", err)
|
results <- fmt.Errorf("Got unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
@@ -313,26 +272,17 @@ func TestBasicConcurrentExecution(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSaturatedSystem(t *testing.T) {
|
func TestSaturatedSystem(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
|
||||||
s := apiutils.SetupHarness()
|
defer cancel()
|
||||||
|
rt := &models.Route{
|
||||||
// override default 60 secs with shorter.
|
Path: routeName,
|
||||||
s.Cancel()
|
Timeout: 1,
|
||||||
s.Context, s.Cancel = context.WithTimeout(context.Background(), 4*time.Second)
|
Image: "fnproject/fn-test-utils",
|
||||||
|
Format: "json",
|
||||||
s.GivenAppExists(t, &sdkmodels.App{Name: s.AppName})
|
Memory: 300,
|
||||||
defer s.Cleanup()
|
Type: "sync",
|
||||||
|
}
|
||||||
timeout := int32(1)
|
rt = ensureRoute(t, rt)
|
||||||
|
|
||||||
rt := s.BasicRoute()
|
|
||||||
rt.Image = "fnproject/fn-test-utils"
|
|
||||||
rt.Format = "json"
|
|
||||||
rt.Timeout = &timeout
|
|
||||||
rt.Memory = 300
|
|
||||||
rt.Type = "sync"
|
|
||||||
|
|
||||||
s.GivenRouteExists(t, s.AppName, rt)
|
|
||||||
|
|
||||||
lb, err := LB()
|
lb, err := LB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -342,14 +292,119 @@ func TestSaturatedSystem(t *testing.T) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: lb,
|
Host: lb,
|
||||||
}
|
}
|
||||||
u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath)
|
u.Path = path.Join(u.Path, "r", appName, rt.Path)
|
||||||
|
|
||||||
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
body := `{"echoContent": "HelloWorld", "sleepTime": 0, "isDebug": true}`
|
||||||
content := bytes.NewBuffer([]byte(body))
|
content := bytes.NewBuffer([]byte(body))
|
||||||
output := &bytes.Buffer{}
|
output := &bytes.Buffer{}
|
||||||
|
|
||||||
resp, err := apiutils.CallFN(s.Context, u.String(), content, output, "POST", []string{})
|
resp, err := callFN(ctx, u.String(), content, output, "POST")
|
||||||
if resp != nil || err == nil || s.Context.Err() == nil {
|
if resp != nil || err == nil || ctx.Err() == nil {
|
||||||
t.Fatalf("Expected response: %v err:%v", resp, err)
|
t.Fatalf("Expected response: %v err:%v", resp, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func callFN(ctx context.Context, u string, content io.Reader, output io.Writer, method string) (*http.Response, error) {
|
||||||
|
if method == "" {
|
||||||
|
if content == nil {
|
||||||
|
method = "GET"
|
||||||
|
} else {
|
||||||
|
method = "POST"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, u, content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error running route: %s", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error running route: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(output, resp.Body)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAPIURL() (string, *url.URL) {
|
||||||
|
apiURL := getEnv("FN_API_URL", "http://localhost:8080")
|
||||||
|
u, err := url.Parse(apiURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Couldn't parse API URL: %s error: %s", apiURL, err)
|
||||||
|
}
|
||||||
|
return apiURL, u
|
||||||
|
}
|
||||||
|
|
||||||
|
func host() string {
|
||||||
|
u, _ := getAPIURL()
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
appName = "systemtestapp"
|
||||||
|
routeName = "/systemtestroute"
|
||||||
|
image = "fnproject/fn-test-utils"
|
||||||
|
format = "json"
|
||||||
|
memory = 64
|
||||||
|
typ = "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ensureRoute(t *testing.T, rts ...*models.Route) *models.Route {
|
||||||
|
var rt *models.Route
|
||||||
|
if len(rts) > 0 {
|
||||||
|
rt = rts[0]
|
||||||
|
} else {
|
||||||
|
rt = &models.Route{
|
||||||
|
Path: routeName + "yabbadabbadoo",
|
||||||
|
Image: image,
|
||||||
|
Format: format,
|
||||||
|
Memory: memory,
|
||||||
|
Type: typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wrapped struct {
|
||||||
|
Route *models.Route `json:"route"`
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped.Route = rt
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := json.NewEncoder(&buf).Encode(wrapped)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error encoding body", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
urlStr := host() + "/v1/apps/" + appName + "/routes" + rt.Path
|
||||||
|
u, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error creating url", urlStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("PUT", u.String(), &buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error creating request", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error creating route", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
io.Copy(&buf, resp.Body)
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
t.Fatal("error creating/updating app or otherwise ensuring it exists:", resp.StatusCode, buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped.Route = nil
|
||||||
|
err = json.NewDecoder(&buf).Decode(&wrapped)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error decoding response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapped.Route
|
||||||
|
}
|
||||||
|
|||||||
5
vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
5
vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
*.sublime-*
|
|
||||||
.DS_Store
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
tags
|
|
||||||
7
vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
7
vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
@@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
12
vendor/github.com/PuerkitoBio/purell/LICENSE
generated
vendored
12
vendor/github.com/PuerkitoBio/purell/LICENSE
generated
vendored
@@ -1,12 +0,0 @@
|
|||||||
Copyright (c) 2012, Martin Angers
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
187
vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
187
vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
@@ -1,187 +0,0 @@
|
|||||||
# Purell
|
|
||||||
|
|
||||||
Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know...
|
|
||||||
|
|
||||||
Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc].
|
|
||||||
|
|
||||||
[](http://travis-ci.org/PuerkitoBio/purell)
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
`go get github.com/PuerkitoBio/purell`
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121).
|
|
||||||
* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich).
|
|
||||||
* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]).
|
|
||||||
* **v0.2.0** : Add benchmarks, Attempt IDN support.
|
|
||||||
* **v0.1.0** : Initial release.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."):
|
|
||||||
|
|
||||||
```go
|
|
||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleNormalizeURLString() {
|
|
||||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
|
||||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
fmt.Print(normalized)
|
|
||||||
}
|
|
||||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleMustNormalizeURLString() {
|
|
||||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
|
||||||
FlagsUnsafeGreedy)
|
|
||||||
fmt.Print(normalized)
|
|
||||||
|
|
||||||
// Output: http://somewebsite.com/Amazing%FA/url
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleNormalizeURL() {
|
|
||||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
|
||||||
fmt.Print(normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags:
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
// Safe normalizations
|
|
||||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
|
||||||
FlagLowercaseHost // http://HOST -> http://host
|
|
||||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
|
||||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
|
||||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
|
||||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
|
||||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
|
||||||
|
|
||||||
// Usually safe normalizations
|
|
||||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
|
||||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
|
||||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
|
||||||
|
|
||||||
// Unsafe normalizations
|
|
||||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
|
||||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
|
||||||
FlagForceHTTP // https://host -> http://host
|
|
||||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
|
||||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
|
||||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
|
||||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
|
||||||
|
|
||||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
|
||||||
// submitted by jehiah
|
|
||||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
|
||||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
|
||||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
|
||||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
|
||||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
|
||||||
|
|
||||||
// Convenience set of safe normalizations
|
|
||||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
|
||||||
|
|
||||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
|
||||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
|
||||||
|
|
||||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
|
||||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
|
||||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
|
||||||
|
|
||||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
|
||||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
|
||||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
|
||||||
|
|
||||||
// Convenience set of all available flags
|
|
||||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set.
|
|
||||||
|
|
||||||
The [full godoc reference is available on gopkgdoc][godoc].
|
|
||||||
|
|
||||||
Some things to note:
|
|
||||||
|
|
||||||
* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it.
|
|
||||||
|
|
||||||
* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*):
|
|
||||||
- %24 -> $
|
|
||||||
- %26 -> &
|
|
||||||
- %2B-%3B -> +,-./0123456789:;
|
|
||||||
- %3D -> =
|
|
||||||
- %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
||||||
- %5F -> _
|
|
||||||
- %61-%7A -> abcdefghijklmnopqrstuvwxyz
|
|
||||||
- %7E -> ~
|
|
||||||
|
|
||||||
|
|
||||||
* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization).
|
|
||||||
|
|
||||||
* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell.
|
|
||||||
|
|
||||||
* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object.
|
|
||||||
|
|
||||||
### Safe vs Usually Safe vs Unsafe
|
|
||||||
|
|
||||||
Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between.
|
|
||||||
|
|
||||||
Consider the following URL:
|
|
||||||
|
|
||||||
`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
|
||||||
|
|
||||||
Normalizing with the `FlagsSafe` gives:
|
|
||||||
|
|
||||||
`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
|
||||||
|
|
||||||
With the `FlagsUsuallySafeGreedy`:
|
|
||||||
|
|
||||||
`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid`
|
|
||||||
|
|
||||||
And with `FlagsUnsafeGreedy`:
|
|
||||||
|
|
||||||
`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3`
|
|
||||||
|
|
||||||
## TODOs
|
|
||||||
|
|
||||||
* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`.
|
|
||||||
|
|
||||||
## Thanks / Contributions
|
|
||||||
|
|
||||||
@rogpeppe
|
|
||||||
@jehiah
|
|
||||||
@opennota
|
|
||||||
@pchristopher1275
|
|
||||||
@zenovich
|
|
||||||
@beeker1121
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The [BSD 3-Clause license][bsd].
|
|
||||||
|
|
||||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
|
||||||
[wiki]: http://en.wikipedia.org/wiki/URL_normalization
|
|
||||||
[rfc]: http://tools.ietf.org/html/rfc3986#section-6
|
|
||||||
[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell
|
|
||||||
[pr5]: https://github.com/PuerkitoBio/purell/pull/5
|
|
||||||
[iss7]: https://github.com/PuerkitoBio/purell/issues/7
|
|
||||||
57
vendor/github.com/PuerkitoBio/purell/bench_test.go
generated
vendored
57
vendor/github.com/PuerkitoBio/purell/bench_test.go
generated
vendored
@@ -1,57 +0,0 @@
|
|||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
safeUrl = "HttPS://..iaMHost..Test:443/paTh^A%ef//./%41PaTH/..//?"
|
|
||||||
usuallySafeUrl = "HttPS://..iaMHost..Test:443/paTh^A%ef//./%41PaTH/../final/"
|
|
||||||
unsafeUrl = "HttPS://..www.iaMHost..Test:443/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
|
||||||
allDWORDUrl = "HttPS://1113982867:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
|
||||||
allOctalUrl = "HttPS://0102.0146.07.0223:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
|
||||||
allHexUrl = "HttPS://0x42660793:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
|
||||||
allCombinedUrl = "HttPS://..0x42660793.:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkSafe(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(safeUrl, FlagsSafe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkUsuallySafe(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(usuallySafeUrl, FlagsUsuallySafeGreedy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkUnsafe(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(unsafeUrl, FlagsUnsafeGreedy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAllDWORD(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(allDWORDUrl, FlagsAllGreedy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAllOctal(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(allOctalUrl, FlagsAllGreedy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAllHex(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(allHexUrl, FlagsAllGreedy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAllCombined(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NormalizeURLString(allCombinedUrl, FlagsAllGreedy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0
generated
vendored
9
vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
PASS
|
|
||||||
BenchmarkSafe 500000 6131 ns/op
|
|
||||||
BenchmarkUsuallySafe 200000 7864 ns/op
|
|
||||||
BenchmarkUnsafe 100000 28560 ns/op
|
|
||||||
BenchmarkAllDWORD 50000 38722 ns/op
|
|
||||||
BenchmarkAllOctal 50000 40941 ns/op
|
|
||||||
BenchmarkAllHex 50000 44063 ns/op
|
|
||||||
BenchmarkAllCombined 50000 33613 ns/op
|
|
||||||
ok github.com/PuerkitoBio/purell 17.404s
|
|
||||||
35
vendor/github.com/PuerkitoBio/purell/example_test.go
generated
vendored
35
vendor/github.com/PuerkitoBio/purell/example_test.go
generated
vendored
@@ -1,35 +0,0 @@
|
|||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleNormalizeURLString() {
|
|
||||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
|
||||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
fmt.Print(normalized)
|
|
||||||
}
|
|
||||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleMustNormalizeURLString() {
|
|
||||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
|
||||||
FlagsUnsafeGreedy)
|
|
||||||
fmt.Print(normalized)
|
|
||||||
|
|
||||||
// Output: http://somewebsite.com/Amazing%FA/url
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleNormalizeURL() {
|
|
||||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
|
||||||
fmt.Print(normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
|
||||||
}
|
|
||||||
379
vendor/github.com/PuerkitoBio/purell/purell.go
generated
vendored
379
vendor/github.com/PuerkitoBio/purell/purell.go
generated
vendored
@@ -1,379 +0,0 @@
|
|||||||
/*
|
|
||||||
Package purell offers URL normalization as described on the wikipedia page:
|
|
||||||
http://en.wikipedia.org/wiki/URL_normalization
|
|
||||||
*/
|
|
||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/PuerkitoBio/urlesc"
|
|
||||||
"golang.org/x/net/idna"
|
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
"golang.org/x/text/width"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A set of normalization flags determines how a URL will
|
|
||||||
// be normalized.
|
|
||||||
type NormalizationFlags uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Safe normalizations
|
|
||||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
|
||||||
FlagLowercaseHost // http://HOST -> http://host
|
|
||||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
|
||||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
|
||||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
|
||||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
|
||||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
|
||||||
|
|
||||||
// Usually safe normalizations
|
|
||||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
|
||||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
|
||||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
|
||||||
|
|
||||||
// Unsafe normalizations
|
|
||||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
|
||||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
|
||||||
FlagForceHTTP // https://host -> http://host
|
|
||||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
|
||||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
|
||||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
|
||||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
|
||||||
|
|
||||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
|
||||||
// submitted by jehiah
|
|
||||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
|
||||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
|
||||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
|
||||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
|
||||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
|
||||||
|
|
||||||
// Convenience set of safe normalizations
|
|
||||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
|
||||||
|
|
||||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
|
||||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
|
||||||
|
|
||||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
|
||||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
|
||||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
|
||||||
|
|
||||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
|
||||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
|
||||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
|
||||||
|
|
||||||
// Convenience set of all available flags
|
|
||||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultHttpPort = ":80"
|
|
||||||
defaultHttpsPort = ":443"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Regular expressions used by the normalizations
|
|
||||||
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
|
|
||||||
var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`)
|
|
||||||
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
|
|
||||||
var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`)
|
|
||||||
var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`)
|
|
||||||
var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`)
|
|
||||||
var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`)
|
|
||||||
var rxEmptyPort = regexp.MustCompile(`:+$`)
|
|
||||||
|
|
||||||
// Map of flags to implementation function.
|
|
||||||
// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically
|
|
||||||
// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator.
|
|
||||||
|
|
||||||
// Since maps have undefined traversing order, make a slice of ordered keys
|
|
||||||
var flagsOrder = []NormalizationFlags{
|
|
||||||
FlagLowercaseScheme,
|
|
||||||
FlagLowercaseHost,
|
|
||||||
FlagRemoveDefaultPort,
|
|
||||||
FlagRemoveDirectoryIndex,
|
|
||||||
FlagRemoveDotSegments,
|
|
||||||
FlagRemoveFragment,
|
|
||||||
FlagForceHTTP, // Must be after remove default port (because https=443/http=80)
|
|
||||||
FlagRemoveDuplicateSlashes,
|
|
||||||
FlagRemoveWWW,
|
|
||||||
FlagAddWWW,
|
|
||||||
FlagSortQuery,
|
|
||||||
FlagDecodeDWORDHost,
|
|
||||||
FlagDecodeOctalHost,
|
|
||||||
FlagDecodeHexHost,
|
|
||||||
FlagRemoveUnnecessaryHostDots,
|
|
||||||
FlagRemoveEmptyPortSeparator,
|
|
||||||
FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last
|
|
||||||
FlagAddTrailingSlash,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and then the map, where order is unimportant
|
|
||||||
var flags = map[NormalizationFlags]func(*url.URL){
|
|
||||||
FlagLowercaseScheme: lowercaseScheme,
|
|
||||||
FlagLowercaseHost: lowercaseHost,
|
|
||||||
FlagRemoveDefaultPort: removeDefaultPort,
|
|
||||||
FlagRemoveDirectoryIndex: removeDirectoryIndex,
|
|
||||||
FlagRemoveDotSegments: removeDotSegments,
|
|
||||||
FlagRemoveFragment: removeFragment,
|
|
||||||
FlagForceHTTP: forceHTTP,
|
|
||||||
FlagRemoveDuplicateSlashes: removeDuplicateSlashes,
|
|
||||||
FlagRemoveWWW: removeWWW,
|
|
||||||
FlagAddWWW: addWWW,
|
|
||||||
FlagSortQuery: sortQuery,
|
|
||||||
FlagDecodeDWORDHost: decodeDWORDHost,
|
|
||||||
FlagDecodeOctalHost: decodeOctalHost,
|
|
||||||
FlagDecodeHexHost: decodeHexHost,
|
|
||||||
FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots,
|
|
||||||
FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator,
|
|
||||||
FlagRemoveTrailingSlash: removeTrailingSlash,
|
|
||||||
FlagAddTrailingSlash: addTrailingSlash,
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustNormalizeURLString returns the normalized string, and panics if an error occurs.
|
|
||||||
// It takes an URL string as input, as well as the normalization flags.
|
|
||||||
func MustNormalizeURLString(u string, f NormalizationFlags) string {
|
|
||||||
result, e := NormalizeURLString(u, f)
|
|
||||||
if e != nil {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object.
|
|
||||||
// It takes an URL string as input, as well as the normalization flags.
|
|
||||||
func NormalizeURLString(u string, f NormalizationFlags) (string, error) {
|
|
||||||
parsed, err := url.Parse(u)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if f&FlagLowercaseHost == FlagLowercaseHost {
|
|
||||||
parsed.Host = strings.ToLower(parsed.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The idna package doesn't fully conform to RFC 5895
|
|
||||||
// (https://tools.ietf.org/html/rfc5895), so we do it here.
|
|
||||||
// Taken from Go 1.8 cycle source, courtesy of bradfitz.
|
|
||||||
// TODO: Remove when (if?) idna package conforms to RFC 5895.
|
|
||||||
parsed.Host = width.Fold.String(parsed.Host)
|
|
||||||
parsed.Host = norm.NFC.String(parsed.Host)
|
|
||||||
if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NormalizeURL(parsed, f), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeURL returns the normalized string.
|
|
||||||
// It takes a parsed URL object as input, as well as the normalization flags.
|
|
||||||
func NormalizeURL(u *url.URL, f NormalizationFlags) string {
|
|
||||||
for _, k := range flagsOrder {
|
|
||||||
if f&k == k {
|
|
||||||
flags[k](u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return urlesc.Escape(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lowercaseScheme(u *url.URL) {
|
|
||||||
if len(u.Scheme) > 0 {
|
|
||||||
u.Scheme = strings.ToLower(u.Scheme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lowercaseHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
u.Host = strings.ToLower(u.Host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDefaultPort(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
scheme := strings.ToLower(u.Scheme)
|
|
||||||
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
|
|
||||||
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeTrailingSlash(u *url.URL) {
|
|
||||||
if l := len(u.Path); l > 0 {
|
|
||||||
if strings.HasSuffix(u.Path, "/") {
|
|
||||||
u.Path = u.Path[:l-1]
|
|
||||||
}
|
|
||||||
} else if l = len(u.Host); l > 0 {
|
|
||||||
if strings.HasSuffix(u.Host, "/") {
|
|
||||||
u.Host = u.Host[:l-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addTrailingSlash(u *url.URL) {
|
|
||||||
if l := len(u.Path); l > 0 {
|
|
||||||
if !strings.HasSuffix(u.Path, "/") {
|
|
||||||
u.Path += "/"
|
|
||||||
}
|
|
||||||
} else if l = len(u.Host); l > 0 {
|
|
||||||
if !strings.HasSuffix(u.Host, "/") {
|
|
||||||
u.Host += "/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDotSegments(u *url.URL) {
|
|
||||||
if len(u.Path) > 0 {
|
|
||||||
var dotFree []string
|
|
||||||
var lastIsDot bool
|
|
||||||
|
|
||||||
sections := strings.Split(u.Path, "/")
|
|
||||||
for _, s := range sections {
|
|
||||||
if s == ".." {
|
|
||||||
if len(dotFree) > 0 {
|
|
||||||
dotFree = dotFree[:len(dotFree)-1]
|
|
||||||
}
|
|
||||||
} else if s != "." {
|
|
||||||
dotFree = append(dotFree, s)
|
|
||||||
}
|
|
||||||
lastIsDot = (s == "." || s == "..")
|
|
||||||
}
|
|
||||||
// Special case if host does not end with / and new path does not begin with /
|
|
||||||
u.Path = strings.Join(dotFree, "/")
|
|
||||||
if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") {
|
|
||||||
u.Path = "/" + u.Path
|
|
||||||
}
|
|
||||||
// Special case if the last segment was a dot, make sure the path ends with a slash
|
|
||||||
if lastIsDot && !strings.HasSuffix(u.Path, "/") {
|
|
||||||
u.Path += "/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDirectoryIndex(u *url.URL) {
|
|
||||||
if len(u.Path) > 0 {
|
|
||||||
u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeFragment(u *url.URL) {
|
|
||||||
u.Fragment = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func forceHTTP(u *url.URL) {
|
|
||||||
if strings.ToLower(u.Scheme) == "https" {
|
|
||||||
u.Scheme = "http"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDuplicateSlashes(u *url.URL) {
|
|
||||||
if len(u.Path) > 0 {
|
|
||||||
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeWWW(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
|
||||||
u.Host = u.Host[4:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addWWW(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
|
||||||
u.Host = "www." + u.Host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortQuery(u *url.URL) {
|
|
||||||
q := u.Query()
|
|
||||||
|
|
||||||
if len(q) > 0 {
|
|
||||||
arKeys := make([]string, len(q))
|
|
||||||
i := 0
|
|
||||||
for k, _ := range q {
|
|
||||||
arKeys[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Strings(arKeys)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
for _, k := range arKeys {
|
|
||||||
sort.Strings(q[k])
|
|
||||||
for _, v := range q[k] {
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
buf.WriteRune('&')
|
|
||||||
}
|
|
||||||
buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild the raw query string
|
|
||||||
u.RawQuery = buf.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeDWORDHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
|
||||||
var parts [4]int64
|
|
||||||
|
|
||||||
dword, _ := strconv.ParseInt(matches[1], 10, 0)
|
|
||||||
for i, shift := range []uint{24, 16, 8, 0} {
|
|
||||||
parts[i] = dword >> shift & 0xFF
|
|
||||||
}
|
|
||||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeOctalHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 {
|
|
||||||
var parts [4]int64
|
|
||||||
|
|
||||||
for i := 1; i <= 4; i++ {
|
|
||||||
parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0)
|
|
||||||
}
|
|
||||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeHexHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
|
||||||
// Conversion is safe because of regex validation
|
|
||||||
parsed, _ := strconv.ParseInt(matches[1], 16, 0)
|
|
||||||
// Set host as DWORD (base 10) encoded host
|
|
||||||
u.Host = fmt.Sprintf("%d%s", parsed, matches[2])
|
|
||||||
// The rest is the same as decoding a DWORD host
|
|
||||||
decodeDWORDHost(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeUnncessaryHostDots(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 {
|
|
||||||
// Trim the leading and trailing dots
|
|
||||||
u.Host = strings.Trim(matches[1], ".")
|
|
||||||
if len(matches) > 2 {
|
|
||||||
u.Host += matches[2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeEmptyPortSeparator(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
u.Host = rxEmptyPort.ReplaceAllString(u.Host, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
768
vendor/github.com/PuerkitoBio/purell/purell_test.go
generated
vendored
768
vendor/github.com/PuerkitoBio/purell/purell_test.go
generated
vendored
@@ -1,768 +0,0 @@
|
|||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
nm string
|
|
||||||
src string
|
|
||||||
flgs NormalizationFlags
|
|
||||||
res string
|
|
||||||
parsed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
cases = [...]*testCase{
|
|
||||||
&testCase{
|
|
||||||
"LowerScheme",
|
|
||||||
"HTTP://www.SRC.ca",
|
|
||||||
FlagLowercaseScheme,
|
|
||||||
"http://www.SRC.ca",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"LowerScheme2",
|
|
||||||
"http://www.SRC.ca",
|
|
||||||
FlagLowercaseScheme,
|
|
||||||
"http://www.SRC.ca",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"LowerHost",
|
|
||||||
"HTTP://www.SRC.ca/",
|
|
||||||
FlagLowercaseHost,
|
|
||||||
"http://www.src.ca/", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UpperEscapes",
|
|
||||||
`http://www.whatever.com/Some%aa%20Special%8Ecases/`,
|
|
||||||
FlagUppercaseEscapes,
|
|
||||||
"http://www.whatever.com/Some%AA%20Special%8Ecases/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UnnecessaryEscapes",
|
|
||||||
`http://www.toto.com/%41%42%2E%44/%32%33%52%2D/%5f%7E`,
|
|
||||||
FlagDecodeUnnecessaryEscapes,
|
|
||||||
"http://www.toto.com/AB.D/23R-/_~",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDefaultPort",
|
|
||||||
"HTTP://www.SRC.ca:80/",
|
|
||||||
FlagRemoveDefaultPort,
|
|
||||||
"http://www.SRC.ca/", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDefaultPort2",
|
|
||||||
"HTTP://www.SRC.ca:80",
|
|
||||||
FlagRemoveDefaultPort,
|
|
||||||
"http://www.SRC.ca", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDefaultPort3",
|
|
||||||
"HTTP://www.SRC.ca:8080",
|
|
||||||
FlagRemoveDefaultPort,
|
|
||||||
"http://www.SRC.ca:8080", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Safe",
|
|
||||||
"HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e",
|
|
||||||
FlagsSafe,
|
|
||||||
"http://www.src.ca/to%1Ato%8B%EE/OKnowABC~",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"BothLower",
|
|
||||||
"HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e",
|
|
||||||
FlagLowercaseHost | FlagLowercaseScheme,
|
|
||||||
"http://www.src.ca:80/to%1Ato%8B%EE/OKnowABC~",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveTrailingSlash",
|
|
||||||
"HTTP://www.SRC.ca:80/",
|
|
||||||
FlagRemoveTrailingSlash,
|
|
||||||
"http://www.SRC.ca:80", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveTrailingSlash2",
|
|
||||||
"HTTP://www.SRC.ca:80/toto/titi/",
|
|
||||||
FlagRemoveTrailingSlash,
|
|
||||||
"http://www.SRC.ca:80/toto/titi", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveTrailingSlash3",
|
|
||||||
"HTTP://www.SRC.ca:80/toto/titi/fin/?a=1",
|
|
||||||
FlagRemoveTrailingSlash,
|
|
||||||
"http://www.SRC.ca:80/toto/titi/fin?a=1", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"AddTrailingSlash",
|
|
||||||
"HTTP://www.SRC.ca:80",
|
|
||||||
FlagAddTrailingSlash,
|
|
||||||
"http://www.SRC.ca:80/", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"AddTrailingSlash2",
|
|
||||||
"HTTP://www.SRC.ca:80/toto/titi.html",
|
|
||||||
FlagAddTrailingSlash,
|
|
||||||
"http://www.SRC.ca:80/toto/titi.html/", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"AddTrailingSlash3",
|
|
||||||
"HTTP://www.SRC.ca:80/toto/titi/fin?a=1",
|
|
||||||
FlagAddTrailingSlash,
|
|
||||||
"http://www.SRC.ca:80/toto/titi/fin/?a=1", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDotSegments",
|
|
||||||
"HTTP://root/a/b/./../../c/",
|
|
||||||
FlagRemoveDotSegments,
|
|
||||||
"http://root/c/", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDotSegments2",
|
|
||||||
"HTTP://root/../a/b/./../c/../d",
|
|
||||||
FlagRemoveDotSegments,
|
|
||||||
"http://root/a/d", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UsuallySafe",
|
|
||||||
"HTTP://www.SRC.ca:80/to%1ato%8b%ee/./c/d/../OKnow%41%42%43%7e/?a=b#test",
|
|
||||||
FlagsUsuallySafeGreedy,
|
|
||||||
"http://www.src.ca/to%1Ato%8B%EE/c/OKnowABC~?a=b#test",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDirectoryIndex",
|
|
||||||
"HTTP://root/a/b/c/default.aspx",
|
|
||||||
FlagRemoveDirectoryIndex,
|
|
||||||
"http://root/a/b/c/", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDirectoryIndex2",
|
|
||||||
"HTTP://root/a/b/c/default#a=b",
|
|
||||||
FlagRemoveDirectoryIndex,
|
|
||||||
"http://root/a/b/c/default#a=b", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveFragment",
|
|
||||||
"HTTP://root/a/b/c/default#toto=tata",
|
|
||||||
FlagRemoveFragment,
|
|
||||||
"http://root/a/b/c/default", // Since Go1.1, scheme is automatically lowercased
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"ForceHTTP",
|
|
||||||
"https://root/a/b/c/default#toto=tata",
|
|
||||||
FlagForceHTTP,
|
|
||||||
"http://root/a/b/c/default#toto=tata",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDuplicateSlashes",
|
|
||||||
"https://root/a//b///c////default#toto=tata",
|
|
||||||
FlagRemoveDuplicateSlashes,
|
|
||||||
"https://root/a/b/c/default#toto=tata",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveDuplicateSlashes2",
|
|
||||||
"https://root//a//b///c////default#toto=tata",
|
|
||||||
FlagRemoveDuplicateSlashes,
|
|
||||||
"https://root/a/b/c/default#toto=tata",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveWWW",
|
|
||||||
"https://www.root/a/b/c/",
|
|
||||||
FlagRemoveWWW,
|
|
||||||
"https://root/a/b/c/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveWWW2",
|
|
||||||
"https://WwW.Root/a/b/c/",
|
|
||||||
FlagRemoveWWW,
|
|
||||||
"https://Root/a/b/c/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"AddWWW",
|
|
||||||
"https://Root/a/b/c/",
|
|
||||||
FlagAddWWW,
|
|
||||||
"https://www.Root/a/b/c/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"SortQuery",
|
|
||||||
"http://root/toto/?b=4&a=1&c=3&b=2&a=5",
|
|
||||||
FlagSortQuery,
|
|
||||||
"http://root/toto/?a=1&a=5&b=2&b=4&c=3",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"RemoveEmptyQuerySeparator",
|
|
||||||
"http://root/toto/?",
|
|
||||||
FlagRemoveEmptyQuerySeparator,
|
|
||||||
"http://root/toto/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Unsafe",
|
|
||||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
|
||||||
FlagsUnsafeGreedy,
|
|
||||||
"http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Safe2",
|
|
||||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
|
||||||
FlagsSafe,
|
|
||||||
"https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UsuallySafe2",
|
|
||||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
|
||||||
FlagsUsuallySafeGreedy,
|
|
||||||
"https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"AddTrailingSlashBug",
|
|
||||||
"http://src.ca/",
|
|
||||||
FlagsAllNonGreedy,
|
|
||||||
"http://www.src.ca/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"SourceModified",
|
|
||||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
|
||||||
FlagsUnsafeGreedy,
|
|
||||||
"http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"IPv6-1",
|
|
||||||
"http://[2001:db8:1f70::999:de8:7648:6e8]/test",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://[2001:db8:1f70::999:de8:7648:6e8]/test",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"IPv6-2",
|
|
||||||
"http://[::ffff:192.168.1.1]/test",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://[::ffff:192.168.1.1]/test",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"IPv6-3",
|
|
||||||
"http://[::ffff:192.168.1.1]:80/test",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://[::ffff:192.168.1.1]/test",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"IPv6-4",
|
|
||||||
"htTps://[::fFff:192.168.1.1]:443/test",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"https://[::ffff:192.168.1.1]/test",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"FTP",
|
|
||||||
"ftp://user:pass@ftp.foo.net/foo/bar",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"ftp://user:pass@ftp.foo.net/foo/bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-1",
|
|
||||||
"http://www.foo.com:80/foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://www.foo.com/foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-2",
|
|
||||||
"http://www.foo.com:8000/foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://www.foo.com:8000/foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-3",
|
|
||||||
"http://www.foo.com/%7ebar",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://www.foo.com/~bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-4",
|
|
||||||
"http://www.foo.com/%7Ebar",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://www.foo.com/~bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-5",
|
|
||||||
"http://USER:pass@www.Example.COM/foo/bar",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://USER:pass@www.example.com/foo/bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-6",
|
|
||||||
"http://test.example/?a=%26&b=1",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://test.example/?a=%26&b=1",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-7",
|
|
||||||
"http://test.example/%25/?p=%20val%20%25",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://test.example/%25/?p=%20val%20%25",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-8",
|
|
||||||
"http://test.example/path/with a%20space+/",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://test.example/path/with%20a%20space+/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-9",
|
|
||||||
"http://test.example/?",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://test.example/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Standard-10",
|
|
||||||
"http://a.COM/path/?b&a",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://a.com/path/?b&a",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"StandardCasesAddTrailingSlash",
|
|
||||||
"http://test.example?",
|
|
||||||
FlagsSafe | FlagAddTrailingSlash,
|
|
||||||
"http://test.example/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"OctalIP-1",
|
|
||||||
"http://0123.011.0.4/",
|
|
||||||
FlagsSafe | FlagDecodeOctalHost,
|
|
||||||
"http://0123.011.0.4/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"OctalIP-2",
|
|
||||||
"http://0102.0146.07.0223/",
|
|
||||||
FlagsSafe | FlagDecodeOctalHost,
|
|
||||||
"http://66.102.7.147/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"OctalIP-3",
|
|
||||||
"http://0102.0146.07.0223.:23/",
|
|
||||||
FlagsSafe | FlagDecodeOctalHost,
|
|
||||||
"http://66.102.7.147.:23/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"OctalIP-4",
|
|
||||||
"http://USER:pass@0102.0146.07.0223../",
|
|
||||||
FlagsSafe | FlagDecodeOctalHost,
|
|
||||||
"http://USER:pass@66.102.7.147../",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"DWORDIP-1",
|
|
||||||
"http://123.1113982867/",
|
|
||||||
FlagsSafe | FlagDecodeDWORDHost,
|
|
||||||
"http://123.1113982867/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"DWORDIP-2",
|
|
||||||
"http://1113982867/",
|
|
||||||
FlagsSafe | FlagDecodeDWORDHost,
|
|
||||||
"http://66.102.7.147/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"DWORDIP-3",
|
|
||||||
"http://1113982867.:23/",
|
|
||||||
FlagsSafe | FlagDecodeDWORDHost,
|
|
||||||
"http://66.102.7.147.:23/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"DWORDIP-4",
|
|
||||||
"http://USER:pass@1113982867../",
|
|
||||||
FlagsSafe | FlagDecodeDWORDHost,
|
|
||||||
"http://USER:pass@66.102.7.147../",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"HexIP-1",
|
|
||||||
"http://0x123.1113982867/",
|
|
||||||
FlagsSafe | FlagDecodeHexHost,
|
|
||||||
"http://0x123.1113982867/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"HexIP-2",
|
|
||||||
"http://0x42660793/",
|
|
||||||
FlagsSafe | FlagDecodeHexHost,
|
|
||||||
"http://66.102.7.147/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"HexIP-3",
|
|
||||||
"http://0x42660793.:23/",
|
|
||||||
FlagsSafe | FlagDecodeHexHost,
|
|
||||||
"http://66.102.7.147.:23/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"HexIP-4",
|
|
||||||
"http://USER:pass@0x42660793../",
|
|
||||||
FlagsSafe | FlagDecodeHexHost,
|
|
||||||
"http://USER:pass@66.102.7.147../",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UnnecessaryHostDots-1",
|
|
||||||
"http://.www.foo.com../foo/bar.html",
|
|
||||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
|
||||||
"http://www.foo.com/foo/bar.html",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UnnecessaryHostDots-2",
|
|
||||||
"http://www.foo.com./foo/bar.html",
|
|
||||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
|
||||||
"http://www.foo.com/foo/bar.html",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UnnecessaryHostDots-3",
|
|
||||||
"http://www.foo.com.:81/foo",
|
|
||||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
|
||||||
"http://www.foo.com:81/foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UnnecessaryHostDots-4",
|
|
||||||
"http://www.example.com./",
|
|
||||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
|
||||||
"http://www.example.com/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"EmptyPort-1",
|
|
||||||
"http://www.thedraymin.co.uk:/main/?p=308",
|
|
||||||
FlagsSafe | FlagRemoveEmptyPortSeparator,
|
|
||||||
"http://www.thedraymin.co.uk/main/?p=308",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"EmptyPort-2",
|
|
||||||
"http://www.src.ca:",
|
|
||||||
FlagsSafe | FlagRemoveEmptyPortSeparator,
|
|
||||||
"http://www.src.ca",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-1",
|
|
||||||
"http://test.example/foo/bar/.",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/bar/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-2",
|
|
||||||
"http://test.example/foo/bar/./",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/bar/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-3",
|
|
||||||
"http://test.example/foo/bar/..",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-4",
|
|
||||||
"http://test.example/foo/bar/../",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-5",
|
|
||||||
"http://test.example/foo/bar/../baz",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/baz",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-6",
|
|
||||||
"http://test.example/foo/bar/../..",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-7",
|
|
||||||
"http://test.example/foo/bar/../../",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-8",
|
|
||||||
"http://test.example/foo/bar/../../baz",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/baz",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-9",
|
|
||||||
"http://test.example/foo/bar/../../../baz",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/baz",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-10",
|
|
||||||
"http://test.example/foo/bar/../../../../baz",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/baz",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-11",
|
|
||||||
"http://test.example/./foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-12",
|
|
||||||
"http://test.example/../foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-13",
|
|
||||||
"http://test.example/foo.",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo.",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-14",
|
|
||||||
"http://test.example/.foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/.foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-15",
|
|
||||||
"http://test.example/foo..",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo..",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-16",
|
|
||||||
"http://test.example/..foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/..foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-17",
|
|
||||||
"http://test.example/./../foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-18",
|
|
||||||
"http://test.example/./foo/.",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-19",
|
|
||||||
"http://test.example/foo/./bar",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-20",
|
|
||||||
"http://test.example/foo/../bar",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-21",
|
|
||||||
"http://test.example/foo//",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Slashes-22",
|
|
||||||
"http://test.example/foo///bar//",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"http://test.example/foo/bar/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Relative",
|
|
||||||
"foo/bar",
|
|
||||||
FlagsAllGreedy,
|
|
||||||
"foo/bar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Relative-1",
|
|
||||||
"./../foo",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Relative-2",
|
|
||||||
"./foo/bar/../baz/../bang/..",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"foo/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Relative-3",
|
|
||||||
"foo///bar//",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
|
||||||
"foo/bar/",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"Relative-4",
|
|
||||||
"www.youtube.com",
|
|
||||||
FlagsUsuallySafeGreedy,
|
|
||||||
"www.youtube.com",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
/*&testCase{
|
|
||||||
"UrlNorm-5",
|
|
||||||
"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3",
|
|
||||||
FlagsSafe | FlagRemoveDotSegments,
|
|
||||||
"http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&testCase{
|
|
||||||
"UrlNorm-1",
|
|
||||||
"http://test.example/?a=%e3%82%82%26",
|
|
||||||
FlagsAllGreedy,
|
|
||||||
"http://test.example/?a=\xe3\x82\x82%26",
|
|
||||||
false,
|
|
||||||
},*/
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRunner(t *testing.T) {
|
|
||||||
for _, tc := range cases {
|
|
||||||
runCase(tc, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCase(tc *testCase, t *testing.T) {
|
|
||||||
t.Logf("running %s...", tc.nm)
|
|
||||||
if tc.parsed {
|
|
||||||
u, e := url.Parse(tc.src)
|
|
||||||
if e != nil {
|
|
||||||
t.Errorf("%s - FAIL : %s", tc.nm, e)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
NormalizeURL(u, tc.flgs)
|
|
||||||
if s := u.String(); s != tc.res {
|
|
||||||
t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if s, e := NormalizeURLString(tc.src, tc.flgs); e != nil {
|
|
||||||
t.Errorf("%s - FAIL : %s", tc.nm, e)
|
|
||||||
} else if s != tc.res {
|
|
||||||
t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeUnnecessaryEscapesAll(t *testing.T) {
|
|
||||||
var url = "http://host/"
|
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
url += fmt.Sprintf("%%%02x", i)
|
|
||||||
}
|
|
||||||
if s, e := NormalizeURLString(url, FlagDecodeUnnecessaryEscapes); e != nil {
|
|
||||||
t.Fatalf("Got error %s", e.Error())
|
|
||||||
} else {
|
|
||||||
const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22%23$%25&'()*+,-./0123456789:;%3C=%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("DecodeUnnecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeNecessaryEscapesAll(t *testing.T) {
|
|
||||||
var url = "http://host/"
|
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
if i != 0x25 {
|
|
||||||
url += string(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if s, e := NormalizeURLString(url, FlagEncodeNecessaryEscapes); e != nil {
|
|
||||||
t.Fatalf("Got error %s", e.Error())
|
|
||||||
} else {
|
|
||||||
const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22#$&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%C2%80%C2%81%C2%82%C2%83%C2%84%C2%85%C2%86%C2%87%C2%88%C2%89%C2%8A%C2%8B%C2%8C%C2%8D%C2%8E%C2%8F%C2%90%C2%91%C2%92%C2%93%C2%94%C2%95%C2%96%C2%97%C2%98%C2%99%C2%9A%C2%9B%C2%9C%C2%9D%C2%9E%C2%9F%C2%A0%C2%A1%C2%A2%C2%A3%C2%A4%C2%A5%C2%A6%C2%A7%C2%A8%C2%A9%C2%AA%C2%AB%C2%AC%C2%AD%C2%AE%C2%AF%C2%B0%C2%B1%C2%B2%C2%B3%C2%B4%C2%B5%C2%B6%C2%B7%C2%B8%C2%B9%C2%BA%C2%BB%C2%BC%C2%BD%C2%BE%C2%BF%C3%80%C3%81%C3%82%C3%83%C3%84%C3%85%C3%86%C3%87%C3%88%C3%89%C3%8A%C3%8B%C3%8C%C3%8D%C3%8E%C3%8F%C3%90%C3%91%C3%92%C3%93%C3%94%C3%95%C3%96%C3%97%C3%98%C3%99%C3%9A%C3%9B%C3%9C%C3%9D%C3%9E%C3%9F%C3%A0%C3%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7%C3%A8%C3%A9%C3%AA%C3%AB%C3%AC%C3%AD%C3%AE%C3%AF%C3%B0%C3%B1%C3%B2%C3%B3%C3%B4%C3%B5%C3%B6%C3%B7%C3%B8%C3%B9%C3%BA%C3%BB%C3%BC%C3%BD%C3%BE%C3%BF"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("EncodeNecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
53
vendor/github.com/PuerkitoBio/purell/urlnorm_test.go
generated
vendored
53
vendor/github.com/PuerkitoBio/purell/urlnorm_test.go
generated
vendored
@@ -1,53 +0,0 @@
|
|||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test cases merged from PR #1
|
|
||||||
// Originally from https://github.com/jehiah/urlnorm/blob/master/test_urlnorm.py
|
|
||||||
|
|
||||||
func assertMap(t *testing.T, cases map[string]string, f NormalizationFlags) {
|
|
||||||
for bad, good := range cases {
|
|
||||||
s, e := NormalizeURLString(bad, f)
|
|
||||||
if e != nil {
|
|
||||||
t.Errorf("%s normalizing %v to %v", e.Error(), bad, good)
|
|
||||||
} else {
|
|
||||||
if s != good {
|
|
||||||
t.Errorf("source: %v expected: %v got: %v", bad, good, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This tests normalization to a unicode representation
|
|
||||||
// precent escapes for unreserved values are unescaped to their unicode value
|
|
||||||
// tests normalization to idna domains
|
|
||||||
// test ip word handling, ipv6 address handling, and trailing domain periods
|
|
||||||
// in general, this matches google chromes unescaping for things in the address bar.
|
|
||||||
// spaces are converted to '+' (perhaphs controversial)
|
|
||||||
// http://code.google.com/p/google-url/ probably is another good reference for this approach
|
|
||||||
func TestUrlnorm(t *testing.T) {
|
|
||||||
testcases := map[string]string{
|
|
||||||
"http://test.example/?a=%e3%82%82%26": "http://test.example/?a=%e3%82%82%26",
|
|
||||||
//"http://test.example/?a=%e3%82%82%26": "http://test.example/?a=\xe3\x82\x82%26", //should return a unicode character
|
|
||||||
"http://s.xn--q-bga.DE/": "http://s.xn--q-bga.de/", //should be in idna format
|
|
||||||
"http://XBLA\u306eXbox.com": "http://xn--xblaxbox-jf4g.com", //test utf8 and unicode
|
|
||||||
"http://президент.рф": "http://xn--d1abbgf6aiiy.xn--p1ai",
|
|
||||||
"http://ПРЕЗИДЕНТ.РФ": "http://xn--d1abbgf6aiiy.xn--p1ai",
|
|
||||||
"http://ab¥ヲ₩○.com": "http://xn--ab-ida8983azmfnvs.com", //test width folding
|
|
||||||
"http://\u00e9.com": "http://xn--9ca.com",
|
|
||||||
"http://e\u0301.com": "http://xn--9ca.com",
|
|
||||||
"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3": "http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3",
|
|
||||||
//"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3": "http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3",
|
|
||||||
|
|
||||||
"http://test.example/\xe3\x82\xad": "http://test.example/%E3%82%AD",
|
|
||||||
//"http://test.example/\xe3\x82\xad": "http://test.example/\xe3\x82\xad",
|
|
||||||
"http://test.example/?p=%23val#test-%23-val%25": "http://test.example/?p=%23val#test-%23-val%25", //check that %23 (#) is not escaped where it shouldn't be
|
|
||||||
|
|
||||||
"http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n": "http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n",
|
|
||||||
//"http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n": "http://test.domain/I\xc3\xb1t\xc3\xabrn\xc3\xa2ti\xc3\xb4n\xef\xbf\xbdliz\xc3\xa6ti\xc3\xb8n",
|
|
||||||
}
|
|
||||||
|
|
||||||
assertMap(t, testcases, FlagsSafe|FlagRemoveDotSegments)
|
|
||||||
}
|
|
||||||
15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml
generated
vendored
15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml
generated
vendored
@@ -1,15 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4.x
|
|
||||||
- 1.5.x
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go build .
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v
|
|
||||||
27
vendor/github.com/PuerkitoBio/urlesc/LICENSE
generated
vendored
27
vendor/github.com/PuerkitoBio/urlesc/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
16
vendor/github.com/PuerkitoBio/urlesc/README.md
generated
vendored
16
vendor/github.com/PuerkitoBio/urlesc/README.md
generated
vendored
@@ -1,16 +0,0 @@
|
|||||||
urlesc [](https://travis-ci.org/PuerkitoBio/urlesc) [](http://godoc.org/github.com/PuerkitoBio/urlesc)
|
|
||||||
======
|
|
||||||
|
|
||||||
Package urlesc implements query escaping as per RFC 3986.
|
|
||||||
|
|
||||||
It contains some parts of the net/url package, modified so as to allow
|
|
||||||
some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)).
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
go get github.com/PuerkitoBio/urlesc
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Go license (BSD-3-Clause)
|
|
||||||
|
|
||||||
180
vendor/github.com/PuerkitoBio/urlesc/urlesc.go
generated
vendored
180
vendor/github.com/PuerkitoBio/urlesc/urlesc.go
generated
vendored
@@ -1,180 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package urlesc implements query escaping as per RFC 3986.
|
|
||||||
// It contains some parts of the net/url package, modified so as to allow
|
|
||||||
// some reserved characters incorrectly escaped by net/url.
|
|
||||||
// See https://github.com/golang/go/issues/5684
|
|
||||||
package urlesc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encoding int
|
|
||||||
|
|
||||||
const (
|
|
||||||
encodePath encoding = 1 + iota
|
|
||||||
encodeUserPassword
|
|
||||||
encodeQueryComponent
|
|
||||||
encodeFragment
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return true if the specified character should be escaped when
|
|
||||||
// appearing in a URL string, according to RFC 3986.
|
|
||||||
func shouldEscape(c byte, mode encoding) bool {
|
|
||||||
// §2.3 Unreserved characters (alphanum)
|
|
||||||
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c {
|
|
||||||
case '-', '.', '_', '~': // §2.3 Unreserved characters (mark)
|
|
||||||
return false
|
|
||||||
|
|
||||||
// §2.2 Reserved characters (reserved)
|
|
||||||
case ':', '/', '?', '#', '[', ']', '@', // gen-delims
|
|
||||||
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims
|
|
||||||
// Different sections of the URL allow a few of
|
|
||||||
// the reserved characters to appear unescaped.
|
|
||||||
switch mode {
|
|
||||||
case encodePath: // §3.3
|
|
||||||
// The RFC allows sub-delims and : @.
|
|
||||||
// '/', '[' and ']' can be used to assign meaning to individual path
|
|
||||||
// segments. This package only manipulates the path as a whole,
|
|
||||||
// so we allow those as well. That leaves only ? and # to escape.
|
|
||||||
return c == '?' || c == '#'
|
|
||||||
|
|
||||||
case encodeUserPassword: // §3.2.1
|
|
||||||
// The RFC allows : and sub-delims in
|
|
||||||
// userinfo. The parsing of userinfo treats ':' as special so we must escape
|
|
||||||
// all the gen-delims.
|
|
||||||
return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'
|
|
||||||
|
|
||||||
case encodeQueryComponent: // §3.4
|
|
||||||
// The RFC allows / and ?.
|
|
||||||
return c != '/' && c != '?'
|
|
||||||
|
|
||||||
case encodeFragment: // §4.1
|
|
||||||
// The RFC text is silent but the grammar allows
|
|
||||||
// everything, so escape nothing but #
|
|
||||||
return c == '#'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything else must be escaped.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryEscape escapes the string so it can be safely placed
|
|
||||||
// inside a URL query.
|
|
||||||
func QueryEscape(s string) string {
|
|
||||||
return escape(s, encodeQueryComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func escape(s string, mode encoding) string {
|
|
||||||
spaceCount, hexCount := 0, 0
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
c := s[i]
|
|
||||||
if shouldEscape(c, mode) {
|
|
||||||
if c == ' ' && mode == encodeQueryComponent {
|
|
||||||
spaceCount++
|
|
||||||
} else {
|
|
||||||
hexCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if spaceCount == 0 && hexCount == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
t := make([]byte, len(s)+2*hexCount)
|
|
||||||
j := 0
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
switch c := s[i]; {
|
|
||||||
case c == ' ' && mode == encodeQueryComponent:
|
|
||||||
t[j] = '+'
|
|
||||||
j++
|
|
||||||
case shouldEscape(c, mode):
|
|
||||||
t[j] = '%'
|
|
||||||
t[j+1] = "0123456789ABCDEF"[c>>4]
|
|
||||||
t[j+2] = "0123456789ABCDEF"[c&15]
|
|
||||||
j += 3
|
|
||||||
default:
|
|
||||||
t[j] = s[i]
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
var uiReplacer = strings.NewReplacer(
|
|
||||||
"%21", "!",
|
|
||||||
"%27", "'",
|
|
||||||
"%28", "(",
|
|
||||||
"%29", ")",
|
|
||||||
"%2A", "*",
|
|
||||||
)
|
|
||||||
|
|
||||||
// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986.
|
|
||||||
func unescapeUserinfo(s string) string {
|
|
||||||
return uiReplacer.Replace(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape reassembles the URL into a valid URL string.
|
|
||||||
// The general form of the result is one of:
|
|
||||||
//
|
|
||||||
// scheme:opaque
|
|
||||||
// scheme://userinfo@host/path?query#fragment
|
|
||||||
//
|
|
||||||
// If u.Opaque is non-empty, String uses the first form;
|
|
||||||
// otherwise it uses the second form.
|
|
||||||
//
|
|
||||||
// In the second form, the following rules apply:
|
|
||||||
// - if u.Scheme is empty, scheme: is omitted.
|
|
||||||
// - if u.User is nil, userinfo@ is omitted.
|
|
||||||
// - if u.Host is empty, host/ is omitted.
|
|
||||||
// - if u.Scheme and u.Host are empty and u.User is nil,
|
|
||||||
// the entire scheme://userinfo@host/ is omitted.
|
|
||||||
// - if u.Host is non-empty and u.Path begins with a /,
|
|
||||||
// the form host/path does not add its own /.
|
|
||||||
// - if u.RawQuery is empty, ?query is omitted.
|
|
||||||
// - if u.Fragment is empty, #fragment is omitted.
|
|
||||||
func Escape(u *url.URL) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if u.Scheme != "" {
|
|
||||||
buf.WriteString(u.Scheme)
|
|
||||||
buf.WriteByte(':')
|
|
||||||
}
|
|
||||||
if u.Opaque != "" {
|
|
||||||
buf.WriteString(u.Opaque)
|
|
||||||
} else {
|
|
||||||
if u.Scheme != "" || u.Host != "" || u.User != nil {
|
|
||||||
buf.WriteString("//")
|
|
||||||
if ui := u.User; ui != nil {
|
|
||||||
buf.WriteString(unescapeUserinfo(ui.String()))
|
|
||||||
buf.WriteByte('@')
|
|
||||||
}
|
|
||||||
if h := u.Host; h != "" {
|
|
||||||
buf.WriteString(h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
|
|
||||||
buf.WriteByte('/')
|
|
||||||
}
|
|
||||||
buf.WriteString(escape(u.Path, encodePath))
|
|
||||||
}
|
|
||||||
if u.RawQuery != "" {
|
|
||||||
buf.WriteByte('?')
|
|
||||||
buf.WriteString(u.RawQuery)
|
|
||||||
}
|
|
||||||
if u.Fragment != "" {
|
|
||||||
buf.WriteByte('#')
|
|
||||||
buf.WriteString(escape(u.Fragment, encodeFragment))
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
641
vendor/github.com/PuerkitoBio/urlesc/urlesc_test.go
generated
vendored
641
vendor/github.com/PuerkitoBio/urlesc/urlesc_test.go
generated
vendored
@@ -1,641 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package urlesc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type URLTest struct {
|
|
||||||
in string
|
|
||||||
out *url.URL
|
|
||||||
roundtrip string // expected result of reserializing the URL; empty means same as "in".
|
|
||||||
}
|
|
||||||
|
|
||||||
var urltests = []URLTest{
|
|
||||||
// no path
|
|
||||||
{
|
|
||||||
"http://www.google.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// path
|
|
||||||
{
|
|
||||||
"http://www.google.com/",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// path with hex escaping
|
|
||||||
{
|
|
||||||
"http://www.google.com/file%20one%26two",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/file one&two",
|
|
||||||
},
|
|
||||||
"http://www.google.com/file%20one&two",
|
|
||||||
},
|
|
||||||
// user
|
|
||||||
{
|
|
||||||
"ftp://webmaster@www.google.com/",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "ftp",
|
|
||||||
User: url.User("webmaster"),
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// escape sequence in username
|
|
||||||
{
|
|
||||||
"ftp://john%20doe@www.google.com/",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "ftp",
|
|
||||||
User: url.User("john doe"),
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
},
|
|
||||||
"ftp://john%20doe@www.google.com/",
|
|
||||||
},
|
|
||||||
// query
|
|
||||||
{
|
|
||||||
"http://www.google.com/?q=go+language",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
RawQuery: "q=go+language",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// query with hex escaping: NOT parsed
|
|
||||||
{
|
|
||||||
"http://www.google.com/?q=go%20language",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
RawQuery: "q=go%20language",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// %20 outside query
|
|
||||||
{
|
|
||||||
"http://www.google.com/a%20b?q=c+d",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/a b",
|
|
||||||
RawQuery: "q=c+d",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// path without leading /, so no parsing
|
|
||||||
{
|
|
||||||
"http:www.google.com/?q=go+language",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Opaque: "www.google.com/",
|
|
||||||
RawQuery: "q=go+language",
|
|
||||||
},
|
|
||||||
"http:www.google.com/?q=go+language",
|
|
||||||
},
|
|
||||||
// path without leading /, so no parsing
|
|
||||||
{
|
|
||||||
"http:%2f%2fwww.google.com/?q=go+language",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Opaque: "%2f%2fwww.google.com/",
|
|
||||||
RawQuery: "q=go+language",
|
|
||||||
},
|
|
||||||
"http:%2f%2fwww.google.com/?q=go+language",
|
|
||||||
},
|
|
||||||
// non-authority with path
|
|
||||||
{
|
|
||||||
"mailto:/webmaster@golang.org",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "mailto",
|
|
||||||
Path: "/webmaster@golang.org",
|
|
||||||
},
|
|
||||||
"mailto:///webmaster@golang.org", // unfortunate compromise
|
|
||||||
},
|
|
||||||
// non-authority
|
|
||||||
{
|
|
||||||
"mailto:webmaster@golang.org",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "mailto",
|
|
||||||
Opaque: "webmaster@golang.org",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// unescaped :// in query should not create a scheme
|
|
||||||
{
|
|
||||||
"/foo?query=http://bad",
|
|
||||||
&url.URL{
|
|
||||||
Path: "/foo",
|
|
||||||
RawQuery: "query=http://bad",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// leading // without scheme should create an authority
|
|
||||||
{
|
|
||||||
"//foo",
|
|
||||||
&url.URL{
|
|
||||||
Host: "foo",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// leading // without scheme, with userinfo, path, and query
|
|
||||||
{
|
|
||||||
"//user@foo/path?a=b",
|
|
||||||
&url.URL{
|
|
||||||
User: url.User("user"),
|
|
||||||
Host: "foo",
|
|
||||||
Path: "/path",
|
|
||||||
RawQuery: "a=b",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// Three leading slashes isn't an authority, but doesn't return an error.
|
|
||||||
// (We can't return an error, as this code is also used via
|
|
||||||
// ServeHTTP -> ReadRequest -> Parse, which is arguably a
|
|
||||||
// different URL parsing context, but currently shares the
|
|
||||||
// same codepath)
|
|
||||||
{
|
|
||||||
"///threeslashes",
|
|
||||||
&url.URL{
|
|
||||||
Path: "///threeslashes",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"http://user:password@google.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword("user", "password"),
|
|
||||||
Host: "google.com",
|
|
||||||
},
|
|
||||||
"http://user:password@google.com",
|
|
||||||
},
|
|
||||||
// unescaped @ in username should not confuse host
|
|
||||||
{
|
|
||||||
"http://j@ne:password@google.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword("j@ne", "password"),
|
|
||||||
Host: "google.com",
|
|
||||||
},
|
|
||||||
"http://j%40ne:password@google.com",
|
|
||||||
},
|
|
||||||
// unescaped @ in password should not confuse host
|
|
||||||
{
|
|
||||||
"http://jane:p@ssword@google.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword("jane", "p@ssword"),
|
|
||||||
Host: "google.com",
|
|
||||||
},
|
|
||||||
"http://jane:p%40ssword@google.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"http://j@ne:password@google.com/p@th?q=@go",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword("j@ne", "password"),
|
|
||||||
Host: "google.com",
|
|
||||||
Path: "/p@th",
|
|
||||||
RawQuery: "q=@go",
|
|
||||||
},
|
|
||||||
"http://j%40ne:password@google.com/p@th?q=@go",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"http://www.google.com/?q=go+language#foo",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
RawQuery: "q=go+language",
|
|
||||||
Fragment: "foo",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"http://www.google.com/?q=go+language#foo%26bar",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "/",
|
|
||||||
RawQuery: "q=go+language",
|
|
||||||
Fragment: "foo&bar",
|
|
||||||
},
|
|
||||||
"http://www.google.com/?q=go+language#foo&bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file:///home/adg/rabbits",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "file",
|
|
||||||
Host: "",
|
|
||||||
Path: "/home/adg/rabbits",
|
|
||||||
},
|
|
||||||
"file:///home/adg/rabbits",
|
|
||||||
},
|
|
||||||
// "Windows" paths are no exception to the rule.
|
|
||||||
// See golang.org/issue/6027, especially comment #9.
|
|
||||||
{
|
|
||||||
"file:///C:/FooBar/Baz.txt",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "file",
|
|
||||||
Host: "",
|
|
||||||
Path: "/C:/FooBar/Baz.txt",
|
|
||||||
},
|
|
||||||
"file:///C:/FooBar/Baz.txt",
|
|
||||||
},
|
|
||||||
// case-insensitive scheme
|
|
||||||
{
|
|
||||||
"MaIlTo:webmaster@golang.org",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "mailto",
|
|
||||||
Opaque: "webmaster@golang.org",
|
|
||||||
},
|
|
||||||
"mailto:webmaster@golang.org",
|
|
||||||
},
|
|
||||||
// Relative path
|
|
||||||
{
|
|
||||||
"a/b/c",
|
|
||||||
&url.URL{
|
|
||||||
Path: "a/b/c",
|
|
||||||
},
|
|
||||||
"a/b/c",
|
|
||||||
},
|
|
||||||
// escaped '?' in username and password
|
|
||||||
{
|
|
||||||
"http://%3Fam:pa%3Fsword@google.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword("?am", "pa?sword"),
|
|
||||||
Host: "google.com",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// escaped '?' and '#' in path
|
|
||||||
{
|
|
||||||
"http://example.com/%3F%23",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "?#",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// unescaped [ ] ! ' ( ) * in path
|
|
||||||
{
|
|
||||||
"http://example.com/[]!'()*",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "[]!'()*",
|
|
||||||
},
|
|
||||||
"http://example.com/[]!'()*",
|
|
||||||
},
|
|
||||||
// escaped : / ? # [ ] @ in username and password
|
|
||||||
{
|
|
||||||
"http://%3A%2F%3F:%23%5B%5D%40@example.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword(":/?", "#[]@"),
|
|
||||||
Host: "example.com",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// unescaped ! $ & ' ( ) * + , ; = in username and password
|
|
||||||
{
|
|
||||||
"http://!$&'():*+,;=@example.com",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
User: url.UserPassword("!$&'()", "*+,;="),
|
|
||||||
Host: "example.com",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// unescaped = : / . ? = in query component
|
|
||||||
{
|
|
||||||
"http://example.com/?q=http://google.com/?q=",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "/",
|
|
||||||
RawQuery: "q=http://google.com/?q=",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
// unescaped : / ? [ ] @ ! $ & ' ( ) * + , ; = in fragment
|
|
||||||
{
|
|
||||||
"http://example.com/#:/?%23[]@!$&'()*+,;=",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "/",
|
|
||||||
Fragment: ":/?#[]@!$&'()*+,;=",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func DoTestString(t *testing.T, parse func(string) (*url.URL, error), name string, tests []URLTest) {
|
|
||||||
for _, tt := range tests {
|
|
||||||
u, err := parse(tt.in)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s(%q) returned error %s", name, tt.in, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
expected := tt.in
|
|
||||||
if len(tt.roundtrip) > 0 {
|
|
||||||
expected = tt.roundtrip
|
|
||||||
}
|
|
||||||
s := Escape(u)
|
|
||||||
if s != expected {
|
|
||||||
t.Errorf("Escape(%s(%q)) == %q (expected %q)", name, tt.in, s, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestURLString(t *testing.T) {
|
|
||||||
DoTestString(t, url.Parse, "Parse", urltests)
|
|
||||||
|
|
||||||
// no leading slash on path should prepend
|
|
||||||
// slash on String() call
|
|
||||||
noslash := URLTest{
|
|
||||||
"http://www.google.com/search",
|
|
||||||
&url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "www.google.com",
|
|
||||||
Path: "search",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
s := Escape(noslash.out)
|
|
||||||
if s != noslash.in {
|
|
||||||
t.Errorf("Expected %s; go %s", noslash.in, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type EscapeTest struct {
|
|
||||||
in string
|
|
||||||
out string
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
var escapeTests = []EscapeTest{
|
|
||||||
{
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"abc",
|
|
||||||
"abc",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"one two",
|
|
||||||
"one+two",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"10%",
|
|
||||||
"10%25",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
" ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
|
|
||||||
"+?%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A/%40%24%27%28%29%2A%2C%3B",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEscape(t *testing.T) {
|
|
||||||
for _, tt := range escapeTests {
|
|
||||||
actual := QueryEscape(tt.in)
|
|
||||||
if tt.out != actual {
|
|
||||||
t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// for bonus points, verify that escape:unescape is an identity.
|
|
||||||
roundtrip, err := url.QueryUnescape(actual)
|
|
||||||
if roundtrip != tt.in || err != nil {
|
|
||||||
t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var resolveReferenceTests = []struct {
|
|
||||||
base, rel, expected string
|
|
||||||
}{
|
|
||||||
// Absolute URL references
|
|
||||||
{"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
|
|
||||||
{"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
|
|
||||||
{"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
|
|
||||||
|
|
||||||
// Path-absolute references
|
|
||||||
{"http://foo.com/bar", "/baz", "http://foo.com/baz"},
|
|
||||||
{"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
|
|
||||||
{"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
|
|
||||||
|
|
||||||
// Scheme-relative
|
|
||||||
{"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"},
|
|
||||||
|
|
||||||
// Path-relative references:
|
|
||||||
|
|
||||||
// ... current directory
|
|
||||||
{"http://foo.com", ".", "http://foo.com/"},
|
|
||||||
{"http://foo.com/bar", ".", "http://foo.com/"},
|
|
||||||
{"http://foo.com/bar/", ".", "http://foo.com/bar/"},
|
|
||||||
|
|
||||||
// ... going down
|
|
||||||
{"http://foo.com", "bar", "http://foo.com/bar"},
|
|
||||||
{"http://foo.com/", "bar", "http://foo.com/bar"},
|
|
||||||
{"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"},
|
|
||||||
|
|
||||||
// ... going up
|
|
||||||
{"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"},
|
|
||||||
{"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
|
|
||||||
{"http://foo.com/bar", "..", "http://foo.com/"},
|
|
||||||
{"http://foo.com/bar/baz", "./..", "http://foo.com/"},
|
|
||||||
// ".." in the middle (issue 3560)
|
|
||||||
{"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
|
|
||||||
{"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot/"},
|
|
||||||
|
|
||||||
// Remove any dot-segments prior to forming the target URI.
|
|
||||||
// http://tools.ietf.org/html/rfc3986#section-5.2.4
|
|
||||||
{"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/baz"},
|
|
||||||
|
|
||||||
// Triple dot isn't special
|
|
||||||
{"http://foo.com/bar", "...", "http://foo.com/..."},
|
|
||||||
|
|
||||||
// Fragment
|
|
||||||
{"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
|
|
||||||
|
|
||||||
// RFC 3986: Normal Examples
|
|
||||||
// http://tools.ietf.org/html/rfc3986#section-5.4.1
|
|
||||||
{"http://a/b/c/d;p?q", "g:h", "g:h"},
|
|
||||||
{"http://a/b/c/d;p?q", "g", "http://a/b/c/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "g/", "http://a/b/c/g/"},
|
|
||||||
{"http://a/b/c/d;p?q", "/g", "http://a/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "//g", "http://g"},
|
|
||||||
{"http://a/b/c/d;p?q", "?y", "http://a/b/c/d;p?y"},
|
|
||||||
{"http://a/b/c/d;p?q", "g?y", "http://a/b/c/g?y"},
|
|
||||||
{"http://a/b/c/d;p?q", "#s", "http://a/b/c/d;p?q#s"},
|
|
||||||
{"http://a/b/c/d;p?q", "g#s", "http://a/b/c/g#s"},
|
|
||||||
{"http://a/b/c/d;p?q", "g?y#s", "http://a/b/c/g?y#s"},
|
|
||||||
{"http://a/b/c/d;p?q", ";x", "http://a/b/c/;x"},
|
|
||||||
{"http://a/b/c/d;p?q", "g;x", "http://a/b/c/g;x"},
|
|
||||||
{"http://a/b/c/d;p?q", "g;x?y#s", "http://a/b/c/g;x?y#s"},
|
|
||||||
{"http://a/b/c/d;p?q", "", "http://a/b/c/d;p?q"},
|
|
||||||
{"http://a/b/c/d;p?q", ".", "http://a/b/c/"},
|
|
||||||
{"http://a/b/c/d;p?q", "./", "http://a/b/c/"},
|
|
||||||
{"http://a/b/c/d;p?q", "..", "http://a/b/"},
|
|
||||||
{"http://a/b/c/d;p?q", "../", "http://a/b/"},
|
|
||||||
{"http://a/b/c/d;p?q", "../g", "http://a/b/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "../..", "http://a/"},
|
|
||||||
{"http://a/b/c/d;p?q", "../../", "http://a/"},
|
|
||||||
{"http://a/b/c/d;p?q", "../../g", "http://a/g"},
|
|
||||||
|
|
||||||
// RFC 3986: Abnormal Examples
|
|
||||||
// http://tools.ietf.org/html/rfc3986#section-5.4.2
|
|
||||||
{"http://a/b/c/d;p?q", "../../../g", "http://a/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "../../../../g", "http://a/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "/./g", "http://a/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "/../g", "http://a/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "g.", "http://a/b/c/g."},
|
|
||||||
{"http://a/b/c/d;p?q", ".g", "http://a/b/c/.g"},
|
|
||||||
{"http://a/b/c/d;p?q", "g..", "http://a/b/c/g.."},
|
|
||||||
{"http://a/b/c/d;p?q", "..g", "http://a/b/c/..g"},
|
|
||||||
{"http://a/b/c/d;p?q", "./../g", "http://a/b/g"},
|
|
||||||
{"http://a/b/c/d;p?q", "./g/.", "http://a/b/c/g/"},
|
|
||||||
{"http://a/b/c/d;p?q", "g/./h", "http://a/b/c/g/h"},
|
|
||||||
{"http://a/b/c/d;p?q", "g/../h", "http://a/b/c/h"},
|
|
||||||
{"http://a/b/c/d;p?q", "g;x=1/./y", "http://a/b/c/g;x=1/y"},
|
|
||||||
{"http://a/b/c/d;p?q", "g;x=1/../y", "http://a/b/c/y"},
|
|
||||||
{"http://a/b/c/d;p?q", "g?y/./x", "http://a/b/c/g?y/./x"},
|
|
||||||
{"http://a/b/c/d;p?q", "g?y/../x", "http://a/b/c/g?y/../x"},
|
|
||||||
{"http://a/b/c/d;p?q", "g#s/./x", "http://a/b/c/g#s/./x"},
|
|
||||||
{"http://a/b/c/d;p?q", "g#s/../x", "http://a/b/c/g#s/../x"},
|
|
||||||
|
|
||||||
// Extras.
|
|
||||||
{"https://a/b/c/d;p?q", "//g?q", "https://g?q"},
|
|
||||||
{"https://a/b/c/d;p?q", "//g#s", "https://g#s"},
|
|
||||||
{"https://a/b/c/d;p?q", "//g/d/e/f?y#s", "https://g/d/e/f?y#s"},
|
|
||||||
{"https://a/b/c/d;p#s", "?y", "https://a/b/c/d;p?y"},
|
|
||||||
{"https://a/b/c/d;p?q#s", "?y", "https://a/b/c/d;p?y"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolveReference(t *testing.T) {
|
|
||||||
mustParse := func(url_ string) *url.URL {
|
|
||||||
u, err := url.Parse(url_)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected URL to parse: %q, got error: %v", url_, err)
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
opaque := &url.URL{Scheme: "scheme", Opaque: "opaque"}
|
|
||||||
for _, test := range resolveReferenceTests {
|
|
||||||
base := mustParse(test.base)
|
|
||||||
rel := mustParse(test.rel)
|
|
||||||
url := base.ResolveReference(rel)
|
|
||||||
if Escape(url) != test.expected {
|
|
||||||
t.Errorf("URL(%q).ResolveReference(%q) == %q, got %q", test.base, test.rel, test.expected, Escape(url))
|
|
||||||
}
|
|
||||||
// Ensure that new instances are returned.
|
|
||||||
if base == url {
|
|
||||||
t.Errorf("Expected URL.ResolveReference to return new URL instance.")
|
|
||||||
}
|
|
||||||
// Test the convenience wrapper too.
|
|
||||||
url, err := base.Parse(test.rel)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err)
|
|
||||||
} else if Escape(url) != test.expected {
|
|
||||||
t.Errorf("URL(%q).Parse(%q) == %q, got %q", test.base, test.rel, test.expected, Escape(url))
|
|
||||||
} else if base == url {
|
|
||||||
// Ensure that new instances are returned for the wrapper too.
|
|
||||||
t.Errorf("Expected URL.Parse to return new URL instance.")
|
|
||||||
}
|
|
||||||
// Ensure Opaque resets the URL.
|
|
||||||
url = base.ResolveReference(opaque)
|
|
||||||
if *url != *opaque {
|
|
||||||
t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", url, opaque)
|
|
||||||
}
|
|
||||||
// Test the convenience wrapper with an opaque URL too.
|
|
||||||
url, err = base.Parse("scheme:opaque")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(`URL(%q).Parse("scheme:opaque") failed: %v`, test.base, err)
|
|
||||||
} else if *url != *opaque {
|
|
||||||
t.Errorf("Parse failed to resolve opaque URL: want %#v, got %#v", url, opaque)
|
|
||||||
} else if base == url {
|
|
||||||
// Ensure that new instances are returned, again.
|
|
||||||
t.Errorf("Expected URL.Parse to return new URL instance.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type shouldEscapeTest struct {
|
|
||||||
in byte
|
|
||||||
mode encoding
|
|
||||||
escape bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var shouldEscapeTests = []shouldEscapeTest{
|
|
||||||
// Unreserved characters (§2.3)
|
|
||||||
{'a', encodePath, false},
|
|
||||||
{'a', encodeUserPassword, false},
|
|
||||||
{'a', encodeQueryComponent, false},
|
|
||||||
{'a', encodeFragment, false},
|
|
||||||
{'z', encodePath, false},
|
|
||||||
{'A', encodePath, false},
|
|
||||||
{'Z', encodePath, false},
|
|
||||||
{'0', encodePath, false},
|
|
||||||
{'9', encodePath, false},
|
|
||||||
{'-', encodePath, false},
|
|
||||||
{'-', encodeUserPassword, false},
|
|
||||||
{'-', encodeQueryComponent, false},
|
|
||||||
{'-', encodeFragment, false},
|
|
||||||
{'.', encodePath, false},
|
|
||||||
{'_', encodePath, false},
|
|
||||||
{'~', encodePath, false},
|
|
||||||
|
|
||||||
// User information (§3.2.1)
|
|
||||||
{':', encodeUserPassword, true},
|
|
||||||
{'/', encodeUserPassword, true},
|
|
||||||
{'?', encodeUserPassword, true},
|
|
||||||
{'@', encodeUserPassword, true},
|
|
||||||
{'$', encodeUserPassword, false},
|
|
||||||
{'&', encodeUserPassword, false},
|
|
||||||
{'+', encodeUserPassword, false},
|
|
||||||
{',', encodeUserPassword, false},
|
|
||||||
{';', encodeUserPassword, false},
|
|
||||||
{'=', encodeUserPassword, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShouldEscape(t *testing.T) {
|
|
||||||
for _, tt := range shouldEscapeTests {
|
|
||||||
if shouldEscape(tt.in, tt.mode) != tt.escape {
|
|
||||||
t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
vendor/github.com/asaskevich/govalidator/.github/ISSUE_TEMPLATE.md
generated
vendored
2
vendor/github.com/asaskevich/govalidator/.github/ISSUE_TEMPLATE.md
generated
vendored
@@ -1,2 +0,0 @@
|
|||||||
<!-- Love govalidator? Please consider supporting our collective:
|
|
||||||
👉 https://opencollective.com/govalidator/donate -->
|
|
||||||
14
vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
14
vendor/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.1
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
- bwatas@gmail.com
|
|
||||||
63
vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md
generated
vendored
63
vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md
generated
vendored
@@ -1,63 +0,0 @@
|
|||||||
#### Support
|
|
||||||
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
|
|
||||||
|
|
||||||
#### What to contribute
|
|
||||||
If you don't know what to do, there are some features and functions that need to be done
|
|
||||||
|
|
||||||
- [ ] Refactor code
|
|
||||||
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
|
|
||||||
- [ ] Create actual list of contributors and projects that currently using this package
|
|
||||||
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
|
|
||||||
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
|
|
||||||
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
|
|
||||||
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
|
|
||||||
- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
|
|
||||||
- [ ] Implement fuzzing testing
|
|
||||||
- [ ] Implement some struct/map/array utilities
|
|
||||||
- [ ] Implement map/array validation
|
|
||||||
- [ ] Implement benchmarking
|
|
||||||
- [ ] Implement batch of examples
|
|
||||||
- [ ] Look at forks for new features and fixes
|
|
||||||
|
|
||||||
#### Advice
|
|
||||||
Feel free to create what you want, but keep in mind when you implement new features:
|
|
||||||
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
|
|
||||||
- Public functions must be documented and described in source file and added to README.md to the list of available functions
|
|
||||||
- There are must be unit-tests for any new functions and improvements
|
|
||||||
|
|
||||||
## Financial contributions
|
|
||||||
|
|
||||||
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/govalidator).
|
|
||||||
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
|
|
||||||
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
|
|
||||||
### Contributors
|
|
||||||
|
|
||||||
Thank you to all the people who have already contributed to govalidator!
|
|
||||||
<a href="graphs/contributors"><img src="https://opencollective.com/govalidator/contributors.svg?width=890" /></a>
|
|
||||||
|
|
||||||
|
|
||||||
### Backers
|
|
||||||
|
|
||||||
Thank you to all our backers! [[Become a backer](https://opencollective.com/govalidator#backer)]
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/govalidator#backers" target="_blank"><img src="https://opencollective.com/govalidator/backers.svg?width=890"></a>
|
|
||||||
|
|
||||||
|
|
||||||
### Sponsors
|
|
||||||
|
|
||||||
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/govalidator#sponsor))
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/0/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/1/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/2/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/3/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/4/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/5/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/6/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/7/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/8/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/9/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/9/avatar.svg"></a>
|
|
||||||
21
vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
21
vendor/github.com/asaskevich/govalidator/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Alex Saskevich
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
490
vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
490
vendor/github.com/asaskevich/govalidator/README.md
generated
vendored
@@ -1,490 +0,0 @@
|
|||||||
govalidator
|
|
||||||
===========
|
|
||||||
[](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://godoc.org/github.com/asaskevich/govalidator) [](https://coveralls.io/r/asaskevich/govalidator?branch=master) [](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
|
||||||
[](https://travis-ci.org/asaskevich/govalidator) [](https://goreportcard.com/report/github.com/asaskevich/govalidator) [](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) [](#backers) [](#sponsors)
|
|
||||||
|
|
||||||
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
|
||||||
|
|
||||||
#### Installation
|
|
||||||
Make sure that Go is installed on your computer.
|
|
||||||
Type the following command in your terminal:
|
|
||||||
|
|
||||||
go get github.com/asaskevich/govalidator
|
|
||||||
|
|
||||||
or you can get specified release of the package with `gopkg.in`:
|
|
||||||
|
|
||||||
go get gopkg.in/asaskevich/govalidator.v4
|
|
||||||
|
|
||||||
After it the package is ready to use.
|
|
||||||
|
|
||||||
|
|
||||||
#### Import package in your project
|
|
||||||
Add following line in your `*.go` file:
|
|
||||||
```go
|
|
||||||
import "github.com/asaskevich/govalidator"
|
|
||||||
```
|
|
||||||
If you are unhappy to use long `govalidator`, you can do something like this:
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
valid "github.com/asaskevich/govalidator"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Activate behavior to require all fields have a validation tag by default
|
|
||||||
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/asaskevich/govalidator"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
govalidator.SetFieldsRequiredByDefault(true)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here's some code to explain it:
|
|
||||||
```go
|
|
||||||
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
|
||||||
type exampleStruct struct {
|
|
||||||
Name string ``
|
|
||||||
Email string `valid:"email"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// this, however, will only fail when Email is empty or an invalid email address:
|
|
||||||
type exampleStruct2 struct {
|
|
||||||
Name string `valid:"-"`
|
|
||||||
Email string `valid:"email"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
|
||||||
type exampleStruct2 struct {
|
|
||||||
Name string `valid:"-"`
|
|
||||||
Email string `valid:"email,optional"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
|
|
||||||
##### Custom validator function signature
|
|
||||||
A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.
|
|
||||||
```go
|
|
||||||
import "github.com/asaskevich/govalidator"
|
|
||||||
|
|
||||||
// old signature
|
|
||||||
func(i interface{}) bool
|
|
||||||
|
|
||||||
// new signature
|
|
||||||
func(i interface{}, o interface{}) bool
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Adding a custom validator
|
|
||||||
This was changed to prevent data races when accessing custom validators.
|
|
||||||
```go
|
|
||||||
import "github.com/asaskevich/govalidator"
|
|
||||||
|
|
||||||
// before
|
|
||||||
govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
|
||||||
// ...
|
|
||||||
})
|
|
||||||
|
|
||||||
// after
|
|
||||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
|
||||||
// ...
|
|
||||||
}))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### List of functions:
|
|
||||||
```go
|
|
||||||
func Abs(value float64) float64
|
|
||||||
func BlackList(str, chars string) string
|
|
||||||
func ByteLength(str string, params ...string) bool
|
|
||||||
func CamelCaseToUnderscore(str string) string
|
|
||||||
func Contains(str, substring string) bool
|
|
||||||
func Count(array []interface{}, iterator ConditionIterator) int
|
|
||||||
func Each(array []interface{}, iterator Iterator)
|
|
||||||
func ErrorByField(e error, field string) string
|
|
||||||
func ErrorsByField(e error) map[string]string
|
|
||||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
|
|
||||||
func Find(array []interface{}, iterator ConditionIterator) interface{}
|
|
||||||
func GetLine(s string, index int) (string, error)
|
|
||||||
func GetLines(s string) []string
|
|
||||||
func InRange(value, left, right float64) bool
|
|
||||||
func IsASCII(str string) bool
|
|
||||||
func IsAlpha(str string) bool
|
|
||||||
func IsAlphanumeric(str string) bool
|
|
||||||
func IsBase64(str string) bool
|
|
||||||
func IsByteLength(str string, min, max int) bool
|
|
||||||
func IsCIDR(str string) bool
|
|
||||||
func IsCreditCard(str string) bool
|
|
||||||
func IsDNSName(str string) bool
|
|
||||||
func IsDataURI(str string) bool
|
|
||||||
func IsDialString(str string) bool
|
|
||||||
func IsDivisibleBy(str, num string) bool
|
|
||||||
func IsEmail(str string) bool
|
|
||||||
func IsFilePath(str string) (bool, int)
|
|
||||||
func IsFloat(str string) bool
|
|
||||||
func IsFullWidth(str string) bool
|
|
||||||
func IsHalfWidth(str string) bool
|
|
||||||
func IsHexadecimal(str string) bool
|
|
||||||
func IsHexcolor(str string) bool
|
|
||||||
func IsHost(str string) bool
|
|
||||||
func IsIP(str string) bool
|
|
||||||
func IsIPv4(str string) bool
|
|
||||||
func IsIPv6(str string) bool
|
|
||||||
func IsISBN(str string, version int) bool
|
|
||||||
func IsISBN10(str string) bool
|
|
||||||
func IsISBN13(str string) bool
|
|
||||||
func IsISO3166Alpha2(str string) bool
|
|
||||||
func IsISO3166Alpha3(str string) bool
|
|
||||||
func IsISO693Alpha2(str string) bool
|
|
||||||
func IsISO693Alpha3b(str string) bool
|
|
||||||
func IsISO4217(str string) bool
|
|
||||||
func IsIn(str string, params ...string) bool
|
|
||||||
func IsInt(str string) bool
|
|
||||||
func IsJSON(str string) bool
|
|
||||||
func IsLatitude(str string) bool
|
|
||||||
func IsLongitude(str string) bool
|
|
||||||
func IsLowerCase(str string) bool
|
|
||||||
func IsMAC(str string) bool
|
|
||||||
func IsMongoID(str string) bool
|
|
||||||
func IsMultibyte(str string) bool
|
|
||||||
func IsNatural(value float64) bool
|
|
||||||
func IsNegative(value float64) bool
|
|
||||||
func IsNonNegative(value float64) bool
|
|
||||||
func IsNonPositive(value float64) bool
|
|
||||||
func IsNull(str string) bool
|
|
||||||
func IsNumeric(str string) bool
|
|
||||||
func IsPort(str string) bool
|
|
||||||
func IsPositive(value float64) bool
|
|
||||||
func IsPrintableASCII(str string) bool
|
|
||||||
func IsRFC3339(str string) bool
|
|
||||||
func IsRFC3339WithoutZone(str string) bool
|
|
||||||
func IsRGBcolor(str string) bool
|
|
||||||
func IsRequestURI(rawurl string) bool
|
|
||||||
func IsRequestURL(rawurl string) bool
|
|
||||||
func IsSSN(str string) bool
|
|
||||||
func IsSemver(str string) bool
|
|
||||||
func IsTime(str string, format string) bool
|
|
||||||
func IsURL(str string) bool
|
|
||||||
func IsUTFDigit(str string) bool
|
|
||||||
func IsUTFLetter(str string) bool
|
|
||||||
func IsUTFLetterNumeric(str string) bool
|
|
||||||
func IsUTFNumeric(str string) bool
|
|
||||||
func IsUUID(str string) bool
|
|
||||||
func IsUUIDv3(str string) bool
|
|
||||||
func IsUUIDv4(str string) bool
|
|
||||||
func IsUUIDv5(str string) bool
|
|
||||||
func IsUpperCase(str string) bool
|
|
||||||
func IsVariableWidth(str string) bool
|
|
||||||
func IsWhole(value float64) bool
|
|
||||||
func LeftTrim(str, chars string) string
|
|
||||||
func Map(array []interface{}, iterator ResultIterator) []interface{}
|
|
||||||
func Matches(str, pattern string) bool
|
|
||||||
func NormalizeEmail(str string) (string, error)
|
|
||||||
func PadBoth(str string, padStr string, padLen int) string
|
|
||||||
func PadLeft(str string, padStr string, padLen int) string
|
|
||||||
func PadRight(str string, padStr string, padLen int) string
|
|
||||||
func Range(str string, params ...string) bool
|
|
||||||
func RemoveTags(s string) string
|
|
||||||
func ReplacePattern(str, pattern, replace string) string
|
|
||||||
func Reverse(s string) string
|
|
||||||
func RightTrim(str, chars string) string
|
|
||||||
func RuneLength(str string, params ...string) bool
|
|
||||||
func SafeFileName(str string) string
|
|
||||||
func SetFieldsRequiredByDefault(value bool)
|
|
||||||
func Sign(value float64) float64
|
|
||||||
func StringLength(str string, params ...string) bool
|
|
||||||
func StringMatches(s string, params ...string) bool
|
|
||||||
func StripLow(str string, keepNewLines bool) string
|
|
||||||
func ToBoolean(str string) (bool, error)
|
|
||||||
func ToFloat(str string) (float64, error)
|
|
||||||
func ToInt(str string) (int64, error)
|
|
||||||
func ToJSON(obj interface{}) (string, error)
|
|
||||||
func ToString(obj interface{}) string
|
|
||||||
func Trim(str, chars string) string
|
|
||||||
func Truncate(str string, length int, ending string) string
|
|
||||||
func UnderscoreToCamelCase(s string) string
|
|
||||||
func ValidateStruct(s interface{}) (bool, error)
|
|
||||||
func WhiteList(str, chars string) string
|
|
||||||
type ConditionIterator
|
|
||||||
type CustomTypeValidator
|
|
||||||
type Error
|
|
||||||
func (e Error) Error() string
|
|
||||||
type Errors
|
|
||||||
func (es Errors) Error() string
|
|
||||||
func (es Errors) Errors() []error
|
|
||||||
type ISO3166Entry
|
|
||||||
type Iterator
|
|
||||||
type ParamValidator
|
|
||||||
type ResultIterator
|
|
||||||
type UnsupportedTypeError
|
|
||||||
func (e *UnsupportedTypeError) Error() string
|
|
||||||
type Validator
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Examples
|
|
||||||
###### IsURL
|
|
||||||
```go
|
|
||||||
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
|
|
||||||
```
|
|
||||||
###### ToString
|
|
||||||
```go
|
|
||||||
type User struct {
|
|
||||||
FirstName string
|
|
||||||
LastName string
|
|
||||||
}
|
|
||||||
|
|
||||||
str := govalidator.ToString(&User{"John", "Juan"})
|
|
||||||
println(str)
|
|
||||||
```
|
|
||||||
###### Each, Map, Filter, Count for slices
|
|
||||||
Each iterates over the slice/array and calls Iterator for every item
|
|
||||||
```go
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5}
|
|
||||||
var fn govalidator.Iterator = func(value interface{}, index int) {
|
|
||||||
println(value.(int))
|
|
||||||
}
|
|
||||||
govalidator.Each(data, fn)
|
|
||||||
```
|
|
||||||
```go
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5}
|
|
||||||
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
|
|
||||||
return value.(int) * 3
|
|
||||||
}
|
|
||||||
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
|
||||||
```
|
|
||||||
```go
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
||||||
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
return value.(int)%2 == 0
|
|
||||||
}
|
|
||||||
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
|
||||||
_ = govalidator.Count(data, fn) // result = 5
|
|
||||||
```
|
|
||||||
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
|
|
||||||
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
|
|
||||||
```go
|
|
||||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
|
||||||
return str == "duck"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
For completely custom validators (interface-based), see below.
|
|
||||||
|
|
||||||
Here is a list of available validators for struct fields (validator - used function):
|
|
||||||
```go
|
|
||||||
"email": IsEmail,
|
|
||||||
"url": IsURL,
|
|
||||||
"dialstring": IsDialString,
|
|
||||||
"requrl": IsRequestURL,
|
|
||||||
"requri": IsRequestURI,
|
|
||||||
"alpha": IsAlpha,
|
|
||||||
"utfletter": IsUTFLetter,
|
|
||||||
"alphanum": IsAlphanumeric,
|
|
||||||
"utfletternum": IsUTFLetterNumeric,
|
|
||||||
"numeric": IsNumeric,
|
|
||||||
"utfnumeric": IsUTFNumeric,
|
|
||||||
"utfdigit": IsUTFDigit,
|
|
||||||
"hexadecimal": IsHexadecimal,
|
|
||||||
"hexcolor": IsHexcolor,
|
|
||||||
"rgbcolor": IsRGBcolor,
|
|
||||||
"lowercase": IsLowerCase,
|
|
||||||
"uppercase": IsUpperCase,
|
|
||||||
"int": IsInt,
|
|
||||||
"float": IsFloat,
|
|
||||||
"null": IsNull,
|
|
||||||
"uuid": IsUUID,
|
|
||||||
"uuidv3": IsUUIDv3,
|
|
||||||
"uuidv4": IsUUIDv4,
|
|
||||||
"uuidv5": IsUUIDv5,
|
|
||||||
"creditcard": IsCreditCard,
|
|
||||||
"isbn10": IsISBN10,
|
|
||||||
"isbn13": IsISBN13,
|
|
||||||
"json": IsJSON,
|
|
||||||
"multibyte": IsMultibyte,
|
|
||||||
"ascii": IsASCII,
|
|
||||||
"printableascii": IsPrintableASCII,
|
|
||||||
"fullwidth": IsFullWidth,
|
|
||||||
"halfwidth": IsHalfWidth,
|
|
||||||
"variablewidth": IsVariableWidth,
|
|
||||||
"base64": IsBase64,
|
|
||||||
"datauri": IsDataURI,
|
|
||||||
"ip": IsIP,
|
|
||||||
"port": IsPort,
|
|
||||||
"ipv4": IsIPv4,
|
|
||||||
"ipv6": IsIPv6,
|
|
||||||
"dns": IsDNSName,
|
|
||||||
"host": IsHost,
|
|
||||||
"mac": IsMAC,
|
|
||||||
"latitude": IsLatitude,
|
|
||||||
"longitude": IsLongitude,
|
|
||||||
"ssn": IsSSN,
|
|
||||||
"semver": IsSemver,
|
|
||||||
"rfc3339": IsRFC3339,
|
|
||||||
"rfc3339WithoutZone": IsRFC3339WithoutZone,
|
|
||||||
"ISO3166Alpha2": IsISO3166Alpha2,
|
|
||||||
"ISO3166Alpha3": IsISO3166Alpha3,
|
|
||||||
```
|
|
||||||
Validators with parameters
|
|
||||||
|
|
||||||
```go
|
|
||||||
"range(min|max)": Range,
|
|
||||||
"length(min|max)": ByteLength,
|
|
||||||
"runelength(min|max)": RuneLength,
|
|
||||||
"matches(pattern)": StringMatches,
|
|
||||||
"in(string1|string2|...|stringN)": IsIn,
|
|
||||||
```
|
|
||||||
|
|
||||||
And here is small example of usage:
|
|
||||||
```go
|
|
||||||
type Post struct {
|
|
||||||
Title string `valid:"alphanum,required"`
|
|
||||||
Message string `valid:"duck,ascii"`
|
|
||||||
AuthorIP string `valid:"ipv4"`
|
|
||||||
Date string `valid:"-"`
|
|
||||||
}
|
|
||||||
post := &Post{
|
|
||||||
Title: "My Example Post",
|
|
||||||
Message: "duck",
|
|
||||||
AuthorIP: "123.234.54.3",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add your own struct validation tags
|
|
||||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
|
||||||
return str == "duck"
|
|
||||||
})
|
|
||||||
|
|
||||||
result, err := govalidator.ValidateStruct(post)
|
|
||||||
if err != nil {
|
|
||||||
println("error: " + err.Error())
|
|
||||||
}
|
|
||||||
println(result)
|
|
||||||
```
|
|
||||||
###### WhiteList
|
|
||||||
```go
|
|
||||||
// Remove all characters from string ignoring characters between "a" and "z"
|
|
||||||
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
|
||||||
```
|
|
||||||
|
|
||||||
###### Custom validation functions
|
|
||||||
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
|
|
||||||
```go
|
|
||||||
import "github.com/asaskevich/govalidator"
|
|
||||||
|
|
||||||
type CustomByteArray [6]byte // custom types are supported and can be validated
|
|
||||||
|
|
||||||
type StructWithCustomByteArray struct {
|
|
||||||
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
|
|
||||||
Email string `valid:"email"`
|
|
||||||
CustomMinLength int `valid:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
|
||||||
switch v := context.(type) { // you can type switch on the context interface being validated
|
|
||||||
case StructWithCustomByteArray:
|
|
||||||
// you can check and validate against some other field in the context,
|
|
||||||
// return early or not validate against the context at all – your choice
|
|
||||||
case SomeOtherType:
|
|
||||||
// ...
|
|
||||||
default:
|
|
||||||
// expecting some other type? Throw/panic here or continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := i.(type) { // type switch on the struct field being validated
|
|
||||||
case CustomByteArray:
|
|
||||||
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
|
|
||||||
if e != 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}))
|
|
||||||
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
|
||||||
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
|
|
||||||
case StructWithCustomByteArray:
|
|
||||||
return len(v.ID) >= v.CustomMinLength
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}))
|
|
||||||
```
|
|
||||||
|
|
||||||
###### Custom error messages
|
|
||||||
Custom error messages are supported via annotations by adding the `~` separator - here's an example of how to use it:
|
|
||||||
```go
|
|
||||||
type Ticket struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
FirstName string `json:"firstname" valid:"required~First name is blank"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Notes
|
|
||||||
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
|
||||||
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
|
||||||
|
|
||||||
#### Support
|
|
||||||
If you do have a contribution to the package, feel free to create a Pull Request or an Issue.
|
|
||||||
|
|
||||||
#### What to contribute
|
|
||||||
If you don't know what to do, there are some features and functions that need to be done
|
|
||||||
|
|
||||||
- [ ] Refactor code
|
|
||||||
- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check
|
|
||||||
- [ ] Create actual list of contributors and projects that currently using this package
|
|
||||||
- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues)
|
|
||||||
- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions)
|
|
||||||
- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new
|
|
||||||
- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc
|
|
||||||
- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224)
|
|
||||||
- [ ] Implement fuzzing testing
|
|
||||||
- [ ] Implement some struct/map/array utilities
|
|
||||||
- [ ] Implement map/array validation
|
|
||||||
- [ ] Implement benchmarking
|
|
||||||
- [ ] Implement batch of examples
|
|
||||||
- [ ] Look at forks for new features and fixes
|
|
||||||
|
|
||||||
#### Advice
|
|
||||||
Feel free to create what you want, but keep in mind when you implement new features:
|
|
||||||
- Code must be clear and readable, names of variables/constants clearly describes what they are doing
|
|
||||||
- Public functions must be documented and described in source file and added to README.md to the list of available functions
|
|
||||||
- There are must be unit-tests for any new functions and improvements
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
### Contributors
|
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
|
||||||
|
|
||||||
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
|
||||||
* [Daniel Lohse](https://github.com/annismckenzie)
|
|
||||||
* [Attila Oláh](https://github.com/attilaolah)
|
|
||||||
* [Daniel Korner](https://github.com/Dadie)
|
|
||||||
* [Steven Wilkin](https://github.com/stevenwilkin)
|
|
||||||
* [Deiwin Sarjas](https://github.com/deiwin)
|
|
||||||
* [Noah Shibley](https://github.com/slugmobile)
|
|
||||||
* [Nathan Davies](https://github.com/nathj07)
|
|
||||||
* [Matt Sanford](https://github.com/mzsanford)
|
|
||||||
* [Simon ccl1115](https://github.com/ccl1115)
|
|
||||||
|
|
||||||
<a href="graphs/contributors"><img src="https://opencollective.com/govalidator/contributors.svg?width=890" /></a>
|
|
||||||
|
|
||||||
|
|
||||||
### Backers
|
|
||||||
|
|
||||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/govalidator#backer)]
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/govalidator#backers" target="_blank"><img src="https://opencollective.com/govalidator/backers.svg?width=890"></a>
|
|
||||||
|
|
||||||
|
|
||||||
### Sponsors
|
|
||||||
|
|
||||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/govalidator#sponsor)]
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/0/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/1/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/2/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/3/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/4/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/5/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/6/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/7/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/8/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/govalidator/sponsor/9/website" target="_blank"><img src="https://opencollective.com/govalidator/sponsor/9/avatar.svg"></a>
|
|
||||||
|
|
||||||
|
|
||||||
58
vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
58
vendor/github.com/asaskevich/govalidator/arrays.go
generated
vendored
@@ -1,58 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
// Iterator is the function that accepts element of slice/array and its index
|
|
||||||
type Iterator func(interface{}, int)
|
|
||||||
|
|
||||||
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
|
||||||
type ResultIterator func(interface{}, int) interface{}
|
|
||||||
|
|
||||||
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
|
||||||
type ConditionIterator func(interface{}, int) bool
|
|
||||||
|
|
||||||
// Each iterates over the slice and apply Iterator to every item
|
|
||||||
func Each(array []interface{}, iterator Iterator) {
|
|
||||||
for index, data := range array {
|
|
||||||
iterator(data, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
|
||||||
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
|
||||||
var result = make([]interface{}, len(array))
|
|
||||||
for index, data := range array {
|
|
||||||
result[index] = iterator(data, index)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
|
||||||
func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
|
||||||
for index, data := range array {
|
|
||||||
if iterator(data, index) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
|
||||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
|
||||||
var result = make([]interface{}, 0)
|
|
||||||
for index, data := range array {
|
|
||||||
if iterator(data, index) {
|
|
||||||
result = append(result, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
|
||||||
func Count(array []interface{}, iterator ConditionIterator) int {
|
|
||||||
count := 0
|
|
||||||
for index, data := range array {
|
|
||||||
if iterator(data, index) {
|
|
||||||
count = count + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
116
vendor/github.com/asaskevich/govalidator/arrays_test.go
generated
vendored
116
vendor/github.com/asaskevich/govalidator/arrays_test.go
generated
vendored
@@ -1,116 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestEach(t *testing.T) {
|
|
||||||
// TODO Maybe refactor?
|
|
||||||
t.Parallel()
|
|
||||||
acc := 0
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5}
|
|
||||||
var fn Iterator = func(value interface{}, index int) {
|
|
||||||
acc = acc + value.(int)
|
|
||||||
}
|
|
||||||
Each(data, fn)
|
|
||||||
if acc != 15 {
|
|
||||||
t.Errorf("Expected Each(..) to be %v, got %v", 15, acc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleEach() {
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5}
|
|
||||||
var fn Iterator = func(value interface{}, index int) {
|
|
||||||
println(value.(int))
|
|
||||||
}
|
|
||||||
Each(data, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMap(t *testing.T) {
|
|
||||||
// TODO Maybe refactor?
|
|
||||||
t.Parallel()
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5}
|
|
||||||
var fn ResultIterator = func(value interface{}, index int) interface{} {
|
|
||||||
return value.(int) * 3
|
|
||||||
}
|
|
||||||
result := Map(data, fn)
|
|
||||||
for i, d := range result {
|
|
||||||
if d != fn(data[i], i) {
|
|
||||||
t.Errorf("Expected Map(..) to be %v, got %v", fn(data[i], i), d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleMap() {
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5}
|
|
||||||
var fn ResultIterator = func(value interface{}, index int) interface{} {
|
|
||||||
return value.(int) * 3
|
|
||||||
}
|
|
||||||
_ = Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFind(t *testing.T) {
|
|
||||||
// TODO Maybe refactor?
|
|
||||||
t.Parallel()
|
|
||||||
findElement := 96
|
|
||||||
data := []interface{}{1, 2, 3, 4, findElement, 5}
|
|
||||||
var fn1 ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
return value.(int) == findElement
|
|
||||||
}
|
|
||||||
var fn2 ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
value, _ = value.(string)
|
|
||||||
return value == "govalidator"
|
|
||||||
}
|
|
||||||
val1 := Find(data, fn1)
|
|
||||||
val2 := Find(data, fn2)
|
|
||||||
if val1 != findElement {
|
|
||||||
t.Errorf("Expected Find(..) to be %v, got %v", findElement, val1)
|
|
||||||
}
|
|
||||||
if val2 != nil {
|
|
||||||
t.Errorf("Expected Find(..) to be %v, got %v", nil, val2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilter(t *testing.T) {
|
|
||||||
// TODO Maybe refactor?
|
|
||||||
t.Parallel()
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
||||||
answer := []interface{}{2, 4, 6, 8, 10}
|
|
||||||
var fn ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
return value.(int)%2 == 0
|
|
||||||
}
|
|
||||||
result := Filter(data, fn)
|
|
||||||
for i := range result {
|
|
||||||
if result[i] != answer[i] {
|
|
||||||
t.Errorf("Expected Filter(..) to be %v, got %v", answer[i], result[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleFilter() {
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
||||||
var fn ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
return value.(int)%2 == 0
|
|
||||||
}
|
|
||||||
_ = Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCount(t *testing.T) {
|
|
||||||
// TODO Maybe refactor?
|
|
||||||
t.Parallel()
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
||||||
count := 5
|
|
||||||
var fn ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
return value.(int)%2 == 0
|
|
||||||
}
|
|
||||||
result := Count(data, fn)
|
|
||||||
if result != count {
|
|
||||||
t.Errorf("Expected Count(..) to be %v, got %v", count, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleCount() {
|
|
||||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
||||||
var fn ConditionIterator = func(value interface{}, index int) bool {
|
|
||||||
return value.(int)%2 == 0
|
|
||||||
}
|
|
||||||
_ = Count(data, fn) // result = 5
|
|
||||||
}
|
|
||||||
64
vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
64
vendor/github.com/asaskevich/govalidator/converter.go
generated
vendored
@@ -1,64 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ToString convert the input to a string.
|
|
||||||
func ToString(obj interface{}) string {
|
|
||||||
res := fmt.Sprintf("%v", obj)
|
|
||||||
return string(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToJSON convert the input to a valid JSON string
|
|
||||||
func ToJSON(obj interface{}) (string, error) {
|
|
||||||
res, err := json.Marshal(obj)
|
|
||||||
if err != nil {
|
|
||||||
res = []byte("")
|
|
||||||
}
|
|
||||||
return string(res), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
|
||||||
func ToFloat(str string) (float64, error) {
|
|
||||||
res, err := strconv.ParseFloat(str, 64)
|
|
||||||
if err != nil {
|
|
||||||
res = 0.0
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer.
|
|
||||||
func ToInt(value interface{}) (res int64, err error) {
|
|
||||||
val := reflect.ValueOf(value)
|
|
||||||
|
|
||||||
switch value.(type) {
|
|
||||||
case int, int8, int16, int32, int64:
|
|
||||||
res = val.Int()
|
|
||||||
case uint, uint8, uint16, uint32, uint64:
|
|
||||||
res = int64(val.Uint())
|
|
||||||
case string:
|
|
||||||
if IsInt(val.String()) {
|
|
||||||
res, err = strconv.ParseInt(val.String(), 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
res = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("math: square root of negative number %g", value)
|
|
||||||
res = 0
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("math: square root of negative number %g", value)
|
|
||||||
res = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBoolean convert the input string to a boolean.
|
|
||||||
func ToBoolean(str string) (bool, error) {
|
|
||||||
return strconv.ParseBool(str)
|
|
||||||
}
|
|
||||||
78
vendor/github.com/asaskevich/govalidator/converter_test.go
generated
vendored
78
vendor/github.com/asaskevich/govalidator/converter_test.go
generated
vendored
@@ -1,78 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestToInt(t *testing.T) {
|
|
||||||
tests := []string{"1000", "-123", "abcdef", "100000000000000000000000000000000000000000000"}
|
|
||||||
expected := []int64{1000, -123, 0, 0}
|
|
||||||
for i := 0; i < len(tests); i++ {
|
|
||||||
result, _ := ToInt(tests[i])
|
|
||||||
if result != expected[i] {
|
|
||||||
t.Log("Case ", i, ": expected ", expected[i], " when result is ", result)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToBoolean(t *testing.T) {
|
|
||||||
tests := []string{"true", "1", "True", "false", "0", "abcdef"}
|
|
||||||
expected := []bool{true, true, true, false, false, false}
|
|
||||||
for i := 0; i < len(tests); i++ {
|
|
||||||
res, _ := ToBoolean(tests[i])
|
|
||||||
if res != expected[i] {
|
|
||||||
t.Log("Case ", i, ": expected ", expected[i], " when result is ", res)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toString(t *testing.T, test interface{}, expected string) {
|
|
||||||
res := ToString(test)
|
|
||||||
if res != expected {
|
|
||||||
t.Log("Case ToString: expected ", expected, " when result is ", res)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToString(t *testing.T) {
|
|
||||||
toString(t, "str123", "str123")
|
|
||||||
toString(t, 123, "123")
|
|
||||||
toString(t, 12.3, "12.3")
|
|
||||||
toString(t, true, "true")
|
|
||||||
toString(t, 1.5+10i, "(1.5+10i)")
|
|
||||||
// Sprintf function not guarantee that maps with equal keys always will be equal in string representation
|
|
||||||
//toString(t, struct{ Keys map[int]int }{Keys: map[int]int{1: 2, 3: 4}}, "{map[1:2 3:4]}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToFloat(t *testing.T) {
|
|
||||||
tests := []string{"", "123", "-.01", "10.", "string", "1.23e3", ".23e10"}
|
|
||||||
expected := []float64{0, 123, -0.01, 10.0, 0, 1230, 0.23e10}
|
|
||||||
for i := 0; i < len(tests); i++ {
|
|
||||||
res, _ := ToFloat(tests[i])
|
|
||||||
if res != expected[i] {
|
|
||||||
t.Log("Case ", i, ": expected ", expected[i], " when result is ", res)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToJSON(t *testing.T) {
|
|
||||||
tests := []interface{}{"test", map[string]string{"a": "b", "b": "c"}, func() error { return fmt.Errorf("Error") }}
|
|
||||||
expected := [][]string{
|
|
||||||
{"\"test\"", "<nil>"},
|
|
||||||
{"{\"a\":\"b\",\"b\":\"c\"}", "<nil>"},
|
|
||||||
{"", "json: unsupported type: func() error"},
|
|
||||||
}
|
|
||||||
for i, test := range tests {
|
|
||||||
actual, err := ToJSON(test)
|
|
||||||
if actual != expected[i][0] {
|
|
||||||
t.Errorf("Expected toJSON(%v) to return '%v', got '%v'", test, expected[i][0], actual)
|
|
||||||
}
|
|
||||||
if fmt.Sprintf("%v", err) != expected[i][1] {
|
|
||||||
t.Errorf("Expected error returned from toJSON(%v) to return '%v', got '%v'", test, expected[i][1], fmt.Sprintf("%v", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
36
vendor/github.com/asaskevich/govalidator/error.go
generated
vendored
@@ -1,36 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// Errors is an array of multiple errors and conforms to the error interface.
|
|
||||||
type Errors []error
|
|
||||||
|
|
||||||
// Errors returns itself.
|
|
||||||
func (es Errors) Errors() []error {
|
|
||||||
return es
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es Errors) Error() string {
|
|
||||||
var errs []string
|
|
||||||
for _, e := range es {
|
|
||||||
errs = append(errs, e.Error())
|
|
||||||
}
|
|
||||||
return strings.Join(errs, ";")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error encapsulates a name, an error and whether there's a custom error message or not.
|
|
||||||
type Error struct {
|
|
||||||
Name string
|
|
||||||
Err error
|
|
||||||
CustomErrorMessageExists bool
|
|
||||||
|
|
||||||
// Validator indicates the name of the validator that failed
|
|
||||||
Validator string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
|
||||||
if e.CustomErrorMessageExists {
|
|
||||||
return e.Err.Error()
|
|
||||||
}
|
|
||||||
return e.Name + ": " + e.Err.Error()
|
|
||||||
}
|
|
||||||
29
vendor/github.com/asaskevich/govalidator/error_test.go
generated
vendored
29
vendor/github.com/asaskevich/govalidator/error_test.go
generated
vendored
@@ -1,29 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestErrorsToString(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
customErr := &Error{Name: "Custom Error Name", Err: fmt.Errorf("stdlib error")}
|
|
||||||
customErrWithCustomErrorMessage := &Error{Name: "Custom Error Name 2", Err: fmt.Errorf("Bad stuff happened"), CustomErrorMessageExists: true}
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 Errors
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{Errors{}, ""},
|
|
||||||
{Errors{fmt.Errorf("Error 1")}, "Error 1"},
|
|
||||||
{Errors{fmt.Errorf("Error 1"), fmt.Errorf("Error 2")}, "Error 1;Error 2"},
|
|
||||||
{Errors{customErr, fmt.Errorf("Error 2")}, "Custom Error Name: stdlib error;Error 2"},
|
|
||||||
{Errors{fmt.Errorf("Error 123"), customErrWithCustomErrorMessage}, "Error 123;Bad stuff happened"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := test.param1.Error()
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Error() to return '%v', got '%v'", test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
97
vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
97
vendor/github.com/asaskevich/govalidator/numerics.go
generated
vendored
@@ -1,97 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Abs returns absolute value of number
|
|
||||||
func Abs(value float64) float64 {
|
|
||||||
return math.Abs(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
|
||||||
func Sign(value float64) float64 {
|
|
||||||
if value > 0 {
|
|
||||||
return 1
|
|
||||||
} else if value < 0 {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNegative returns true if value < 0
|
|
||||||
func IsNegative(value float64) bool {
|
|
||||||
return value < 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPositive returns true if value > 0
|
|
||||||
func IsPositive(value float64) bool {
|
|
||||||
return value > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNonNegative returns true if value >= 0
|
|
||||||
func IsNonNegative(value float64) bool {
|
|
||||||
return value >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNonPositive returns true if value <= 0
|
|
||||||
func IsNonPositive(value float64) bool {
|
|
||||||
return value <= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// InRange returns true if value lies between left and right border
|
|
||||||
func InRangeInt(value, left, right interface{}) bool {
|
|
||||||
value64, _ := ToInt(value)
|
|
||||||
left64, _ := ToInt(left)
|
|
||||||
right64, _ := ToInt(right)
|
|
||||||
if left64 > right64 {
|
|
||||||
left64, right64 = right64, left64
|
|
||||||
}
|
|
||||||
return value64 >= left64 && value64 <= right64
|
|
||||||
}
|
|
||||||
|
|
||||||
// InRange returns true if value lies between left and right border
|
|
||||||
func InRangeFloat32(value, left, right float32) bool {
|
|
||||||
if left > right {
|
|
||||||
left, right = right, left
|
|
||||||
}
|
|
||||||
return value >= left && value <= right
|
|
||||||
}
|
|
||||||
|
|
||||||
// InRange returns true if value lies between left and right border
|
|
||||||
func InRangeFloat64(value, left, right float64) bool {
|
|
||||||
if left > right {
|
|
||||||
left, right = right, left
|
|
||||||
}
|
|
||||||
return value >= left && value <= right
|
|
||||||
}
|
|
||||||
|
|
||||||
// InRange returns true if value lies between left and right border, generic type to handle int, float32 or float64, all types must the same type
|
|
||||||
func InRange(value interface{}, left interface{}, right interface{}) bool {
|
|
||||||
|
|
||||||
reflectValue := reflect.TypeOf(value).Kind()
|
|
||||||
reflectLeft := reflect.TypeOf(left).Kind()
|
|
||||||
reflectRight := reflect.TypeOf(right).Kind()
|
|
||||||
|
|
||||||
if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int {
|
|
||||||
return InRangeInt(value.(int), left.(int), right.(int))
|
|
||||||
} else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 {
|
|
||||||
return InRangeFloat32(value.(float32), left.(float32), right.(float32))
|
|
||||||
} else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 {
|
|
||||||
return InRangeFloat64(value.(float64), left.(float64), right.(float64))
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWhole returns true if value is whole number
|
|
||||||
func IsWhole(value float64) bool {
|
|
||||||
return math.Remainder(value, 1) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNatural returns true if value is natural number (positive and whole)
|
|
||||||
func IsNatural(value float64) bool {
|
|
||||||
return IsWhole(value) && IsPositive(value)
|
|
||||||
}
|
|
||||||
549
vendor/github.com/asaskevich/govalidator/numerics_test.go
generated
vendored
549
vendor/github.com/asaskevich/govalidator/numerics_test.go
generated
vendored
@@ -1,549 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestAbs(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected float64
|
|
||||||
}{
|
|
||||||
{0, 0},
|
|
||||||
{-1, 1},
|
|
||||||
{10, 10},
|
|
||||||
{3.14, 3.14},
|
|
||||||
{-96, 96},
|
|
||||||
{-10e-12, 10e-12},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Abs(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Abs(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSign(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected float64
|
|
||||||
}{
|
|
||||||
{0, 0},
|
|
||||||
{-1, -1},
|
|
||||||
{10, 1},
|
|
||||||
{3.14, 1},
|
|
||||||
{-96, -1},
|
|
||||||
{-10e-12, -1},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Sign(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Sign(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsNegative(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, false},
|
|
||||||
{-1, true},
|
|
||||||
{10, false},
|
|
||||||
{3.14, false},
|
|
||||||
{-96, true},
|
|
||||||
{-10e-12, true},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := IsNegative(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected IsNegative(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsNonNegative(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, true},
|
|
||||||
{-1, false},
|
|
||||||
{10, true},
|
|
||||||
{3.14, true},
|
|
||||||
{-96, false},
|
|
||||||
{-10e-12, false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := IsNonNegative(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected IsNonNegative(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsPositive(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, false},
|
|
||||||
{-1, false},
|
|
||||||
{10, true},
|
|
||||||
{3.14, true},
|
|
||||||
{-96, false},
|
|
||||||
{-10e-12, false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := IsPositive(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected IsPositive(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsNonPositive(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, true},
|
|
||||||
{-1, true},
|
|
||||||
{10, false},
|
|
||||||
{3.14, false},
|
|
||||||
{-96, true},
|
|
||||||
{-10e-12, true},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := IsNonPositive(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected IsNonPositive(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsWhole(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, true},
|
|
||||||
{-1, true},
|
|
||||||
{10, true},
|
|
||||||
{3.14, false},
|
|
||||||
{-96, true},
|
|
||||||
{-10e-12, false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := IsWhole(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected IsWhole(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsNatural(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, false},
|
|
||||||
{-1, false},
|
|
||||||
{10, true},
|
|
||||||
{3.14, false},
|
|
||||||
{96, true},
|
|
||||||
{-10e-12, false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := IsNatural(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected IsNatural(%v) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInRangeInt(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var testAsInts = []struct {
|
|
||||||
param int
|
|
||||||
left int
|
|
||||||
right int
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsInts {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type int", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsInt8s = []struct {
|
|
||||||
param int8
|
|
||||||
left int8
|
|
||||||
right int8
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsInt8s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type int8", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsInt16s = []struct {
|
|
||||||
param int16
|
|
||||||
left int16
|
|
||||||
right int16
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsInt16s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type int16", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsInt32s = []struct {
|
|
||||||
param int32
|
|
||||||
left int32
|
|
||||||
right int32
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsInt32s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type int32", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsInt64s = []struct {
|
|
||||||
param int64
|
|
||||||
left int64
|
|
||||||
right int64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsInt64s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type int64", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsUInts = []struct {
|
|
||||||
param uint
|
|
||||||
left uint
|
|
||||||
right uint
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsUInts {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type uint", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsUInt8s = []struct {
|
|
||||||
param uint8
|
|
||||||
left uint8
|
|
||||||
right uint8
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsUInt8s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type uint", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsUInt16s = []struct {
|
|
||||||
param uint16
|
|
||||||
left uint16
|
|
||||||
right uint16
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsUInt16s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type uint", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsUInt32s = []struct {
|
|
||||||
param uint32
|
|
||||||
left uint32
|
|
||||||
right uint32
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsUInt32s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type uint", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsUInt64s = []struct {
|
|
||||||
param uint64
|
|
||||||
left uint64
|
|
||||||
right uint64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsUInt64s {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type uint", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAsStrings = []struct {
|
|
||||||
param string
|
|
||||||
left string
|
|
||||||
right string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{"0", "0", "0", true},
|
|
||||||
{"1", "0", "0", false},
|
|
||||||
{"-1", "0", "0", false},
|
|
||||||
{"0", "-1", "1", true},
|
|
||||||
{"0", "0", "1", true},
|
|
||||||
{"0", "-1", "0", true},
|
|
||||||
{"0", "0", "-1", true},
|
|
||||||
{"0", "10", "5", false},
|
|
||||||
}
|
|
||||||
for _, test := range testAsStrings {
|
|
||||||
actual := InRangeInt(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v using type string", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInRangeFloat32(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float32
|
|
||||||
left float32
|
|
||||||
right float32
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := InRangeFloat32(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeFloat32(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInRangeFloat64(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param float64
|
|
||||||
left float64
|
|
||||||
right float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := InRangeFloat64(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRangeFloat64(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInRange(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var testsInt = []struct {
|
|
||||||
param int
|
|
||||||
left int
|
|
||||||
right int
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testsInt {
|
|
||||||
actual := InRange(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testsFloat32 = []struct {
|
|
||||||
param float32
|
|
||||||
left float32
|
|
||||||
right float32
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testsFloat32 {
|
|
||||||
actual := InRange(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testsFloat64 = []struct {
|
|
||||||
param float64
|
|
||||||
left float64
|
|
||||||
right float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, true},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, true},
|
|
||||||
{0, 0, 1, true},
|
|
||||||
{0, -1, 0, true},
|
|
||||||
{0, 0, -1, true},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testsFloat64 {
|
|
||||||
actual := InRange(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testsTypeMix = []struct {
|
|
||||||
param int
|
|
||||||
left float64
|
|
||||||
right float64
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, false},
|
|
||||||
{1, 0, 0, false},
|
|
||||||
{-1, 0, 0, false},
|
|
||||||
{0, -1, 1, false},
|
|
||||||
{0, 0, 1, false},
|
|
||||||
{0, -1, 0, false},
|
|
||||||
{0, 0, -1, false},
|
|
||||||
{0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for _, test := range testsTypeMix {
|
|
||||||
actual := InRange(test.param, test.left, test.right)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
97
vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
97
vendor/github.com/asaskevich/govalidator/patterns.go
generated
vendored
@@ -1,97 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import "regexp"
|
|
||||||
|
|
||||||
// Basic regular expressions for validating strings
|
|
||||||
const (
|
|
||||||
//Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
|
||||||
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
|
|
||||||
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
|
|
||||||
ISBN13 string = "^(?:[0-9]{13})$"
|
|
||||||
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
|
||||||
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
|
||||||
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
|
||||||
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
|
||||||
Alpha string = "^[a-zA-Z]+$"
|
|
||||||
Alphanumeric string = "^[a-zA-Z0-9]+$"
|
|
||||||
Numeric string = "^[0-9]+$"
|
|
||||||
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
|
|
||||||
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
|
|
||||||
Hexadecimal string = "^[0-9a-fA-F]+$"
|
|
||||||
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
|
||||||
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
|
|
||||||
ASCII string = "^[\x00-\x7F]+$"
|
|
||||||
Multibyte string = "[^\x00-\x7F]"
|
|
||||||
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
|
||||||
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
|
||||||
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
|
||||||
PrintableASCII string = "^[\x20-\x7E]+$"
|
|
||||||
DataURI string = "^data:.+\\/(.+);base64$"
|
|
||||||
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
|
||||||
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
|
||||||
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
|
|
||||||
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
|
|
||||||
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
|
|
||||||
URLUsername string = `(\S+(:\S*)?@)`
|
|
||||||
URLPath string = `((\/|\?|#)[^\s]*)`
|
|
||||||
URLPort string = `(:(\d{1,5}))`
|
|
||||||
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
|
|
||||||
URLSubdomain string = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))`
|
|
||||||
URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
|
|
||||||
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
|
||||||
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
|
||||||
UnixPath string = `^(/[^/\x00]*)+/?$`
|
|
||||||
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
|
|
||||||
tagName string = "valid"
|
|
||||||
hasLowerCase string = ".*[[:lower:]]"
|
|
||||||
hasUpperCase string = ".*[[:upper:]]"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Used by IsFilePath func
|
|
||||||
const (
|
|
||||||
// Unknown is unresolved OS type
|
|
||||||
Unknown = iota
|
|
||||||
// Win is Windows type
|
|
||||||
Win
|
|
||||||
// Unix is *nix OS types
|
|
||||||
Unix
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
|
|
||||||
hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$")
|
|
||||||
userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})")
|
|
||||||
//rxEmail = regexp.MustCompile(Email)
|
|
||||||
rxCreditCard = regexp.MustCompile(CreditCard)
|
|
||||||
rxISBN10 = regexp.MustCompile(ISBN10)
|
|
||||||
rxISBN13 = regexp.MustCompile(ISBN13)
|
|
||||||
rxUUID3 = regexp.MustCompile(UUID3)
|
|
||||||
rxUUID4 = regexp.MustCompile(UUID4)
|
|
||||||
rxUUID5 = regexp.MustCompile(UUID5)
|
|
||||||
rxUUID = regexp.MustCompile(UUID)
|
|
||||||
rxAlpha = regexp.MustCompile(Alpha)
|
|
||||||
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
|
|
||||||
rxNumeric = regexp.MustCompile(Numeric)
|
|
||||||
rxInt = regexp.MustCompile(Int)
|
|
||||||
rxFloat = regexp.MustCompile(Float)
|
|
||||||
rxHexadecimal = regexp.MustCompile(Hexadecimal)
|
|
||||||
rxHexcolor = regexp.MustCompile(Hexcolor)
|
|
||||||
rxRGBcolor = regexp.MustCompile(RGBcolor)
|
|
||||||
rxASCII = regexp.MustCompile(ASCII)
|
|
||||||
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
|
|
||||||
rxMultibyte = regexp.MustCompile(Multibyte)
|
|
||||||
rxFullWidth = regexp.MustCompile(FullWidth)
|
|
||||||
rxHalfWidth = regexp.MustCompile(HalfWidth)
|
|
||||||
rxBase64 = regexp.MustCompile(Base64)
|
|
||||||
rxDataURI = regexp.MustCompile(DataURI)
|
|
||||||
rxLatitude = regexp.MustCompile(Latitude)
|
|
||||||
rxLongitude = regexp.MustCompile(Longitude)
|
|
||||||
rxDNSName = regexp.MustCompile(DNSName)
|
|
||||||
rxURL = regexp.MustCompile(URL)
|
|
||||||
rxSSN = regexp.MustCompile(SSN)
|
|
||||||
rxWinPath = regexp.MustCompile(WinPath)
|
|
||||||
rxUnixPath = regexp.MustCompile(UnixPath)
|
|
||||||
rxSemver = regexp.MustCompile(Semver)
|
|
||||||
rxHasLowerCase = regexp.MustCompile(hasLowerCase)
|
|
||||||
rxHasUpperCase = regexp.MustCompile(hasUpperCase)
|
|
||||||
)
|
|
||||||
616
vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
616
vendor/github.com/asaskevich/govalidator/types.go
generated
vendored
@@ -1,616 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
|
||||||
type Validator func(str string) bool
|
|
||||||
|
|
||||||
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
|
||||||
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
|
|
||||||
type CustomTypeValidator func(i interface{}, o interface{}) bool
|
|
||||||
|
|
||||||
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
|
||||||
type ParamValidator func(str string, params ...string) bool
|
|
||||||
type tagOptionsMap map[string]string
|
|
||||||
|
|
||||||
// UnsupportedTypeError is a wrapper for reflect.Type
|
|
||||||
type UnsupportedTypeError struct {
|
|
||||||
Type reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
|
||||||
// It implements the methods to sort by string.
|
|
||||||
type stringValues []reflect.Value
|
|
||||||
|
|
||||||
// ParamTagMap is a map of functions accept variants parameters
|
|
||||||
var ParamTagMap = map[string]ParamValidator{
|
|
||||||
"length": ByteLength,
|
|
||||||
"range": Range,
|
|
||||||
"runelength": RuneLength,
|
|
||||||
"stringlength": StringLength,
|
|
||||||
"matches": StringMatches,
|
|
||||||
"in": isInRaw,
|
|
||||||
"rsapub": IsRsaPub,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParamTagRegexMap maps param tags to their respective regexes.
|
|
||||||
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
|
||||||
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
|
|
||||||
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
|
||||||
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
|
|
||||||
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
|
||||||
"in": regexp.MustCompile(`^in\((.*)\)`),
|
|
||||||
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
|
|
||||||
"rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"),
|
|
||||||
}
|
|
||||||
|
|
||||||
type customTypeTagMap struct {
|
|
||||||
validators map[string]CustomTypeValidator
|
|
||||||
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
|
|
||||||
tm.RLock()
|
|
||||||
defer tm.RUnlock()
|
|
||||||
v, ok := tm.validators[name]
|
|
||||||
return v, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
|
|
||||||
tm.Lock()
|
|
||||||
defer tm.Unlock()
|
|
||||||
tm.validators[name] = ctv
|
|
||||||
}
|
|
||||||
|
|
||||||
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
|
||||||
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
|
||||||
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
|
||||||
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
|
|
||||||
|
|
||||||
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
|
||||||
var TagMap = map[string]Validator{
|
|
||||||
"email": IsEmail,
|
|
||||||
"url": IsURL,
|
|
||||||
"dialstring": IsDialString,
|
|
||||||
"requrl": IsRequestURL,
|
|
||||||
"requri": IsRequestURI,
|
|
||||||
"alpha": IsAlpha,
|
|
||||||
"utfletter": IsUTFLetter,
|
|
||||||
"alphanum": IsAlphanumeric,
|
|
||||||
"utfletternum": IsUTFLetterNumeric,
|
|
||||||
"numeric": IsNumeric,
|
|
||||||
"utfnumeric": IsUTFNumeric,
|
|
||||||
"utfdigit": IsUTFDigit,
|
|
||||||
"hexadecimal": IsHexadecimal,
|
|
||||||
"hexcolor": IsHexcolor,
|
|
||||||
"rgbcolor": IsRGBcolor,
|
|
||||||
"lowercase": IsLowerCase,
|
|
||||||
"uppercase": IsUpperCase,
|
|
||||||
"int": IsInt,
|
|
||||||
"float": IsFloat,
|
|
||||||
"null": IsNull,
|
|
||||||
"uuid": IsUUID,
|
|
||||||
"uuidv3": IsUUIDv3,
|
|
||||||
"uuidv4": IsUUIDv4,
|
|
||||||
"uuidv5": IsUUIDv5,
|
|
||||||
"creditcard": IsCreditCard,
|
|
||||||
"isbn10": IsISBN10,
|
|
||||||
"isbn13": IsISBN13,
|
|
||||||
"json": IsJSON,
|
|
||||||
"multibyte": IsMultibyte,
|
|
||||||
"ascii": IsASCII,
|
|
||||||
"printableascii": IsPrintableASCII,
|
|
||||||
"fullwidth": IsFullWidth,
|
|
||||||
"halfwidth": IsHalfWidth,
|
|
||||||
"variablewidth": IsVariableWidth,
|
|
||||||
"base64": IsBase64,
|
|
||||||
"datauri": IsDataURI,
|
|
||||||
"ip": IsIP,
|
|
||||||
"port": IsPort,
|
|
||||||
"ipv4": IsIPv4,
|
|
||||||
"ipv6": IsIPv6,
|
|
||||||
"dns": IsDNSName,
|
|
||||||
"host": IsHost,
|
|
||||||
"mac": IsMAC,
|
|
||||||
"latitude": IsLatitude,
|
|
||||||
"longitude": IsLongitude,
|
|
||||||
"ssn": IsSSN,
|
|
||||||
"semver": IsSemver,
|
|
||||||
"rfc3339": IsRFC3339,
|
|
||||||
"rfc3339WithoutZone": IsRFC3339WithoutZone,
|
|
||||||
"ISO3166Alpha2": IsISO3166Alpha2,
|
|
||||||
"ISO3166Alpha3": IsISO3166Alpha3,
|
|
||||||
"ISO4217": IsISO4217,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ISO3166Entry stores country codes
|
|
||||||
type ISO3166Entry struct {
|
|
||||||
EnglishShortName string
|
|
||||||
FrenchShortName string
|
|
||||||
Alpha2Code string
|
|
||||||
Alpha3Code string
|
|
||||||
Numeric string
|
|
||||||
}
|
|
||||||
|
|
||||||
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
|
||||||
var ISO3166List = []ISO3166Entry{
|
|
||||||
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
|
|
||||||
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
|
|
||||||
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
|
|
||||||
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
|
|
||||||
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
|
|
||||||
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
|
|
||||||
{"Angola", "Angola (l')", "AO", "AGO", "024"},
|
|
||||||
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
|
|
||||||
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
|
|
||||||
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
|
|
||||||
{"Australia", "Australie (l')", "AU", "AUS", "036"},
|
|
||||||
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
|
|
||||||
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
|
|
||||||
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
|
|
||||||
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
|
|
||||||
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
|
|
||||||
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
|
|
||||||
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
|
|
||||||
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
|
|
||||||
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
|
|
||||||
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
|
|
||||||
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
|
|
||||||
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
|
|
||||||
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
|
|
||||||
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
|
|
||||||
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
|
|
||||||
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
|
|
||||||
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
|
|
||||||
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
|
|
||||||
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
|
|
||||||
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
|
|
||||||
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
|
|
||||||
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
|
|
||||||
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
|
|
||||||
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
|
|
||||||
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
|
|
||||||
{"Canada", "Canada (le)", "CA", "CAN", "124"},
|
|
||||||
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
|
|
||||||
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
|
|
||||||
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
|
|
||||||
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
|
|
||||||
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
|
|
||||||
{"Chile", "Chili (le)", "CL", "CHL", "152"},
|
|
||||||
{"China", "Chine (la)", "CN", "CHN", "156"},
|
|
||||||
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
|
|
||||||
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
|
|
||||||
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
|
|
||||||
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
|
|
||||||
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
|
|
||||||
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
|
|
||||||
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
|
|
||||||
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
|
|
||||||
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
|
|
||||||
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
|
|
||||||
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
|
|
||||||
{"Cuba", "Cuba", "CU", "CUB", "192"},
|
|
||||||
{"Cyprus", "Chypre", "CY", "CYP", "196"},
|
|
||||||
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
|
|
||||||
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
|
|
||||||
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
|
|
||||||
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
|
|
||||||
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
|
|
||||||
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
|
|
||||||
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
|
|
||||||
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
|
|
||||||
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
|
|
||||||
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
|
|
||||||
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
|
|
||||||
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
|
|
||||||
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
|
|
||||||
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
|
|
||||||
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
|
|
||||||
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
|
|
||||||
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
|
|
||||||
{"France", "France (la)", "FR", "FRA", "250"},
|
|
||||||
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
|
|
||||||
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
|
|
||||||
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
|
|
||||||
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
|
|
||||||
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
|
|
||||||
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
|
|
||||||
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
|
|
||||||
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
|
|
||||||
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
|
|
||||||
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
|
|
||||||
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
|
|
||||||
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
|
|
||||||
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
|
|
||||||
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
|
|
||||||
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
|
|
||||||
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
|
|
||||||
{"Guam", "Guam", "GU", "GUM", "316"},
|
|
||||||
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
|
|
||||||
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
|
|
||||||
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
|
|
||||||
{"Haiti", "Haïti", "HT", "HTI", "332"},
|
|
||||||
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
|
|
||||||
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
|
|
||||||
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
|
|
||||||
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
|
|
||||||
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
|
|
||||||
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
|
|
||||||
{"India", "Inde (l')", "IN", "IND", "356"},
|
|
||||||
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
|
|
||||||
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
|
|
||||||
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
|
|
||||||
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
|
|
||||||
{"Israel", "Israël", "IL", "ISR", "376"},
|
|
||||||
{"Italy", "Italie (l')", "IT", "ITA", "380"},
|
|
||||||
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
|
|
||||||
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
|
|
||||||
{"Japan", "Japon (le)", "JP", "JPN", "392"},
|
|
||||||
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
|
|
||||||
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
|
|
||||||
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
|
|
||||||
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
|
|
||||||
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
|
|
||||||
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
|
|
||||||
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
|
|
||||||
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
|
|
||||||
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
|
|
||||||
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
|
|
||||||
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
|
|
||||||
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
|
|
||||||
{"Libya", "Libye (la)", "LY", "LBY", "434"},
|
|
||||||
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
|
|
||||||
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
|
|
||||||
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
|
|
||||||
{"Macao", "Macao", "MO", "MAC", "446"},
|
|
||||||
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
|
|
||||||
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
|
|
||||||
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
|
|
||||||
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
|
|
||||||
{"Mali", "Mali (le)", "ML", "MLI", "466"},
|
|
||||||
{"Malta", "Malte", "MT", "MLT", "470"},
|
|
||||||
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
|
|
||||||
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
|
|
||||||
{"Mauritius", "Maurice", "MU", "MUS", "480"},
|
|
||||||
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
|
|
||||||
{"Monaco", "Monaco", "MC", "MCO", "492"},
|
|
||||||
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
|
|
||||||
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
|
|
||||||
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
|
|
||||||
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
|
|
||||||
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
|
|
||||||
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
|
|
||||||
{"Oman", "Oman", "OM", "OMN", "512"},
|
|
||||||
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
|
|
||||||
{"Nauru", "Nauru", "NR", "NRU", "520"},
|
|
||||||
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
|
|
||||||
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
|
|
||||||
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
|
|
||||||
{"Aruba", "Aruba", "AW", "ABW", "533"},
|
|
||||||
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
|
|
||||||
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
|
|
||||||
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
|
|
||||||
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
|
|
||||||
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
|
|
||||||
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
|
|
||||||
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
|
|
||||||
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
|
|
||||||
{"Niue", "Niue", "NU", "NIU", "570"},
|
|
||||||
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
|
|
||||||
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
|
|
||||||
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
|
|
||||||
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
|
|
||||||
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
|
|
||||||
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
|
|
||||||
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
|
|
||||||
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
|
|
||||||
{"Panama", "Panama (le)", "PA", "PAN", "591"},
|
|
||||||
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
|
|
||||||
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
|
|
||||||
{"Peru", "Pérou (le)", "PE", "PER", "604"},
|
|
||||||
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
|
|
||||||
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
|
|
||||||
{"Poland", "Pologne (la)", "PL", "POL", "616"},
|
|
||||||
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
|
|
||||||
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
|
|
||||||
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
|
|
||||||
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
|
|
||||||
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
|
|
||||||
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
|
|
||||||
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
|
|
||||||
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
|
|
||||||
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
|
|
||||||
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
|
|
||||||
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
|
|
||||||
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
|
|
||||||
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
|
|
||||||
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
|
|
||||||
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
|
|
||||||
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
|
|
||||||
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
|
|
||||||
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
|
|
||||||
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
|
|
||||||
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
|
|
||||||
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
|
|
||||||
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
|
|
||||||
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
|
|
||||||
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
|
|
||||||
{"Singapore", "Singapour", "SG", "SGP", "702"},
|
|
||||||
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
|
|
||||||
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
|
|
||||||
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
|
|
||||||
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
|
|
||||||
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
|
|
||||||
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
|
|
||||||
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
|
|
||||||
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
|
|
||||||
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
|
|
||||||
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
|
|
||||||
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
|
|
||||||
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
|
|
||||||
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
|
|
||||||
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
|
|
||||||
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
|
|
||||||
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
|
|
||||||
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
|
|
||||||
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
|
|
||||||
{"Togo", "Togo (le)", "TG", "TGO", "768"},
|
|
||||||
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
|
|
||||||
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
|
|
||||||
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
|
|
||||||
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
|
|
||||||
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
|
|
||||||
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
|
|
||||||
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
|
|
||||||
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
|
|
||||||
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
|
|
||||||
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
|
|
||||||
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
|
|
||||||
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
|
|
||||||
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
|
|
||||||
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
|
|
||||||
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
|
|
||||||
{"Jersey", "Jersey", "JE", "JEY", "832"},
|
|
||||||
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
|
|
||||||
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
|
|
||||||
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
|
|
||||||
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
|
|
||||||
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
|
|
||||||
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
|
|
||||||
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
|
|
||||||
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
|
|
||||||
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
|
|
||||||
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
|
|
||||||
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
|
|
||||||
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// ISO4217List is the list of ISO currency codes
|
|
||||||
var ISO4217List = []string{
|
|
||||||
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
|
|
||||||
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
|
|
||||||
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
|
|
||||||
"DJF", "DKK", "DOP", "DZD",
|
|
||||||
"EGP", "ERN", "ETB", "EUR",
|
|
||||||
"FJD", "FKP",
|
|
||||||
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
|
|
||||||
"HKD", "HNL", "HRK", "HTG", "HUF",
|
|
||||||
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
|
|
||||||
"JMD", "JOD", "JPY",
|
|
||||||
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
|
|
||||||
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
|
|
||||||
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
|
|
||||||
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
|
|
||||||
"OMR",
|
|
||||||
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
|
|
||||||
"QAR",
|
|
||||||
"RON", "RSD", "RUB", "RWF",
|
|
||||||
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL",
|
|
||||||
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
|
|
||||||
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS",
|
|
||||||
"VEF", "VND", "VUV",
|
|
||||||
"WST",
|
|
||||||
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
|
|
||||||
"YER",
|
|
||||||
"ZAR", "ZMW", "ZWL",
|
|
||||||
}
|
|
||||||
|
|
||||||
// ISO693Entry stores ISO language codes
|
|
||||||
type ISO693Entry struct {
|
|
||||||
Alpha3bCode string
|
|
||||||
Alpha2Code string
|
|
||||||
English string
|
|
||||||
}
|
|
||||||
|
|
||||||
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
|
|
||||||
var ISO693List = []ISO693Entry{
|
|
||||||
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
|
|
||||||
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
|
|
||||||
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
|
|
||||||
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
|
|
||||||
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
|
|
||||||
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
|
|
||||||
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
|
|
||||||
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
|
|
||||||
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
|
|
||||||
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
|
|
||||||
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
|
|
||||||
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
|
|
||||||
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
|
|
||||||
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
|
|
||||||
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
|
|
||||||
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
|
|
||||||
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
|
|
||||||
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
|
|
||||||
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
|
|
||||||
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
|
|
||||||
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
|
|
||||||
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
|
|
||||||
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
|
|
||||||
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
|
|
||||||
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
|
|
||||||
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
|
|
||||||
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
|
|
||||||
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
|
|
||||||
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
|
|
||||||
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
|
|
||||||
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
|
|
||||||
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
|
|
||||||
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
|
|
||||||
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
|
|
||||||
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
|
|
||||||
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
|
|
||||||
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
|
|
||||||
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
|
|
||||||
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
|
|
||||||
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
|
|
||||||
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
|
|
||||||
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
|
|
||||||
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
|
|
||||||
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
|
|
||||||
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
|
|
||||||
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
|
|
||||||
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
|
|
||||||
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
|
|
||||||
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
|
|
||||||
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
|
|
||||||
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
|
|
||||||
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
|
|
||||||
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
|
|
||||||
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
|
|
||||||
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
|
|
||||||
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
|
|
||||||
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
|
|
||||||
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
|
|
||||||
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
|
|
||||||
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
|
|
||||||
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
|
|
||||||
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
|
|
||||||
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
|
|
||||||
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
|
|
||||||
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
|
|
||||||
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
|
|
||||||
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
|
|
||||||
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
|
|
||||||
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
|
|
||||||
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
|
|
||||||
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
|
|
||||||
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
|
|
||||||
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
|
|
||||||
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
|
|
||||||
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
|
|
||||||
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
|
|
||||||
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
|
|
||||||
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
|
|
||||||
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
|
|
||||||
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
|
|
||||||
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
|
|
||||||
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
|
|
||||||
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
|
|
||||||
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
|
|
||||||
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
|
|
||||||
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
|
|
||||||
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
|
|
||||||
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
|
|
||||||
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
|
|
||||||
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
|
|
||||||
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
|
|
||||||
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
|
|
||||||
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
|
|
||||||
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
|
|
||||||
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
|
|
||||||
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
|
|
||||||
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
|
|
||||||
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
|
|
||||||
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
|
|
||||||
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
|
|
||||||
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
|
|
||||||
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
|
|
||||||
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
|
|
||||||
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
|
|
||||||
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
|
|
||||||
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
|
|
||||||
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
|
|
||||||
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
|
|
||||||
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
|
|
||||||
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
|
|
||||||
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
|
|
||||||
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
|
|
||||||
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
|
|
||||||
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
|
|
||||||
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
|
|
||||||
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
|
|
||||||
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
|
|
||||||
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
|
|
||||||
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
|
|
||||||
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
|
|
||||||
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
|
|
||||||
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
|
|
||||||
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
|
|
||||||
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
|
|
||||||
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
|
|
||||||
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
|
|
||||||
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
|
|
||||||
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
|
|
||||||
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
|
|
||||||
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
|
|
||||||
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
|
|
||||||
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
|
|
||||||
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
|
|
||||||
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
|
|
||||||
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
|
|
||||||
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
|
|
||||||
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
|
|
||||||
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
|
|
||||||
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
|
|
||||||
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
|
|
||||||
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
|
|
||||||
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
|
|
||||||
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
|
|
||||||
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
|
|
||||||
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
|
|
||||||
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
|
|
||||||
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
|
|
||||||
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
|
|
||||||
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
|
|
||||||
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
|
|
||||||
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
|
|
||||||
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
|
|
||||||
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
|
|
||||||
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
|
|
||||||
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
|
|
||||||
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
|
|
||||||
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
|
|
||||||
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
|
|
||||||
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
|
|
||||||
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
|
|
||||||
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
|
|
||||||
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
|
|
||||||
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
|
|
||||||
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
|
|
||||||
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
|
|
||||||
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
|
|
||||||
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
|
|
||||||
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
|
|
||||||
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
|
|
||||||
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
|
|
||||||
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
|
|
||||||
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
|
|
||||||
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
|
|
||||||
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
|
|
||||||
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
|
|
||||||
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
|
|
||||||
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
|
|
||||||
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
|
|
||||||
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
|
|
||||||
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
|
|
||||||
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
|
|
||||||
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
|
|
||||||
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
|
|
||||||
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
|
|
||||||
}
|
|
||||||
264
vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
264
vendor/github.com/asaskevich/govalidator/utils.go
generated
vendored
@@ -1,264 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"math"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Contains check if the string contains the substring.
|
|
||||||
func Contains(str, substring string) bool {
|
|
||||||
return strings.Contains(str, substring)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches check if string matches the pattern (pattern is regular expression)
|
|
||||||
// In case of error return false
|
|
||||||
func Matches(str, pattern string) bool {
|
|
||||||
match, _ := regexp.MatchString(pattern, str)
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
|
|
||||||
// LeftTrim trim characters from the left-side of the input.
|
|
||||||
// If second argument is empty, it's will be remove leading spaces.
|
|
||||||
func LeftTrim(str, chars string) string {
|
|
||||||
if chars == "" {
|
|
||||||
return strings.TrimLeftFunc(str, unicode.IsSpace)
|
|
||||||
}
|
|
||||||
r, _ := regexp.Compile("^[" + chars + "]+")
|
|
||||||
return r.ReplaceAllString(str, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RightTrim trim characters from the right-side of the input.
|
|
||||||
// If second argument is empty, it's will be remove spaces.
|
|
||||||
func RightTrim(str, chars string) string {
|
|
||||||
if chars == "" {
|
|
||||||
return strings.TrimRightFunc(str, unicode.IsSpace)
|
|
||||||
}
|
|
||||||
r, _ := regexp.Compile("[" + chars + "]+$")
|
|
||||||
return r.ReplaceAllString(str, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim trim characters from both sides of the input.
|
|
||||||
// If second argument is empty, it's will be remove spaces.
|
|
||||||
func Trim(str, chars string) string {
|
|
||||||
return LeftTrim(RightTrim(str, chars), chars)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhiteList remove characters that do not appear in the whitelist.
|
|
||||||
func WhiteList(str, chars string) string {
|
|
||||||
pattern := "[^" + chars + "]+"
|
|
||||||
r, _ := regexp.Compile(pattern)
|
|
||||||
return r.ReplaceAllString(str, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlackList remove characters that appear in the blacklist.
|
|
||||||
func BlackList(str, chars string) string {
|
|
||||||
pattern := "[" + chars + "]+"
|
|
||||||
r, _ := regexp.Compile(pattern)
|
|
||||||
return r.ReplaceAllString(str, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
|
|
||||||
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
|
||||||
func StripLow(str string, keepNewLines bool) string {
|
|
||||||
chars := ""
|
|
||||||
if keepNewLines {
|
|
||||||
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
|
|
||||||
} else {
|
|
||||||
chars = "\x00-\x1F\x7F"
|
|
||||||
}
|
|
||||||
return BlackList(str, chars)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplacePattern replace regular expression pattern in string
|
|
||||||
func ReplacePattern(str, pattern, replace string) string {
|
|
||||||
r, _ := regexp.Compile(pattern)
|
|
||||||
return r.ReplaceAllString(str, replace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape replace <, >, & and " with HTML entities.
|
|
||||||
var Escape = html.EscapeString
|
|
||||||
|
|
||||||
func addSegment(inrune, segment []rune) []rune {
|
|
||||||
if len(segment) == 0 {
|
|
||||||
return inrune
|
|
||||||
}
|
|
||||||
if len(inrune) != 0 {
|
|
||||||
inrune = append(inrune, '_')
|
|
||||||
}
|
|
||||||
inrune = append(inrune, segment...)
|
|
||||||
return inrune
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
|
||||||
// Ex.: my_func => MyFunc
|
|
||||||
func UnderscoreToCamelCase(s string) string {
|
|
||||||
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
|
||||||
// Ex.: MyFunc => my_func
|
|
||||||
func CamelCaseToUnderscore(str string) string {
|
|
||||||
var output []rune
|
|
||||||
var segment []rune
|
|
||||||
for _, r := range str {
|
|
||||||
|
|
||||||
// not treat number as separate segment
|
|
||||||
if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) {
|
|
||||||
output = addSegment(output, segment)
|
|
||||||
segment = nil
|
|
||||||
}
|
|
||||||
segment = append(segment, unicode.ToLower(r))
|
|
||||||
}
|
|
||||||
output = addSegment(output, segment)
|
|
||||||
return string(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse return reversed string
|
|
||||||
func Reverse(s string) string {
|
|
||||||
r := []rune(s)
|
|
||||||
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
r[i], r[j] = r[j], r[i]
|
|
||||||
}
|
|
||||||
return string(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLines split string by "\n" and return array of lines
|
|
||||||
func GetLines(s string) []string {
|
|
||||||
return strings.Split(s, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLine return specified line of multiline string
|
|
||||||
func GetLine(s string, index int) (string, error) {
|
|
||||||
lines := GetLines(s)
|
|
||||||
if index < 0 || index >= len(lines) {
|
|
||||||
return "", errors.New("line index out of bounds")
|
|
||||||
}
|
|
||||||
return lines[index], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTags remove all tags from HTML string
|
|
||||||
func RemoveTags(s string) string {
|
|
||||||
return ReplacePattern(s, "<[^>]*>", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SafeFileName return safe string that can be used in file names
|
|
||||||
func SafeFileName(str string) string {
|
|
||||||
name := strings.ToLower(str)
|
|
||||||
name = path.Clean(path.Base(name))
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
separators, err := regexp.Compile(`[ &_=+:]`)
|
|
||||||
if err == nil {
|
|
||||||
name = separators.ReplaceAllString(name, "-")
|
|
||||||
}
|
|
||||||
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
|
|
||||||
if err == nil {
|
|
||||||
name = legal.ReplaceAllString(name, "")
|
|
||||||
}
|
|
||||||
for strings.Contains(name, "--") {
|
|
||||||
name = strings.Replace(name, "--", "-", -1)
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeEmail canonicalize an email address.
|
|
||||||
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
|
||||||
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
|
||||||
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
|
||||||
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
|
||||||
// normalized to @gmail.com.
|
|
||||||
func NormalizeEmail(str string) (string, error) {
|
|
||||||
if !IsEmail(str) {
|
|
||||||
return "", fmt.Errorf("%s is not an email", str)
|
|
||||||
}
|
|
||||||
parts := strings.Split(str, "@")
|
|
||||||
parts[0] = strings.ToLower(parts[0])
|
|
||||||
parts[1] = strings.ToLower(parts[1])
|
|
||||||
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
|
|
||||||
parts[1] = "gmail.com"
|
|
||||||
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
|
|
||||||
}
|
|
||||||
return strings.Join(parts, "@"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Truncate a string to the closest length without breaking words.
|
|
||||||
func Truncate(str string, length int, ending string) string {
|
|
||||||
var aftstr, befstr string
|
|
||||||
if len(str) > length {
|
|
||||||
words := strings.Fields(str)
|
|
||||||
before, present := 0, 0
|
|
||||||
for i := range words {
|
|
||||||
befstr = aftstr
|
|
||||||
before = present
|
|
||||||
aftstr = aftstr + words[i] + " "
|
|
||||||
present = len(aftstr)
|
|
||||||
if present > length && i != 0 {
|
|
||||||
if (length - before) < (present - length) {
|
|
||||||
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
|
||||||
}
|
|
||||||
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
// PadLeft pad left side of string if size of string is less then indicated pad length
|
|
||||||
func PadLeft(str string, padStr string, padLen int) string {
|
|
||||||
return buildPadStr(str, padStr, padLen, true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PadRight pad right side of string if size of string is less then indicated pad length
|
|
||||||
func PadRight(str string, padStr string, padLen int) string {
|
|
||||||
return buildPadStr(str, padStr, padLen, false, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PadBoth pad sides of string if size of string is less then indicated pad length
|
|
||||||
func PadBoth(str string, padStr string, padLen int) string {
|
|
||||||
return buildPadStr(str, padStr, padLen, true, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PadString either left, right or both sides, not the padding string can be unicode and more then one
|
|
||||||
// character
|
|
||||||
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
|
|
||||||
|
|
||||||
// When padded length is less then the current string size
|
|
||||||
if padLen < utf8.RuneCountInString(str) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
padLen -= utf8.RuneCountInString(str)
|
|
||||||
|
|
||||||
targetLen := padLen
|
|
||||||
|
|
||||||
targetLenLeft := targetLen
|
|
||||||
targetLenRight := targetLen
|
|
||||||
if padLeft && padRight {
|
|
||||||
targetLenLeft = padLen / 2
|
|
||||||
targetLenRight = padLen - targetLenLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
strToRepeatLen := utf8.RuneCountInString(padStr)
|
|
||||||
|
|
||||||
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
|
|
||||||
repeatedString := strings.Repeat(padStr, repeatTimes)
|
|
||||||
|
|
||||||
leftSide := ""
|
|
||||||
if padLeft {
|
|
||||||
leftSide = repeatedString[0:targetLenLeft]
|
|
||||||
}
|
|
||||||
|
|
||||||
rightSide := ""
|
|
||||||
if padRight {
|
|
||||||
rightSide = repeatedString[0:targetLenRight]
|
|
||||||
}
|
|
||||||
|
|
||||||
return leftSide + str + rightSide
|
|
||||||
}
|
|
||||||
17
vendor/github.com/asaskevich/govalidator/utils_benchmark_test.go
generated
vendored
17
vendor/github.com/asaskevich/govalidator/utils_benchmark_test.go
generated
vendored
@@ -1,17 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkContains(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
Contains("a0b01c012deffghijklmnopqrstu0123456vwxyz", "0123456789")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMatches(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
Matches("alfkjl12309fdjldfsa209jlksdfjLAKJjs9uJH234", "[\\w\\d]+")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
502
vendor/github.com/asaskevich/govalidator/utils_test.go
generated
vendored
502
vendor/github.com/asaskevich/govalidator/utils_test.go
generated
vendored
@@ -1,502 +0,0 @@
|
|||||||
package govalidator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestContains(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{"abacada", "", true},
|
|
||||||
{"abacada", "ritir", false},
|
|
||||||
{"abacada", "a", true},
|
|
||||||
{"abacada", "aca", true},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Contains(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Contains(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatches(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{"123456789", "[0-9]+", true},
|
|
||||||
{"abacada", "cab$", false},
|
|
||||||
{"111222333", "((111|222|333)+)+", true},
|
|
||||||
{"abacaba", "((123+]", false},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Matches(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Matches(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLeftTrim(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{" \r\n\tfoo \r\n\t ", "", "foo \r\n\t "},
|
|
||||||
{"010100201000", "01", "201000"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := LeftTrim(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected LeftTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRightTrim(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{" \r\n\tfoo \r\n\t ", "", " \r\n\tfoo"},
|
|
||||||
{"010100201000", "01", "0101002"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := RightTrim(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected RightTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTrim(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{" \r\n\tfoo \r\n\t ", "", "foo"},
|
|
||||||
{"010100201000", "01", "2"},
|
|
||||||
{"1234567890987654321", "1-8", "909"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Trim(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Trim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This small example illustrate how to work with Trim function.
|
|
||||||
func ExampleTrim() {
|
|
||||||
// Remove from left and right spaces and "\r", "\n", "\t" characters
|
|
||||||
println(Trim(" \r\r\ntext\r \t\n", "") == "text")
|
|
||||||
// Remove from left and right characters that are between "1" and "8".
|
|
||||||
// "1-8" is like full list "12345678".
|
|
||||||
println(Trim("1234567890987654321", "1-8") == "909")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWhiteList(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"abcdef", "abc", "abc"},
|
|
||||||
{"aaaaaaaaaabbbbbbbbbb", "abc", "aaaaaaaaaabbbbbbbbbb"},
|
|
||||||
{"a1b2c3", "abc", "abc"},
|
|
||||||
{" ", "abc", ""},
|
|
||||||
{"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "aaaaaaaaaaaa"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := WhiteList(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected WhiteList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This small example illustrate how to work with WhiteList function.
|
|
||||||
func ExampleWhiteList() {
|
|
||||||
// Remove all characters from string ignoring characters between "a" and "z"
|
|
||||||
println(WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlackList(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"abcdef", "abc", "def"},
|
|
||||||
{"aaaaaaaaaabbbbbbbbbb", "abc", ""},
|
|
||||||
{"a1b2c3", "abc", "123"},
|
|
||||||
{" ", "abc", " "},
|
|
||||||
{"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "34354322345434"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := BlackList(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected BlackList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStripLow(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 bool
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"foo\x00", false, "foo"},
|
|
||||||
{"\x7Ffoo\x02", false, "foo"},
|
|
||||||
{"\x01\x09", false, ""},
|
|
||||||
{"foo\x0A\x0D", false, "foo"},
|
|
||||||
{"perch\u00e9", false, "perch\u00e9"},
|
|
||||||
{"\u20ac", false, "\u20ac"},
|
|
||||||
{"\u2206\x0A", false, "\u2206"},
|
|
||||||
{"foo\x0A\x0D", true, "foo\x0A\x0D"},
|
|
||||||
{"\x03foo\x0A\x0D", true, "foo\x0A\x0D"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := StripLow(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected StripLow(%q,%t) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplacePattern(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
param3 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"ab123ba", "[0-9]+", "aca", "abacaba"},
|
|
||||||
{"abacaba", "[0-9]+", "aca", "abacaba"},
|
|
||||||
{"httpftp://github.comio", "(ftp|io)", "", "http://github.com"},
|
|
||||||
{"aaaaaaaaaa", "a", "", ""},
|
|
||||||
{"http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "", "http://github.com"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := ReplacePattern(test.param1, test.param2, test.param3)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected ReplacePattern(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This small example illustrate how to work with ReplacePattern function.
|
|
||||||
func ExampleReplacePattern() {
|
|
||||||
// Replace in "http123123ftp://git534543hub.comio" following (pattern "(ftp|io|[0-9]+)"):
|
|
||||||
// - Sequence "ftp".
|
|
||||||
// - Sequence "io".
|
|
||||||
// - Sequence of digits.
|
|
||||||
// with empty string.
|
|
||||||
println(ReplacePattern("http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "") == "http://github.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEscape(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{`<img alt="foo&bar">`, "<img alt="foo&bar">"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Escape(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Escape(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnderscoreToCamelCase(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"a_b_c", "ABC"},
|
|
||||||
{"my_func", "MyFunc"},
|
|
||||||
{"1ab_cd", "1abCd"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := UnderscoreToCamelCase(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected UnderscoreToCamelCase(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCamelCaseToUnderscore(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"MyFunc", "my_func"},
|
|
||||||
{"ABC", "a_b_c"},
|
|
||||||
{"1B", "1_b"},
|
|
||||||
{"foo_bar", "foo_bar"},
|
|
||||||
{"FooV2Bar", "foo_v2_bar"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := CamelCaseToUnderscore(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected CamelCaseToUnderscore(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReverse(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"abc", "cba"},
|
|
||||||
{"カタカナ", "ナカタカ"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Reverse(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Reverse(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetLines(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{"abc", []string{"abc"}},
|
|
||||||
{"a\nb\nc", []string{"a", "b", "c"}},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := GetLines(test.param)
|
|
||||||
if !reflect.DeepEqual(actual, test.expected) {
|
|
||||||
t.Errorf("Expected GetLines(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetLine(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 int
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"abc", 0, "abc"},
|
|
||||||
{"a\nb\nc", 0, "a"},
|
|
||||||
{"abc", -1, ""},
|
|
||||||
{"abacaba\n", 1, ""},
|
|
||||||
{"abc", 3, ""},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual, _ := GetLine(test.param1, test.param2)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected GetLine(%q, %d) to be %v, got %v", test.param1, test.param2, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveTags(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"abc", "abc"},
|
|
||||||
{"<!-- Test -->", ""},
|
|
||||||
{"<div><div><p><a>Text</a></p></div></div>", "Text"},
|
|
||||||
{`<a href="#">Link</a>`, "Link"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := RemoveTags(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected RemoveTags(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSafeFileName(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"abc", "abc"},
|
|
||||||
{"123456789 '_-?ASDF@£$%£%^é.html", "123456789-asdf.html"},
|
|
||||||
{"ReadMe.md", "readme.md"},
|
|
||||||
{"file:///c:/test.go", "test.go"},
|
|
||||||
{"../../../Hello World!.txt", "hello-world.txt"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := SafeFileName(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected SafeFileName(%q) to be %v, got %v", test.param, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeEmail(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{`test@me.com`, `test@me.com`},
|
|
||||||
{`some.name@gmail.com`, `somename@gmail.com`},
|
|
||||||
{`some.name@googlemail.com`, `somename@gmail.com`},
|
|
||||||
{`some.name+extension@gmail.com`, `somename@gmail.com`},
|
|
||||||
{`some.name+extension@googlemail.com`, `somename@gmail.com`},
|
|
||||||
{`some.name.middlename+extension@gmail.com`, `somenamemiddlename@gmail.com`},
|
|
||||||
{`some.name.middlename+extension@googlemail.com`, `somenamemiddlename@gmail.com`},
|
|
||||||
{`some.name.midd.lena.me.+extension@gmail.com`, `somenamemiddlename@gmail.com`},
|
|
||||||
{`some.name.midd.lena.me.+extension@googlemail.com`, `somenamemiddlename@gmail.com`},
|
|
||||||
{`some.name+extension@unknown.com`, `some.name+extension@unknown.com`},
|
|
||||||
// TODO: {`hans@m端ller.com`, `hans@m端ller.com`},
|
|
||||||
{`hans`, ``},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual, err := NormalizeEmail(test.param)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected NormalizeEmail(%q) to be %v, got %v, err %v", test.param, test.expected, actual, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTruncate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 int
|
|
||||||
param3 string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`, 25, `...`, `Lorem ipsum dolor sit amet...`},
|
|
||||||
{`Measuring programming progress by lines of code is like measuring aircraft building progress by weight.`, 35, ` new born babies!`, `Measuring programming progress by new born babies!`},
|
|
||||||
{`Testestestestestestestestestest testestestestestestestestest`, 7, `...`, `Testestestestestestestestestest...`},
|
|
||||||
{`Testing`, 7, `...`, `Testing`},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := Truncate(test.param1, test.param2, test.param3)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected Truncate(%q, %d, %q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPadLeft(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
param3 int
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"こんにちは", "xyz", 12, "xyzxyzxこんにちは"},
|
|
||||||
{"こんにちは", "xyz", 11, "xyzxyzこんにちは"},
|
|
||||||
{"abc", "x", 5, "xxabc"},
|
|
||||||
{"abc", "xyz", 5, "xyabc"},
|
|
||||||
{"abcde", "xyz", 5, "abcde"},
|
|
||||||
{"abcde", "xyz", 4, "abcde"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := PadLeft(test.param1, test.param2, test.param3)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected PadLeft(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPadRight(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
param3 int
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"こんにちは", "xyz", 12, "こんにちはxyzxyzx"},
|
|
||||||
{"こんにちは", "xyz", 11, "こんにちはxyzxyz"},
|
|
||||||
{"abc", "x", 5, "abcxx"},
|
|
||||||
{"abc", "xyz", 5, "abcxy"},
|
|
||||||
{"abcde", "xyz", 5, "abcde"},
|
|
||||||
{"abcde", "xyz", 4, "abcde"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := PadRight(test.param1, test.param2, test.param3)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected PadRight(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPadBoth(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
param1 string
|
|
||||||
param2 string
|
|
||||||
param3 int
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"こんにちは", "xyz", 12, "xyzこんにちはxyzx"},
|
|
||||||
{"こんにちは", "xyz", 11, "xyzこんにちはxyz"},
|
|
||||||
{"abc", "x", 5, "xabcx"},
|
|
||||||
{"abc", "xyz", 5, "xabcx"},
|
|
||||||
{"abcde", "xyz", 5, "abcde"},
|
|
||||||
{"abcde", "xyz", 4, "abcde"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
actual := PadBoth(test.param1, test.param2, test.param3)
|
|
||||||
if actual != test.expected {
|
|
||||||
t.Errorf("Expected PadBoth(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1213
vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
1213
vendor/github.com/asaskevich/govalidator/validator.go
generated
vendored
File diff suppressed because it is too large
Load Diff
3216
vendor/github.com/asaskevich/govalidator/validator_test.go
generated
vendored
3216
vendor/github.com/asaskevich/govalidator/validator_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
15
vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
15
vendor/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
@@ -1,15 +0,0 @@
|
|||||||
box: golang
|
|
||||||
build:
|
|
||||||
steps:
|
|
||||||
- setup-go-workspace
|
|
||||||
|
|
||||||
- script:
|
|
||||||
name: go get
|
|
||||||
code: |
|
|
||||||
go version
|
|
||||||
go get -t ./...
|
|
||||||
|
|
||||||
- script:
|
|
||||||
name: go test
|
|
||||||
code: |
|
|
||||||
go test -race ./...
|
|
||||||
2
vendor/github.com/containerd/continuity/.travis.yml
generated
vendored
2
vendor/github.com/containerd/continuity/.travis.yml
generated
vendored
@@ -4,6 +4,8 @@ sudo: required
|
|||||||
go:
|
go:
|
||||||
- 1.8.x
|
- 1.8.x
|
||||||
- 1.9.x
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
- 1.x
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
go_import_path: github.com/containerd/continuity
|
go_import_path: github.com/containerd/continuity
|
||||||
|
|||||||
4
vendor/github.com/containerd/continuity/fs/du.go
generated
vendored
4
vendor/github.com/containerd/continuity/fs/du.go
generated
vendored
@@ -10,8 +10,8 @@ type Usage struct {
|
|||||||
|
|
||||||
// DiskUsage counts the number of inodes and disk usage for the resources under
|
// DiskUsage counts the number of inodes and disk usage for the resources under
|
||||||
// path.
|
// path.
|
||||||
func DiskUsage(roots ...string) (Usage, error) {
|
func DiskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
||||||
return diskUsage(roots...)
|
return diskUsage(ctx, roots...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiffUsage counts the numbers of inodes and disk usage in the
|
// DiffUsage counts the numbers of inodes and disk usage in the
|
||||||
|
|||||||
8
vendor/github.com/containerd/continuity/fs/du_unix.go
generated
vendored
8
vendor/github.com/containerd/continuity/fs/du_unix.go
generated
vendored
@@ -24,7 +24,7 @@ func newInode(stat *syscall.Stat_t) inode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func diskUsage(roots ...string) (Usage, error) {
|
func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
size int64
|
size int64
|
||||||
@@ -37,6 +37,12 @@ func diskUsage(roots ...string) (Usage, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
|
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
|
||||||
if _, ok := inodes[inoKey]; !ok {
|
if _, ok := inodes[inoKey]; !ok {
|
||||||
inodes[inoKey] = struct{}{}
|
inodes[inoKey] = struct{}{}
|
||||||
|
|||||||
8
vendor/github.com/containerd/continuity/fs/du_windows.go
generated
vendored
8
vendor/github.com/containerd/continuity/fs/du_windows.go
generated
vendored
@@ -8,7 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func diskUsage(roots ...string) (Usage, error) {
|
func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
||||||
var (
|
var (
|
||||||
size int64
|
size int64
|
||||||
)
|
)
|
||||||
@@ -21,6 +21,12 @@ func diskUsage(roots ...string) (Usage, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
size += fi.Size()
|
size += fi.Size()
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
|||||||
5
vendor/github.com/containerd/continuity/fs/fstest/compare_unix.go
generated
vendored
Normal file
5
vendor/github.com/containerd/continuity/fs/fstest/compare_unix.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package fstest
|
||||||
|
|
||||||
|
var metadataFiles map[string]bool
|
||||||
7
vendor/github.com/containerd/continuity/fs/fstest/compare_windows.go
generated
vendored
Normal file
7
vendor/github.com/containerd/continuity/fs/fstest/compare_windows.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package fstest
|
||||||
|
|
||||||
|
// TODO: Any more metadata files generated by Windows layers?
|
||||||
|
var metadataFiles = map[string]bool{
|
||||||
|
"\\System Volume Information": true,
|
||||||
|
"\\WcSandboxState": true,
|
||||||
|
}
|
||||||
10
vendor/github.com/containerd/continuity/syscallx/syscall_unix.go
generated
vendored
Normal file
10
vendor/github.com/containerd/continuity/syscallx/syscall_unix.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package syscallx
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// Readlink returns the destination of the named symbolic link.
|
||||||
|
func Readlink(path string, buf []byte) (n int, err error) {
|
||||||
|
return syscall.Readlink(path, buf)
|
||||||
|
}
|
||||||
96
vendor/github.com/containerd/continuity/syscallx/syscall_windows.go
generated
vendored
Normal file
96
vendor/github.com/containerd/continuity/syscallx/syscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package syscallx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reparseDataBuffer struct {
|
||||||
|
ReparseTag uint32
|
||||||
|
ReparseDataLength uint16
|
||||||
|
Reserved uint16
|
||||||
|
|
||||||
|
// GenericReparseBuffer
|
||||||
|
reparseBuffer byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type mountPointReparseBuffer struct {
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
PathBuffer [1]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type symbolicLinkReparseBuffer struct {
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
Flags uint32
|
||||||
|
PathBuffer [1]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
|
||||||
|
_SYMLINK_FLAG_RELATIVE = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Readlink returns the destination of the named symbolic link.
|
||||||
|
func Readlink(path string, buf []byte) (n int, err error) {
|
||||||
|
fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
|
||||||
|
syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(fd)
|
||||||
|
|
||||||
|
rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
|
||||||
|
var bytesReturned uint32
|
||||||
|
err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
|
||||||
|
var s string
|
||||||
|
switch rdb.ReparseTag {
|
||||||
|
case syscall.IO_REPARSE_TAG_SYMLINK:
|
||||||
|
data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
|
||||||
|
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
|
||||||
|
s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
|
||||||
|
if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
|
||||||
|
if len(s) >= 4 && s[:4] == `\??\` {
|
||||||
|
s = s[4:]
|
||||||
|
switch {
|
||||||
|
case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
|
||||||
|
// do nothing
|
||||||
|
case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
|
||||||
|
s = `\\` + s[4:]
|
||||||
|
default:
|
||||||
|
// unexpected; do nothing
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unexpected; do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _IO_REPARSE_TAG_MOUNT_POINT:
|
||||||
|
data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
|
||||||
|
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
|
||||||
|
s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
|
||||||
|
if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
|
||||||
|
if len(s) < 48 || s[:11] != `\??\Volume{` {
|
||||||
|
s = s[4:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unexpected; do nothing
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// the path is not a symlink or junction but another type of reparse
|
||||||
|
// point
|
||||||
|
return -1, syscall.ENOENT
|
||||||
|
}
|
||||||
|
n = copy(buf, []byte(s))
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
112
vendor/github.com/containerd/continuity/sysx/file_posix.go
generated
vendored
Normal file
112
vendor/github.com/containerd/continuity/sysx/file_posix.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containerd/continuity/syscallx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Readlink returns the destination of the named symbolic link.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func Readlink(name string) (string, error) {
|
||||||
|
for len := 128; ; len *= 2 {
|
||||||
|
b := make([]byte, len)
|
||||||
|
n, e := fixCount(syscallx.Readlink(fixLongPath(name), b))
|
||||||
|
if e != nil {
|
||||||
|
return "", &os.PathError{Op: "readlink", Path: name, Err: e}
|
||||||
|
}
|
||||||
|
if n < len {
|
||||||
|
return string(b[0:n]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Many functions in package syscall return a count of -1 instead of 0.
|
||||||
|
// Using fixCount(call()) instead of call() corrects the count.
|
||||||
|
func fixCount(n int, err error) (int, error) {
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixLongPath returns the extended-length (\\?\-prefixed) form of
|
||||||
|
// path when needed, in order to avoid the default 260 character file
|
||||||
|
// path limit imposed by Windows. If path is not easily converted to
|
||||||
|
// the extended-length form (for example, if path is a relative path
|
||||||
|
// or contains .. elements), or is short enough, fixLongPath returns
|
||||||
|
// path unmodified.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||||
|
func fixLongPath(path string) string {
|
||||||
|
// Do nothing (and don't allocate) if the path is "short".
|
||||||
|
// Empirically (at least on the Windows Server 2013 builder),
|
||||||
|
// the kernel is arbitrarily okay with < 248 bytes. That
|
||||||
|
// matches what the docs above say:
|
||||||
|
// "When using an API to create a directory, the specified
|
||||||
|
// path cannot be so long that you cannot append an 8.3 file
|
||||||
|
// name (that is, the directory name cannot exceed MAX_PATH
|
||||||
|
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
|
||||||
|
//
|
||||||
|
// The MSDN docs appear to say that a normal path that is 248 bytes long
|
||||||
|
// will work; empirically the path must be less then 248 bytes long.
|
||||||
|
if len(path) < 248 {
|
||||||
|
// Don't fix. (This is how Go 1.7 and earlier worked,
|
||||||
|
// not automatically generating the \\?\ form)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// The extended form begins with \\?\, as in
|
||||||
|
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
|
||||||
|
// The extended form disables evaluation of . and .. path
|
||||||
|
// elements and disables the interpretation of / as equivalent
|
||||||
|
// to \. The conversion here rewrites / to \ and elides
|
||||||
|
// . elements as well as trailing or duplicate separators. For
|
||||||
|
// simplicity it avoids the conversion entirely for relative
|
||||||
|
// paths or paths containing .. elements. For now,
|
||||||
|
// \\server\share paths are not converted to
|
||||||
|
// \\?\UNC\server\share paths because the rules for doing so
|
||||||
|
// are less well-specified.
|
||||||
|
if len(path) >= 2 && path[:2] == `\\` {
|
||||||
|
// Don't canonicalize UNC paths.
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
// Relative path
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = `\\?`
|
||||||
|
|
||||||
|
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
|
||||||
|
copy(pathbuf, prefix)
|
||||||
|
n := len(path)
|
||||||
|
r, w := 0, len(prefix)
|
||||||
|
for r < n {
|
||||||
|
switch {
|
||||||
|
case os.IsPathSeparator(path[r]):
|
||||||
|
// empty block
|
||||||
|
r++
|
||||||
|
case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
|
||||||
|
// /./
|
||||||
|
r++
|
||||||
|
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
|
||||||
|
// /../ is currently unhandled
|
||||||
|
return path
|
||||||
|
default:
|
||||||
|
pathbuf[w] = '\\'
|
||||||
|
w++
|
||||||
|
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
|
||||||
|
pathbuf[w] = path[r]
|
||||||
|
w++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A drive's root directory needs a trailing \
|
||||||
|
if w == len(`\\?\c:`) {
|
||||||
|
pathbuf[w] = '\\'
|
||||||
|
w++
|
||||||
|
}
|
||||||
|
return string(pathbuf[:w])
|
||||||
|
}
|
||||||
33
vendor/github.com/docker/go-units/MAINTAINERS
generated
vendored
33
vendor/github.com/docker/go-units/MAINTAINERS
generated
vendored
@@ -1,6 +1,6 @@
|
|||||||
# go-connections maintainers file
|
# go-units maintainers file
|
||||||
#
|
#
|
||||||
# This file describes who runs the docker/go-connections project and how.
|
# This file describes who runs the docker/go-units project and how.
|
||||||
# This is a living document - if you see something out of date or missing, speak up!
|
# This is a living document - if you see something out of date or missing, speak up!
|
||||||
#
|
#
|
||||||
# It is structured to be consumable by both humans and programs.
|
# It is structured to be consumable by both humans and programs.
|
||||||
@@ -11,7 +11,10 @@
|
|||||||
[Org]
|
[Org]
|
||||||
[Org."Core maintainers"]
|
[Org."Core maintainers"]
|
||||||
people = [
|
people = [
|
||||||
"calavera",
|
"akihirosuda",
|
||||||
|
"dnephin",
|
||||||
|
"thajeztah",
|
||||||
|
"vdemeester",
|
||||||
]
|
]
|
||||||
|
|
||||||
[people]
|
[people]
|
||||||
@@ -21,7 +24,23 @@
|
|||||||
# in the people section.
|
# in the people section.
|
||||||
|
|
||||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||||
[people.calavera]
|
|
||||||
Name = "David Calavera"
|
[people.akihirosuda]
|
||||||
Email = "david.calavera@gmail.com"
|
Name = "Akihiro Suda"
|
||||||
GitHub = "calavera"
|
Email = "suda.akihiro@lab.ntt.co.jp"
|
||||||
|
GitHub = "AkihiroSuda"
|
||||||
|
|
||||||
|
[people.dnephin]
|
||||||
|
Name = "Daniel Nephin"
|
||||||
|
Email = "dnephin@gmail.com"
|
||||||
|
GitHub = "dnephin"
|
||||||
|
|
||||||
|
[people.thajeztah]
|
||||||
|
Name = "Sebastiaan van Stijn"
|
||||||
|
Email = "github@gone.nl"
|
||||||
|
GitHub = "thaJeztah"
|
||||||
|
|
||||||
|
[people.vdemeester]
|
||||||
|
Name = "Vincent Demeester"
|
||||||
|
Email = "vincent@sbr.pm"
|
||||||
|
GitHub = "vdemeester"
|
||||||
2
vendor/github.com/docker/go-units/size.go
generated
vendored
2
vendor/github.com/docker/go-units/size.go
generated
vendored
@@ -31,7 +31,7 @@ type unitMap map[string]int64
|
|||||||
var (
|
var (
|
||||||
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
||||||
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
||||||
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
|
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||||
|
|||||||
2
vendor/github.com/docker/go-units/size_test.go
generated
vendored
2
vendor/github.com/docker/go-units/size_test.go
generated
vendored
@@ -116,6 +116,8 @@ func TestRAMInBytes(t *testing.T) {
|
|||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K")
|
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K")
|
||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb")
|
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb")
|
||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb")
|
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb")
|
||||||
|
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kib")
|
||||||
|
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32KIB")
|
||||||
assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb")
|
assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb")
|
||||||
assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb")
|
assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb")
|
||||||
assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb")
|
assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb")
|
||||||
|
|||||||
2
vendor/github.com/fnproject/fdk-go/README.md
generated
vendored
2
vendor/github.com/fnproject/fdk-go/README.md
generated
vendored
@@ -52,7 +52,7 @@ func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if fnctx.Config["FN_METHOD"] != "PUT" {
|
if fnctx.Method != "PUT" {
|
||||||
fdk.WriteStatus(out, 404)
|
fdk.WriteStatus(out, 404)
|
||||||
fdk.SetHeader(out, "Content-Type", "application/json")
|
fdk.SetHeader(out, "Content-Type", "application/json")
|
||||||
io.WriteString(out, `{"error":"route not found"}`)
|
io.WriteString(out, `{"error":"route not found"}`)
|
||||||
|
|||||||
18
vendor/github.com/fnproject/fdk-go/fdk.go
generated
vendored
18
vendor/github.com/fnproject/fdk-go/fdk.go
generated
vendored
@@ -24,23 +24,29 @@ func (f HandlerFunc) Serve(ctx context.Context, in io.Reader, out io.Writer) {
|
|||||||
func Context(ctx context.Context) *Ctx {
|
func Context(ctx context.Context) *Ctx {
|
||||||
utilsCtx := utils.Context(ctx)
|
utilsCtx := utils.Context(ctx)
|
||||||
return &Ctx{
|
return &Ctx{
|
||||||
Header: utilsCtx.Header,
|
Header: utilsCtx.Header,
|
||||||
Config: utilsCtx.Config,
|
Config: utilsCtx.Config,
|
||||||
|
RequestURL: utilsCtx.RequestURL,
|
||||||
|
Method: utilsCtx.Method,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithContext(ctx context.Context, fnctx *Ctx) context.Context {
|
func WithContext(ctx context.Context, fnctx *Ctx) context.Context {
|
||||||
utilsCtx := &utils.Ctx{
|
utilsCtx := &utils.Ctx{
|
||||||
Header: fnctx.Header,
|
Header: fnctx.Header,
|
||||||
Config: fnctx.Config,
|
Config: fnctx.Config,
|
||||||
|
RequestURL: fnctx.RequestURL,
|
||||||
|
Method: fnctx.Method,
|
||||||
}
|
}
|
||||||
return utils.WithContext(ctx, utilsCtx)
|
return utils.WithContext(ctx, utilsCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ctx provides access to Config and Headers from fn.
|
// Ctx provides access to Config and Headers from fn.
|
||||||
type Ctx struct {
|
type Ctx struct {
|
||||||
Header http.Header
|
Header http.Header
|
||||||
Config map[string]string
|
Config map[string]string
|
||||||
|
RequestURL string
|
||||||
|
Method string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHeader will add a header on the function response, for hot function
|
// AddHeader will add a header on the function response, for hot function
|
||||||
|
|||||||
84
vendor/github.com/fnproject/fdk-go/fdk_test.go
generated
vendored
84
vendor/github.com/fnproject/fdk-go/fdk_test.go
generated
vendored
@@ -15,6 +15,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fnproject/fdk-go/utils"
|
"github.com/fnproject/fdk-go/utils"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func echoHTTPHandler(_ context.Context, in io.Reader, out io.Writer) {
|
func echoHTTPHandler(_ context.Context, in io.Reader, out io.Writer) {
|
||||||
@@ -55,12 +56,12 @@ func JSONHandler(_ context.Context, in io.Reader, out io.Writer) {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(in).Decode(&person)
|
json.NewDecoder(in).Decode(&person)
|
||||||
|
|
||||||
if person.Name == "" {
|
if person.Name == "" {
|
||||||
person.Name = "world"
|
person.Name = "world"
|
||||||
}
|
}
|
||||||
|
|
||||||
body := fmt.Sprintf("Hello %s!\n", person.Name)
|
body := fmt.Sprintf("Hello %s!\n", person.Name)
|
||||||
|
SetHeader(out, "Content-Type", "application/json")
|
||||||
err := json.NewEncoder(out).Encode(body)
|
err := json.NewEncoder(out).Encode(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
@@ -80,8 +81,9 @@ func TestJSON(t *testing.T) {
|
|||||||
Deadline: "2018-01-30T16:52:39.786Z",
|
Deadline: "2018-01-30T16:52:39.786Z",
|
||||||
Protocol: utils.CallRequestHTTP{
|
Protocol: utils.CallRequestHTTP{
|
||||||
Type: "http",
|
Type: "http",
|
||||||
RequestURL: "someURL",
|
RequestURL: "http://localhost:8080/r/myapp/yodawg",
|
||||||
Headers: http.Header{},
|
Headers: http.Header{},
|
||||||
|
Method: "POST",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +153,9 @@ func TestJSONOverwriteStatusCodeAndHeaders(t *testing.T) {
|
|||||||
Deadline: "2018-01-30T16:52:39.786Z",
|
Deadline: "2018-01-30T16:52:39.786Z",
|
||||||
Protocol: utils.CallRequestHTTP{
|
Protocol: utils.CallRequestHTTP{
|
||||||
Type: "json",
|
Type: "json",
|
||||||
RequestURL: "someURL",
|
RequestURL: "http://localhost:8080/r/myapp/yodawg",
|
||||||
Headers: http.Header{},
|
Headers: http.Header{},
|
||||||
|
Method: "POST",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,3 +245,78 @@ func HTTPreq(t *testing.T, bod string) io.Reader {
|
|||||||
}
|
}
|
||||||
return bytes.NewReader(byts)
|
return bytes.NewReader(byts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupTestFromRequest(t *testing.T, data interface{}, contentType, nameTest string) {
|
||||||
|
req := &utils.CloudEventIn{
|
||||||
|
CloudEvent: utils.CloudEvent{
|
||||||
|
EventID: "someid",
|
||||||
|
Source: "fn-api",
|
||||||
|
EventType: "test-type",
|
||||||
|
EventTypeVersion: "1.0",
|
||||||
|
EventTime: time.Now(),
|
||||||
|
ContentType: contentType,
|
||||||
|
Data: data,
|
||||||
|
},
|
||||||
|
Extensions: utils.CloudEventInExtension{
|
||||||
|
Deadline: "2018-01-30T16:52:39.786Z",
|
||||||
|
Protocol: utils.CallRequestHTTP{
|
||||||
|
Type: "http",
|
||||||
|
RequestURL: "http://localhost:8080/r/myapp/yodawg",
|
||||||
|
Headers: http.Header{},
|
||||||
|
Method: "POST",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var in bytes.Buffer
|
||||||
|
err := json.NewEncoder(&in).Encode(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Unable to marshal request")
|
||||||
|
}
|
||||||
|
t.Log(in.String())
|
||||||
|
var out, buf bytes.Buffer
|
||||||
|
|
||||||
|
err = utils.DoCloudEventOnce(HandlerFunc(JSONHandler), utils.BuildCtx(),
|
||||||
|
&in, &out, &buf, make(http.Header))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("should not return error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(out.String())
|
||||||
|
ceOut := &utils.CloudEventOut{}
|
||||||
|
err = json.NewDecoder(&out).Decode(ceOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ceOut.Extensions.Protocol.StatusCode != 200 {
|
||||||
|
t.Fatalf("Response code must equal to 200, but have: %v", ceOut.Extensions.Protocol.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var respData string
|
||||||
|
json.Unmarshal([]byte(ceOut.Data.(string)), &respData)
|
||||||
|
|
||||||
|
if respData != "Hello "+nameTest+"!\n" {
|
||||||
|
t.Fatalf("Output assertion mismatch. Expected: `Hello %v!\n`. Actual: %v", nameTest, ceOut.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudEventWithJSONData(t *testing.T) {
|
||||||
|
data := map[string]string{
|
||||||
|
"name": "John",
|
||||||
|
}
|
||||||
|
contentType := "application/json"
|
||||||
|
setupTestFromRequest(t, data, contentType, "John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudEventWithStringData(t *testing.T) {
|
||||||
|
data := `{"name":"John"}`
|
||||||
|
contentType := "text/plain"
|
||||||
|
setupTestFromRequest(t, data, contentType, "John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudEventWithPerfectlyValidJSONValue(t *testing.T) {
|
||||||
|
// https://tools.ietf.org/html/rfc7159#section-3
|
||||||
|
data := false
|
||||||
|
contentType := "application/json"
|
||||||
|
setupTestFromRequest(t, data, contentType, "world")
|
||||||
|
}
|
||||||
|
|||||||
117
vendor/github.com/fnproject/fdk-go/utils/cloudevent.go
generated
vendored
Normal file
117
vendor/github.com/fnproject/fdk-go/utils/cloudevent.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CloudEvent struct {
|
||||||
|
CloudEventsVersion string `json:"cloudEventsVersion"`
|
||||||
|
EventID string `json:"eventID"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
EventType string `json:"eventType"`
|
||||||
|
EventTypeVersion string `json:"eventTypeVersion"`
|
||||||
|
EventTime time.Time `json:"eventTime"`
|
||||||
|
SchemaURL string `json:"schemaURL"`
|
||||||
|
ContentType string `json:"contentType"`
|
||||||
|
Data interface{} `json:"data"` // from docs: the payload is encoded into a media format which is specified by the contentType attribute (e.g. application/json)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudEventInExtension struct {
|
||||||
|
Protocol CallRequestHTTP `json:"protocol"`
|
||||||
|
Deadline string `json:"deadline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudEventOutExtension struct {
|
||||||
|
Protocol CallResponseHTTP `json:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudEventIn struct {
|
||||||
|
CloudEvent
|
||||||
|
Extensions CloudEventInExtension `json:"extensions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudEventOut struct {
|
||||||
|
CloudEvent
|
||||||
|
Extensions CloudEventOutExtension `json:"extensions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeError(ceOut *CloudEventOut, err error) {
|
||||||
|
ceOut.Extensions.Protocol.StatusCode = http.StatusInternalServerError
|
||||||
|
ceOut.Data = fmt.Sprintf(`{"error": %v}`, err.Error())
|
||||||
|
ceOut.ContentType = "application/json"
|
||||||
|
ceOut.EventTime = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoCloudEventOnce(handler Handler, ctx context.Context, in io.Reader, out io.Writer, buf *bytes.Buffer, hdr http.Header) error {
|
||||||
|
buf.Reset()
|
||||||
|
ResetHeaders(hdr)
|
||||||
|
resp := Response{
|
||||||
|
Writer: buf,
|
||||||
|
Status: 200,
|
||||||
|
Header: hdr,
|
||||||
|
}
|
||||||
|
|
||||||
|
ceOut := CloudEventOut{
|
||||||
|
Extensions: CloudEventOutExtension{
|
||||||
|
Protocol: CallResponseHTTP{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Headers: hdr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CloudEvent: CloudEvent{
|
||||||
|
ContentType: "text/plain",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var ceIn CloudEventIn
|
||||||
|
err := json.NewDecoder(in).Decode(&ceIn)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
writeError(&ceOut, err)
|
||||||
|
} else {
|
||||||
|
SetHeaders(ctx, ceIn.Extensions.Protocol.Headers)
|
||||||
|
SetRequestURL(ctx, ceIn.Extensions.Protocol.RequestURL)
|
||||||
|
SetMethod(ctx, ceIn.Extensions.Protocol.Method)
|
||||||
|
ctx, cancel := CtxWithDeadline(ctx, ceIn.Extensions.Deadline)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if ceIn.ContentType == "application/json" {
|
||||||
|
// TODO this is lame, need to make FDK cloud event native and not io.Reader
|
||||||
|
err = json.NewEncoder(buf).Encode(ceIn.Data)
|
||||||
|
in := strings.NewReader(buf.String()) // string is immutable, we need a copy
|
||||||
|
buf.Reset()
|
||||||
|
handler.Serve(ctx, in, &resp)
|
||||||
|
} else {
|
||||||
|
handler.Serve(ctx, strings.NewReader(ceIn.Data.(string)), &resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ceOut.EventID = ceIn.EventID
|
||||||
|
ceOut.EventTime = time.Now()
|
||||||
|
ceOut.ContentType = ceOut.Extensions.Protocol.Headers.Get("Content-Type")
|
||||||
|
ceOut.Data = buf.String()
|
||||||
|
return json.NewEncoder(out).Encode(ceOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoCloudEvent(handler Handler, ctx context.Context, in io.Reader, out io.Writer) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
hdr := make(http.Header)
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := DoCloudEventOnce(handler, ctx, in, out, &buf, hdr)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
vendor/github.com/fnproject/fdk-go/utils/http.go
generated
vendored
Normal file
62
vendor/github.com/fnproject/fdk-go/utils/http.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHTTPResp(buf *bytes.Buffer, fnResp *Response, req *http.Request) http.Response {
|
||||||
|
|
||||||
|
fnResp.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||||
|
|
||||||
|
hResp := http.Response{
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
StatusCode: fnResp.Status,
|
||||||
|
Request: req,
|
||||||
|
Body: ioutil.NopCloser(buf),
|
||||||
|
ContentLength: int64(buf.Len()),
|
||||||
|
Header: fnResp.Header,
|
||||||
|
}
|
||||||
|
|
||||||
|
return hResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoHTTPOnce(handler Handler, ctx context.Context, in io.Reader, out io.Writer, buf *bytes.Buffer, hdr http.Header) error {
|
||||||
|
buf.Reset()
|
||||||
|
ResetHeaders(hdr)
|
||||||
|
resp := Response{
|
||||||
|
Writer: buf,
|
||||||
|
Status: 200,
|
||||||
|
Header: hdr,
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(in))
|
||||||
|
if err != nil {
|
||||||
|
// stdin now closed
|
||||||
|
if err == io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO it would be nice if we could let the user format this response to their preferred style..
|
||||||
|
resp.Status = http.StatusInternalServerError
|
||||||
|
io.WriteString(resp, err.Error())
|
||||||
|
} else {
|
||||||
|
fnDeadline := Context(ctx).Header.Get("FN_DEADLINE")
|
||||||
|
ctx, cancel := CtxWithDeadline(ctx, fnDeadline)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
SetHeaders(ctx, req.Header)
|
||||||
|
SetRequestURL(ctx, req.URL.String())
|
||||||
|
SetMethod(ctx, req.Method)
|
||||||
|
handler.Serve(ctx, req.Body, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
hResp := GetHTTPResp(buf, &resp, req)
|
||||||
|
hResp.Write(out)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
96
vendor/github.com/fnproject/fdk-go/utils/json.go
generated
vendored
Normal file
96
vendor/github.com/fnproject/fdk-go/utils/json.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DoJSON(handler Handler, ctx context.Context, in io.Reader, out io.Writer) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
hdr := make(http.Header)
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := DoJSONOnce(handler, ctx, in, out, &buf, hdr)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallRequestHTTP struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
RequestURL string `json:"request_url"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Headers http.Header `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonIn struct {
|
||||||
|
CallID string `json:"call_id"`
|
||||||
|
Deadline string `json:"deadline"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
ContentType string `json:"content_type"`
|
||||||
|
Protocol CallRequestHTTP `json:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallResponseHTTP struct {
|
||||||
|
StatusCode int `json:"status_code,omitempty"`
|
||||||
|
Headers http.Header `json:"headers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonOut struct {
|
||||||
|
Body string `json:"body"`
|
||||||
|
ContentType string `json:"content_type"`
|
||||||
|
Protocol CallResponseHTTP `json:"protocol,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetJSONResp(buf *bytes.Buffer, fnResp *Response) *JsonOut {
|
||||||
|
|
||||||
|
hResp := &JsonOut{
|
||||||
|
Body: buf.String(),
|
||||||
|
ContentType: "",
|
||||||
|
Protocol: CallResponseHTTP{
|
||||||
|
StatusCode: fnResp.Status,
|
||||||
|
Headers: fnResp.Header,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return hResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoJSONOnce(handler Handler, ctx context.Context, in io.Reader, out io.Writer, buf *bytes.Buffer, hdr http.Header) error {
|
||||||
|
buf.Reset()
|
||||||
|
ResetHeaders(hdr)
|
||||||
|
resp := Response{
|
||||||
|
Writer: buf,
|
||||||
|
Status: 200,
|
||||||
|
Header: hdr,
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonRequest JsonIn
|
||||||
|
err := json.NewDecoder(in).Decode(&jsonRequest)
|
||||||
|
if err != nil {
|
||||||
|
// stdin now closed
|
||||||
|
if err == io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp.Status = http.StatusInternalServerError
|
||||||
|
io.WriteString(resp, fmt.Sprintf(`{"error": %v}`, err.Error()))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
SetHeaders(ctx, jsonRequest.Protocol.Headers)
|
||||||
|
SetRequestURL(ctx, jsonRequest.Protocol.RequestURL)
|
||||||
|
SetMethod(ctx, jsonRequest.Protocol.Method)
|
||||||
|
ctx, cancel := CtxWithDeadline(ctx, jsonRequest.Deadline)
|
||||||
|
defer cancel()
|
||||||
|
handler.Serve(ctx, strings.NewReader(jsonRequest.Body), &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResponse := GetJSONResp(buf, &resp)
|
||||||
|
json.NewEncoder(out).Encode(jsonResponse)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
153
vendor/github.com/fnproject/fdk-go/utils/utils.go
generated
vendored
153
vendor/github.com/fnproject/fdk-go/utils/utils.go
generated
vendored
@@ -1,16 +1,11 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -31,8 +26,10 @@ func WithContext(ctx context.Context, fnctx *Ctx) context.Context {
|
|||||||
|
|
||||||
// Ctx provides access to Config and Headers from fn.
|
// Ctx provides access to Config and Headers from fn.
|
||||||
type Ctx struct {
|
type Ctx struct {
|
||||||
Header http.Header
|
Header http.Header
|
||||||
Config map[string]string
|
Config map[string]string
|
||||||
|
RequestURL string
|
||||||
|
Method string
|
||||||
}
|
}
|
||||||
|
|
||||||
type key struct{}
|
type key struct{}
|
||||||
@@ -46,6 +43,8 @@ func Do(handler Handler, format string, in io.Reader, out io.Writer) {
|
|||||||
DoHTTP(handler, ctx, in, out)
|
DoHTTP(handler, ctx, in, out)
|
||||||
case "json":
|
case "json":
|
||||||
DoJSON(handler, ctx, in, out)
|
DoJSON(handler, ctx, in, out)
|
||||||
|
case "cloudevent":
|
||||||
|
DoCloudEvent(handler, ctx, in, out)
|
||||||
case "default":
|
case "default":
|
||||||
DoDefault(handler, ctx, in, out)
|
DoDefault(handler, ctx, in, out)
|
||||||
default:
|
default:
|
||||||
@@ -81,88 +80,6 @@ func DoHTTP(handler Handler, ctx context.Context, in io.Reader, out io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoJSON(handler Handler, ctx context.Context, in io.Reader, out io.Writer) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
hdr := make(http.Header)
|
|
||||||
|
|
||||||
for {
|
|
||||||
err := DoJSONOnce(handler, ctx, in, out, &buf, hdr)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CallRequestHTTP struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
RequestURL string `json:"request_url"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Headers http.Header `json:"headers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonIn struct {
|
|
||||||
CallID string `json:"call_id"`
|
|
||||||
Deadline string `json:"deadline"`
|
|
||||||
Body string `json:"body"`
|
|
||||||
ContentType string `json:"content_type"`
|
|
||||||
Protocol CallRequestHTTP `json:"protocol"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CallResponseHTTP struct {
|
|
||||||
StatusCode int `json:"status_code,omitempty"`
|
|
||||||
Headers http.Header `json:"headers,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonOut struct {
|
|
||||||
Body string `json:"body"`
|
|
||||||
ContentType string `json:"content_type"`
|
|
||||||
Protocol CallResponseHTTP `json:"protocol,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetJSONResp(buf *bytes.Buffer, fnResp *Response, req *JsonIn) *JsonOut {
|
|
||||||
|
|
||||||
hResp := &JsonOut{
|
|
||||||
Body: buf.String(),
|
|
||||||
ContentType: "",
|
|
||||||
Protocol: CallResponseHTTP{
|
|
||||||
StatusCode: fnResp.Status,
|
|
||||||
Headers: fnResp.Header,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return hResp
|
|
||||||
}
|
|
||||||
|
|
||||||
func DoJSONOnce(handler Handler, ctx context.Context, in io.Reader, out io.Writer, buf *bytes.Buffer, hdr http.Header) error {
|
|
||||||
buf.Reset()
|
|
||||||
ResetHeaders(hdr)
|
|
||||||
resp := Response{
|
|
||||||
Writer: buf,
|
|
||||||
Status: 200,
|
|
||||||
Header: hdr,
|
|
||||||
}
|
|
||||||
|
|
||||||
var jsonRequest JsonIn
|
|
||||||
err := json.NewDecoder(in).Decode(&jsonRequest)
|
|
||||||
if err != nil {
|
|
||||||
// stdin now closed
|
|
||||||
if err == io.EOF {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp.Status = http.StatusInternalServerError
|
|
||||||
io.WriteString(resp, fmt.Sprintf(`{"error": %v}`, err.Error()))
|
|
||||||
} else {
|
|
||||||
SetHeaders(ctx, jsonRequest.Protocol.Headers)
|
|
||||||
ctx, cancel := CtxWithDeadline(ctx, jsonRequest.Deadline)
|
|
||||||
defer cancel()
|
|
||||||
handler.Serve(ctx, strings.NewReader(jsonRequest.Body), &resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonResponse := GetJSONResp(buf, &resp, &jsonRequest)
|
|
||||||
json.NewEncoder(out).Encode(jsonResponse)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CtxWithDeadline(ctx context.Context, fnDeadline string) (context.Context, context.CancelFunc) {
|
func CtxWithDeadline(ctx context.Context, fnDeadline string) (context.Context, context.CancelFunc) {
|
||||||
t, err := time.Parse(time.RFC3339, fnDeadline)
|
t, err := time.Parse(time.RFC3339, fnDeadline)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -171,54 +88,6 @@ func CtxWithDeadline(ctx context.Context, fnDeadline string) (context.Context, c
|
|||||||
return context.WithCancel(ctx)
|
return context.WithCancel(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHTTPResp(buf *bytes.Buffer, fnResp *Response, req *http.Request) http.Response {
|
|
||||||
|
|
||||||
fnResp.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
|
||||||
|
|
||||||
hResp := http.Response{
|
|
||||||
ProtoMajor: 1,
|
|
||||||
ProtoMinor: 1,
|
|
||||||
StatusCode: fnResp.Status,
|
|
||||||
Request: req,
|
|
||||||
Body: ioutil.NopCloser(buf),
|
|
||||||
ContentLength: int64(buf.Len()),
|
|
||||||
Header: fnResp.Header,
|
|
||||||
}
|
|
||||||
|
|
||||||
return hResp
|
|
||||||
}
|
|
||||||
|
|
||||||
func DoHTTPOnce(handler Handler, ctx context.Context, in io.Reader, out io.Writer, buf *bytes.Buffer, hdr http.Header) error {
|
|
||||||
buf.Reset()
|
|
||||||
ResetHeaders(hdr)
|
|
||||||
resp := Response{
|
|
||||||
Writer: buf,
|
|
||||||
Status: 200,
|
|
||||||
Header: hdr,
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.ReadRequest(bufio.NewReader(in))
|
|
||||||
if err != nil {
|
|
||||||
// stdin now closed
|
|
||||||
if err == io.EOF {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TODO it would be nice if we could let the user format this response to their preferred style..
|
|
||||||
resp.Status = http.StatusInternalServerError
|
|
||||||
io.WriteString(resp, err.Error())
|
|
||||||
} else {
|
|
||||||
fnDeadline := Context(ctx).Header.Get("FN_DEADLINE")
|
|
||||||
ctx, cancel := CtxWithDeadline(ctx, fnDeadline)
|
|
||||||
defer cancel()
|
|
||||||
SetHeaders(ctx, req.Header)
|
|
||||||
handler.Serve(ctx, req.Body, &resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
hResp := GetHTTPResp(buf, &resp, req)
|
|
||||||
hResp.Write(out)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResetHeaders(m http.Header) {
|
func ResetHeaders(m http.Header) {
|
||||||
for k := range m { // compiler optimizes this to 1 instruction now
|
for k := range m { // compiler optimizes this to 1 instruction now
|
||||||
delete(m, k)
|
delete(m, k)
|
||||||
@@ -258,6 +127,16 @@ func SetHeaders(ctx context.Context, hdr http.Header) {
|
|||||||
fctx.Header = hdr
|
fctx.Header = hdr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetRequestURL(ctx context.Context, requestURL string) {
|
||||||
|
fctx := ctx.Value(ctxKey).(*Ctx)
|
||||||
|
fctx.RequestURL = requestURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetMethod(ctx context.Context, method string) {
|
||||||
|
fctx := ctx.Value(ctxKey).(*Ctx)
|
||||||
|
fctx.Method = method
|
||||||
|
}
|
||||||
|
|
||||||
func BuildCtx() context.Context {
|
func BuildCtx() context.Context {
|
||||||
ctx := &Ctx{
|
ctx := &Ctx{
|
||||||
Config: BuildConfig(),
|
Config: BuildConfig(),
|
||||||
|
|||||||
14
vendor/github.com/fnproject/fn_go/.gitignore
generated
vendored
14
vendor/github.com/fnproject/fn_go/.gitignore
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test binary, build with `go test -c`
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
|
||||||
|
|
||||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
|
||||||
.glide/
|
|
||||||
201
vendor/github.com/fnproject/fn_go/LICENSE
generated
vendored
201
vendor/github.com/fnproject/fn_go/LICENSE
generated
vendored
@@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
1
vendor/github.com/fnproject/fn_go/VERSION
generated
vendored
1
vendor/github.com/fnproject/fn_go/VERSION
generated
vendored
@@ -1 +0,0 @@
|
|||||||
0.2.6
|
|
||||||
180
vendor/github.com/fnproject/fn_go/client/apps/apps_client.go
generated
vendored
180
vendor/github.com/fnproject/fn_go/client/apps/apps_client.go
generated
vendored
@@ -1,180 +0,0 @@
|
|||||||
// Code generated by go-swagger; DO NOT EDIT.
|
|
||||||
|
|
||||||
package apps
|
|
||||||
|
|
||||||
// This file was generated by the swagger tool.
|
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-openapi/runtime"
|
|
||||||
|
|
||||||
strfmt "github.com/go-openapi/strfmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New creates a new apps API client.
|
|
||||||
func New(transport runtime.ClientTransport, formats strfmt.Registry) *Client {
|
|
||||||
return &Client{transport: transport, formats: formats}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Client for apps API
|
|
||||||
*/
|
|
||||||
type Client struct {
|
|
||||||
transport runtime.ClientTransport
|
|
||||||
formats strfmt.Registry
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
DeleteAppsApp deletes an app
|
|
||||||
|
|
||||||
Delete an app.
|
|
||||||
*/
|
|
||||||
func (a *Client) DeleteAppsApp(params *DeleteAppsAppParams) (*DeleteAppsAppOK, error) {
|
|
||||||
// TODO: Validate the params before sending
|
|
||||||
if params == nil {
|
|
||||||
params = NewDeleteAppsAppParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := a.transport.Submit(&runtime.ClientOperation{
|
|
||||||
ID: "DeleteAppsApp",
|
|
||||||
Method: "DELETE",
|
|
||||||
PathPattern: "/apps/{app}",
|
|
||||||
ProducesMediaTypes: []string{"application/json"},
|
|
||||||
ConsumesMediaTypes: []string{"application/json"},
|
|
||||||
Schemes: []string{"http", "https"},
|
|
||||||
Params: params,
|
|
||||||
Reader: &DeleteAppsAppReader{formats: a.formats},
|
|
||||||
Context: params.Context,
|
|
||||||
Client: params.HTTPClient,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result.(*DeleteAppsAppOK), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetApps gets all app names
|
|
||||||
|
|
||||||
Get a list of all the apps in the system, returned in alphabetical order.
|
|
||||||
*/
|
|
||||||
func (a *Client) GetApps(params *GetAppsParams) (*GetAppsOK, error) {
|
|
||||||
// TODO: Validate the params before sending
|
|
||||||
if params == nil {
|
|
||||||
params = NewGetAppsParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := a.transport.Submit(&runtime.ClientOperation{
|
|
||||||
ID: "GetApps",
|
|
||||||
Method: "GET",
|
|
||||||
PathPattern: "/apps",
|
|
||||||
ProducesMediaTypes: []string{"application/json"},
|
|
||||||
ConsumesMediaTypes: []string{"application/json"},
|
|
||||||
Schemes: []string{"http", "https"},
|
|
||||||
Params: params,
|
|
||||||
Reader: &GetAppsReader{formats: a.formats},
|
|
||||||
Context: params.Context,
|
|
||||||
Client: params.HTTPClient,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result.(*GetAppsOK), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetAppsApp gets information for a app
|
|
||||||
|
|
||||||
This gives more details about a app, such as statistics.
|
|
||||||
*/
|
|
||||||
func (a *Client) GetAppsApp(params *GetAppsAppParams) (*GetAppsAppOK, error) {
|
|
||||||
// TODO: Validate the params before sending
|
|
||||||
if params == nil {
|
|
||||||
params = NewGetAppsAppParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := a.transport.Submit(&runtime.ClientOperation{
|
|
||||||
ID: "GetAppsApp",
|
|
||||||
Method: "GET",
|
|
||||||
PathPattern: "/apps/{app}",
|
|
||||||
ProducesMediaTypes: []string{"application/json"},
|
|
||||||
ConsumesMediaTypes: []string{"application/json"},
|
|
||||||
Schemes: []string{"http", "https"},
|
|
||||||
Params: params,
|
|
||||||
Reader: &GetAppsAppReader{formats: a.formats},
|
|
||||||
Context: params.Context,
|
|
||||||
Client: params.HTTPClient,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result.(*GetAppsAppOK), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
PatchAppsApp updates an app
|
|
||||||
|
|
||||||
You can set app level settings here.
|
|
||||||
*/
|
|
||||||
func (a *Client) PatchAppsApp(params *PatchAppsAppParams) (*PatchAppsAppOK, error) {
|
|
||||||
// TODO: Validate the params before sending
|
|
||||||
if params == nil {
|
|
||||||
params = NewPatchAppsAppParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := a.transport.Submit(&runtime.ClientOperation{
|
|
||||||
ID: "PatchAppsApp",
|
|
||||||
Method: "PATCH",
|
|
||||||
PathPattern: "/apps/{app}",
|
|
||||||
ProducesMediaTypes: []string{"application/json"},
|
|
||||||
ConsumesMediaTypes: []string{"application/json"},
|
|
||||||
Schemes: []string{"http", "https"},
|
|
||||||
Params: params,
|
|
||||||
Reader: &PatchAppsAppReader{formats: a.formats},
|
|
||||||
Context: params.Context,
|
|
||||||
Client: params.HTTPClient,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result.(*PatchAppsAppOK), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
PostApps posts new app
|
|
||||||
|
|
||||||
Insert a new app
|
|
||||||
*/
|
|
||||||
func (a *Client) PostApps(params *PostAppsParams) (*PostAppsOK, error) {
|
|
||||||
// TODO: Validate the params before sending
|
|
||||||
if params == nil {
|
|
||||||
params = NewPostAppsParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := a.transport.Submit(&runtime.ClientOperation{
|
|
||||||
ID: "PostApps",
|
|
||||||
Method: "POST",
|
|
||||||
PathPattern: "/apps",
|
|
||||||
ProducesMediaTypes: []string{"application/json"},
|
|
||||||
ConsumesMediaTypes: []string{"application/json"},
|
|
||||||
Schemes: []string{"http", "https"},
|
|
||||||
Params: params,
|
|
||||||
Reader: &PostAppsReader{formats: a.formats},
|
|
||||||
Context: params.Context,
|
|
||||||
Client: params.HTTPClient,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result.(*PostAppsOK), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTransport changes the transport on the client
|
|
||||||
func (a *Client) SetTransport(transport runtime.ClientTransport) {
|
|
||||||
a.transport = transport
|
|
||||||
}
|
|
||||||
137
vendor/github.com/fnproject/fn_go/client/apps/delete_apps_app_parameters.go
generated
vendored
137
vendor/github.com/fnproject/fn_go/client/apps/delete_apps_app_parameters.go
generated
vendored
@@ -1,137 +0,0 @@
|
|||||||
// Code generated by go-swagger; DO NOT EDIT.
|
|
||||||
|
|
||||||
package apps
|
|
||||||
|
|
||||||
// This file was generated by the swagger tool.
|
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
|
||||||
"github.com/go-openapi/runtime"
|
|
||||||
cr "github.com/go-openapi/runtime/client"
|
|
||||||
|
|
||||||
strfmt "github.com/go-openapi/strfmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDeleteAppsAppParams creates a new DeleteAppsAppParams object
|
|
||||||
// with the default values initialized.
|
|
||||||
func NewDeleteAppsAppParams() *DeleteAppsAppParams {
|
|
||||||
var ()
|
|
||||||
return &DeleteAppsAppParams{
|
|
||||||
|
|
||||||
timeout: cr.DefaultTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDeleteAppsAppParamsWithTimeout creates a new DeleteAppsAppParams object
|
|
||||||
// with the default values initialized, and the ability to set a timeout on a request
|
|
||||||
func NewDeleteAppsAppParamsWithTimeout(timeout time.Duration) *DeleteAppsAppParams {
|
|
||||||
var ()
|
|
||||||
return &DeleteAppsAppParams{
|
|
||||||
|
|
||||||
timeout: timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDeleteAppsAppParamsWithContext creates a new DeleteAppsAppParams object
|
|
||||||
// with the default values initialized, and the ability to set a context for a request
|
|
||||||
func NewDeleteAppsAppParamsWithContext(ctx context.Context) *DeleteAppsAppParams {
|
|
||||||
var ()
|
|
||||||
return &DeleteAppsAppParams{
|
|
||||||
|
|
||||||
Context: ctx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDeleteAppsAppParamsWithHTTPClient creates a new DeleteAppsAppParams object
|
|
||||||
// with the default values initialized, and the ability to set a custom HTTPClient for a request
|
|
||||||
func NewDeleteAppsAppParamsWithHTTPClient(client *http.Client) *DeleteAppsAppParams {
|
|
||||||
var ()
|
|
||||||
return &DeleteAppsAppParams{
|
|
||||||
HTTPClient: client,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*DeleteAppsAppParams contains all the parameters to send to the API endpoint
|
|
||||||
for the delete apps app operation typically these are written to a http.Request
|
|
||||||
*/
|
|
||||||
type DeleteAppsAppParams struct {
|
|
||||||
|
|
||||||
/*App
|
|
||||||
Name of the app.
|
|
||||||
|
|
||||||
*/
|
|
||||||
App string
|
|
||||||
|
|
||||||
timeout time.Duration
|
|
||||||
Context context.Context
|
|
||||||
HTTPClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTimeout adds the timeout to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) WithTimeout(timeout time.Duration) *DeleteAppsAppParams {
|
|
||||||
o.SetTimeout(timeout)
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTimeout adds the timeout to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) SetTimeout(timeout time.Duration) {
|
|
||||||
o.timeout = timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext adds the context to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) WithContext(ctx context.Context) *DeleteAppsAppParams {
|
|
||||||
o.SetContext(ctx)
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetContext adds the context to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) SetContext(ctx context.Context) {
|
|
||||||
o.Context = ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHTTPClient adds the HTTPClient to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) WithHTTPClient(client *http.Client) *DeleteAppsAppParams {
|
|
||||||
o.SetHTTPClient(client)
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHTTPClient adds the HTTPClient to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) SetHTTPClient(client *http.Client) {
|
|
||||||
o.HTTPClient = client
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithApp adds the app to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) WithApp(app string) *DeleteAppsAppParams {
|
|
||||||
o.SetApp(app)
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetApp adds the app to the delete apps app params
|
|
||||||
func (o *DeleteAppsAppParams) SetApp(app string) {
|
|
||||||
o.App = app
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteToRequest writes these params to a swagger request
|
|
||||||
func (o *DeleteAppsAppParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
|
||||||
|
|
||||||
if err := r.SetTimeout(o.timeout); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var res []error
|
|
||||||
|
|
||||||
// path param app
|
|
||||||
if err := r.SetPathParam("app", o.App); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(res) > 0 {
|
|
||||||
return errors.CompositeValidationError(res...)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user