diff --git a/Makefile b/Makefile index a61652126d..a9534e287f 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ BUILD_ARGS+= --build-arg http_proxy=$(http_proxy) --build-arg https_proxy=$(http endif HAS_GLIDE := $(shell command -v glide;) -GO_SWAGGER_VERSION := 0.11.0 +GO_SWAGGER_VERSION := 0.12.0 SWAGGER_BIN := bin/$(GOOS)/swagger-$(GO_SWAGGER_VERSION) .PHONY: all clean code-gen client-gen informer-gen lister-gen @@ -76,8 +76,9 @@ swagger-generate: # --existing-models github.com/sapcc/kubernikus/pkg/api/models seems not to work in our case pkg/client/kubernikus_generated/kubernikus_client.go: swagger.yml ifneq (,$(wildcard $(SWAGGER_BIN))) - $(SWAGGER_BIN) generate client --name kubernikus --target pkg/client --client-package kubernikus_generated \ - --principal models.Principal + $(SWAGGER_BIN) generate client --name kubernikus --target pkg/api --client-package client \ + --existing-models github.com/sapcc/kubernikus/pkg/api/models \ + --principal models.Principal else $(warning WARNING: $(SWAGGER_BIN) missing. Run `make bootstrap` to fix.) endif diff --git a/cmd/go-swagger-deps/main.go b/cmd/go-swagger-deps/main.go new file mode 100644 index 0000000000..26d815b843 --- /dev/null +++ b/cmd/go-swagger-deps/main.go @@ -0,0 +1,105 @@ +// This command is a helper functions that looks up dependency revisions +// for the packages used by the go-swagger generated code +// +// It lists the dependencies used be a given package and looks +// for matching entries in go-swaggers Gopkg.lock file +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "log" + "net/http" + "os/exec" + "regexp" + "strings" + + "github.com/BurntSushi/toml" + "github.com/spf13/pflag" +) + +type Project struct { + Name string + Branch string + Packages []string + Revision string +} + +type Deps struct { + Projects []Project +} + +var ( + blacklist []string + version string +) + +func main() { + pflag.StringVarP(&version, "version", "v", "0.12.0", "go-swagger version") + pflag.StringArrayVarP(&blacklist, "blacklist", "b", []string{"golang.org/x/net", "golang.org/x/text"}, "don't consider the given dependencies") + + pflag.Parse() + packages := flag.Args() + if len(packages) == 0 { + packages = []string{"github.com/sapcc/kubernikus/pkg/api/rest/operations"} + } + + url := fmt.Sprintf("https://raw.githubusercontent.com/go-swagger/go-swagger/%s/Gopkg.lock", version) + log.Println("Fetching ", url) + + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + if resp.StatusCode >= 400 { + log.Fatal("Failed to fetch Gopkg.lock: %s", resp.Status) + } + + var deps Deps + + _, err = toml.DecodeReader(resp.Body, &deps) + if err != nil { + log.Fatal(err) + } + + cmd := exec.Command("go", append([]string{"list", "-f", `{{ join .Deps "\n" }}`}, packages...)...) + log.Printf("Running %v", cmd.Args) + output, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err) + } + result := map[string]string{} + scanner := bufio.NewScanner(bytes.NewReader(output)) + re := regexp.MustCompile(`kubernikus/vendor/(.+)`) + for scanner.Scan() { + dep := scanner.Text() + if matches := re.FindStringSubmatch(dep); matches != nil { + for _, p := range deps.Projects { + if blacklisted(p.Name) { + continue + } + if strings.HasPrefix(matches[1], p.Name) { + result[p.Name] = p.Revision + break + } + } + } + } + fmt.Printf("# Dependencies extracted from go-swagger %s\n", version) + for pkg, rev := range result { + fmt.Printf("- package: %s\n", pkg) + fmt.Printf(" version: %s\n", rev) + } + +} + +func blacklisted(dep string) bool { + for _, entry := range blacklist { + if entry == dep { + return true + } + } + return false +} diff --git a/glide.lock b/glide.lock index 9df02a6883..62269cd3c4 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 61d9f95dee81440023236ef21eab3ba800241b0704cd2a4fa33cd2205d6126d2 -updated: 2017-10-30T15:45:22.065702967+01:00 +hash: 3f87d7fdca10a5a46ba2b729844a4cffb0b39935f567887ee45cdfbd432dd1a5 +updated: 2017-11-06T11:36:49.796462571+01:00 imports: - name: github.com/ajeddeloh/yaml version: 1072abfea31191db507785e2e0c1b8d1440d35a5 @@ -8,7 +8,7 @@ imports: - name: github.com/aokoli/goutils version: 3391d3790d23d03408670993e957e8f408993c34 - name: github.com/asaskevich/govalidator - version: 7664702784775e51966f0885f5cd27435916517b + version: 73945b6115bfbbcc57d89b7316e28109364124e1 - name: github.com/BurntSushi/toml version: b26d9c308763d68093482582cea63d69be07a0f0 - name: github.com/cenkalti/backoff @@ -72,7 +72,7 @@ imports: - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee - name: github.com/go-openapi/analysis - version: 7222828b8ce19afee3c595aef6643b9e42150120 + version: 8ed83f2ea9f00f945516462951a288eaa68bf0d6 - name: github.com/go-openapi/errors version: 03cfca65330da08a5a440053faf994a3c682b5bf - name: github.com/go-openapi/jsonpointer @@ -82,7 +82,7 @@ imports: - name: github.com/go-openapi/loads version: a80dea3052f00e5f032e860dd7355cd0cc67e24d - name: github.com/go-openapi/runtime - version: 73a94727f26953a79ffd70902d0f24977b6297d1 + version: bf2ff8f7150788b1c7256abb0805ba0410cbbabb subpackages: - client - flagext @@ -92,13 +92,13 @@ imports: - middleware/untyped - security - name: github.com/go-openapi/spec - version: e51c28f07047ad90caff03f6450908720d337e0c + version: 3faa0055dbbf2110abc1f3b4e3adbb22721e96e7 - name: github.com/go-openapi/strfmt - version: 93a31ef21ac23f317792fff78f9539219dd74619 + version: 610b6cacdcde6852f4de68998bd20ce1dac85b22 - name: github.com/go-openapi/swag - version: e43299b4afa7bc7f22e5e82e3d48607230e4c177 + version: f3f9494671f93fcff853e3c6e9e948b3eb71e590 - name: github.com/go-openapi/validate - version: 035dcd74f1f61e83debe1c22950dc53556e7e4b2 + version: 8a82927c942c94794a5cd8b8b50ce2f48a955c0c - name: github.com/gobwas/glob version: bea32b9cd2d6f55753d94a28e959b13f0244797a subpackages: @@ -168,7 +168,7 @@ imports: - name: github.com/kennygrant/sanitize version: 6a0bfdde8629a3a3a7418a7eae45c54154692514 - name: github.com/mailru/easyjson - version: 44c0351a5bc860bcb2608d54aa03ea686c4e7b25 + version: 2a92e673c9a6302dd05c3a691ae1f24aef46457d subpackages: - buffer - jlexer @@ -186,9 +186,9 @@ imports: - name: github.com/pmylund/go-cache version: 93d85800f2fa6bd0a739e7bd612bfa3bc008b72d - name: github.com/PuerkitoBio/purell - version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 + version: 0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4 - name: github.com/PuerkitoBio/urlesc - version: 5bd2802263f21d8788851d5305584c82a5c75d7e + version: de5bf2ad457846296e2031421a34e2568e304e35 - name: github.com/rs/cors version: eabcc6af4bbe5ad3a949d36450326a2b0b9894b8 - name: github.com/satori/go.uuid @@ -265,9 +265,15 @@ imports: - bson - internal/json - name: gopkg.in/yaml.v2 - version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 + version: eb3733d160e74a9c7e442f435eb3bea458e1d19f - name: k8s.io/apiextensions-apiserver version: fcd622fe88a4a6efcb5aea9e94ee87324ac1b036 + subpackages: + - pkg/apis/apiextensions + - pkg/apis/apiextensions/v1beta1 + - pkg/client/clientset/clientset + - pkg/client/clientset/clientset/scheme + - pkg/client/clientset/clientset/typed/apiextensions/v1beta1 - name: k8s.io/apimachinery version: 917740426ad66ff818da4809990480bcc0786a77 subpackages: diff --git a/glide.yaml b/glide.yaml index 03da4ae0ff..1432bef135 100644 --- a/glide.yaml +++ b/glide.yaml @@ -10,21 +10,6 @@ import: version: v2.5.0 - package: k8s.io/apiextensions-apiserver version: release-1.7 -# k8s.io/client-go carries some outdated depencencies in its Godeps.json -# that don't fly with swagger. -# We don't import these dependencies via the go-client so this should be ok -- package: github.com/go-openapi/runtime - version: 73a94727f26953a79ffd70902d0f24977b6297d1 -- package: github.com/go-openapi/jsonpointer - version: 779f45308c19820f1a69e9a4cd965f496e0da10f -- package: github.com/go-openapi/jsonreference - version: 36d33bfe519efae5632669801b180bf1a245da3b -- package: github.com/go-openapi/spec - version: e51c28f07047ad90caff03f6450908720d337e0c -- package: github.com/go-openapi/swag - version: e43299b4afa7bc7f22e5e82e3d48607230e4c177 -- package: github.com/mailru/easyjson - version: 44c0351a5bc860bcb2608d54aa03ea686c4e7b25 - package: k8s.io/code-generator - package: k8s.io/utils - package: k8s.io/gengo @@ -36,3 +21,38 @@ import: - package: github.com/imdario/mergo version: 0.2.2 - package: github.com/databus23/guttle +# Dependencies extracted from go-swagger 0.12.0 +- package: github.com/go-openapi/jsonpointer + version: 779f45308c19820f1a69e9a4cd965f496e0da10f +- package: github.com/go-openapi/loads + version: a80dea3052f00e5f032e860dd7355cd0cc67e24d +- package: gopkg.in/yaml.v2 + version: eb3733d160e74a9c7e442f435eb3bea458e1d19f +- package: github.com/PuerkitoBio/urlesc + version: de5bf2ad457846296e2031421a34e2568e304e35 +- package: github.com/go-openapi/analysis + version: 8ed83f2ea9f00f945516462951a288eaa68bf0d6 +- package: github.com/go-openapi/swag + version: f3f9494671f93fcff853e3c6e9e948b3eb71e590 +- package: github.com/mailru/easyjson + version: 2a92e673c9a6302dd05c3a691ae1f24aef46457d +- package: gopkg.in/mgo.v2 + version: 3f83fa5005286a7fe593b055f0d7771a7dce4655 +- package: github.com/PuerkitoBio/purell + version: 0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4 +- package: github.com/go-openapi/strfmt + version: 610b6cacdcde6852f4de68998bd20ce1dac85b22 +- package: github.com/go-openapi/validate + version: 8a82927c942c94794a5cd8b8b50ce2f48a955c0c +- package: github.com/go-openapi/runtime + version: bf2ff8f7150788b1c7256abb0805ba0410cbbabb +- package: github.com/go-openapi/spec + version: 3faa0055dbbf2110abc1f3b4e3adbb22721e96e7 +- package: github.com/mitchellh/mapstructure + version: d0303fe809921458f417bcf828397a65db30a7e4 +- package: github.com/asaskevich/govalidator + version: 73945b6115bfbbcc57d89b7316e28109364124e1 +- package: github.com/go-openapi/errors + version: 03cfca65330da08a5a440053faf994a3c682b5bf +- package: github.com/go-openapi/jsonreference + version: 36d33bfe519efae5632669801b180bf1a245da3b diff --git a/pkg/client/kubernikus_generated/kubernikus_client.go b/pkg/api/client/kubernikus_client.go similarity index 96% rename from pkg/client/kubernikus_generated/kubernikus_client.go rename to pkg/api/client/kubernikus_client.go index 0743af386f..fc4c2313d8 100644 --- a/pkg/client/kubernikus_generated/kubernikus_client.go +++ b/pkg/api/client/kubernikus_client.go @@ -1,6 +1,6 @@ // Code generated by go-swagger; DO NOT EDIT. -package kubernikus_generated +package client // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command @@ -11,7 +11,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/kubernikus_generated/operations" + "github.com/sapcc/kubernikus/pkg/api/client/operations" ) // Default kubernikus HTTP client. diff --git a/pkg/client/kubernikus_generated/operations/create_cluster_parameters.go b/pkg/api/client/operations/create_cluster_parameters.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/create_cluster_parameters.go rename to pkg/api/client/operations/create_cluster_parameters.go index 4f62763f83..6ae86b8e67 100644 --- a/pkg/client/kubernikus_generated/operations/create_cluster_parameters.go +++ b/pkg/api/client/operations/create_cluster_parameters.go @@ -17,7 +17,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // NewCreateClusterParams creates a new CreateClusterParams object diff --git a/pkg/client/kubernikus_generated/operations/create_cluster_responses.go b/pkg/api/client/operations/create_cluster_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/create_cluster_responses.go rename to pkg/api/client/operations/create_cluster_responses.go index ab08f1cc37..ef7d3b355f 100644 --- a/pkg/client/kubernikus_generated/operations/create_cluster_responses.go +++ b/pkg/api/client/operations/create_cluster_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // CreateClusterReader is a Reader for the CreateCluster structure. diff --git a/pkg/client/kubernikus_generated/operations/get_cluster_credentials_parameters.go b/pkg/api/client/operations/get_cluster_credentials_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/get_cluster_credentials_parameters.go rename to pkg/api/client/operations/get_cluster_credentials_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/get_cluster_credentials_responses.go b/pkg/api/client/operations/get_cluster_credentials_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/get_cluster_credentials_responses.go rename to pkg/api/client/operations/get_cluster_credentials_responses.go index efc2a6fb7f..021f603d32 100644 --- a/pkg/client/kubernikus_generated/operations/get_cluster_credentials_responses.go +++ b/pkg/api/client/operations/get_cluster_credentials_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // GetClusterCredentialsReader is a Reader for the GetClusterCredentials structure. diff --git a/pkg/client/kubernikus_generated/operations/get_cluster_info_parameters.go b/pkg/api/client/operations/get_cluster_info_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/get_cluster_info_parameters.go rename to pkg/api/client/operations/get_cluster_info_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/get_cluster_info_responses.go b/pkg/api/client/operations/get_cluster_info_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/get_cluster_info_responses.go rename to pkg/api/client/operations/get_cluster_info_responses.go index 88c12202fb..5e7449ecf8 100644 --- a/pkg/client/kubernikus_generated/operations/get_cluster_info_responses.go +++ b/pkg/api/client/operations/get_cluster_info_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // GetClusterInfoReader is a Reader for the GetClusterInfo structure. diff --git a/pkg/client/kubernikus_generated/operations/info_parameters.go b/pkg/api/client/operations/info_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/info_parameters.go rename to pkg/api/client/operations/info_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/info_responses.go b/pkg/api/client/operations/info_responses.go similarity index 96% rename from pkg/client/kubernikus_generated/operations/info_responses.go rename to pkg/api/client/operations/info_responses.go index 76c5c9b74f..d568883182 100644 --- a/pkg/client/kubernikus_generated/operations/info_responses.go +++ b/pkg/api/client/operations/info_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // InfoReader is a Reader for the Info structure. diff --git a/pkg/client/kubernikus_generated/operations/list_api_versions_parameters.go b/pkg/api/client/operations/list_api_versions_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/list_api_versions_parameters.go rename to pkg/api/client/operations/list_api_versions_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/list_api_versions_responses.go b/pkg/api/client/operations/list_api_versions_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/list_api_versions_responses.go rename to pkg/api/client/operations/list_api_versions_responses.go index 1017d48c9d..b989f41f06 100644 --- a/pkg/client/kubernikus_generated/operations/list_api_versions_responses.go +++ b/pkg/api/client/operations/list_api_versions_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // ListAPIVersionsReader is a Reader for the ListAPIVersions structure. diff --git a/pkg/client/kubernikus_generated/operations/list_clusters_parameters.go b/pkg/api/client/operations/list_clusters_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/list_clusters_parameters.go rename to pkg/api/client/operations/list_clusters_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/list_clusters_responses.go b/pkg/api/client/operations/list_clusters_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/list_clusters_responses.go rename to pkg/api/client/operations/list_clusters_responses.go index f86ec25210..960a91c8e8 100644 --- a/pkg/client/kubernikus_generated/operations/list_clusters_responses.go +++ b/pkg/api/client/operations/list_clusters_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // ListClustersReader is a Reader for the ListClusters structure. diff --git a/pkg/client/kubernikus_generated/operations/operations_client.go b/pkg/api/client/operations/operations_client.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/operations_client.go rename to pkg/api/client/operations/operations_client.go diff --git a/pkg/client/kubernikus_generated/operations/show_cluster_parameters.go b/pkg/api/client/operations/show_cluster_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/show_cluster_parameters.go rename to pkg/api/client/operations/show_cluster_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/show_cluster_responses.go b/pkg/api/client/operations/show_cluster_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/show_cluster_responses.go rename to pkg/api/client/operations/show_cluster_responses.go index f0f659e245..cfb9eb00f2 100644 --- a/pkg/client/kubernikus_generated/operations/show_cluster_responses.go +++ b/pkg/api/client/operations/show_cluster_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // ShowClusterReader is a Reader for the ShowCluster structure. diff --git a/pkg/client/kubernikus_generated/operations/terminate_cluster_parameters.go b/pkg/api/client/operations/terminate_cluster_parameters.go similarity index 100% rename from pkg/client/kubernikus_generated/operations/terminate_cluster_parameters.go rename to pkg/api/client/operations/terminate_cluster_parameters.go diff --git a/pkg/client/kubernikus_generated/operations/terminate_cluster_responses.go b/pkg/api/client/operations/terminate_cluster_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/terminate_cluster_responses.go rename to pkg/api/client/operations/terminate_cluster_responses.go index 9ea86b02f4..91c20dc261 100644 --- a/pkg/client/kubernikus_generated/operations/terminate_cluster_responses.go +++ b/pkg/api/client/operations/terminate_cluster_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // TerminateClusterReader is a Reader for the TerminateCluster structure. @@ -112,4 +112,5 @@ func (o *TerminateClusterDefault) readResponse(response runtime.ClientResponse, /*TerminateClusterAcceptedBody terminate cluster accepted body swagger:model TerminateClusterAcceptedBody */ + type TerminateClusterAcceptedBody interface{} diff --git a/pkg/client/kubernikus_generated/operations/update_cluster_parameters.go b/pkg/api/client/operations/update_cluster_parameters.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/update_cluster_parameters.go rename to pkg/api/client/operations/update_cluster_parameters.go index aed3db4f19..69e6c4095f 100644 --- a/pkg/client/kubernikus_generated/operations/update_cluster_parameters.go +++ b/pkg/api/client/operations/update_cluster_parameters.go @@ -17,7 +17,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // NewUpdateClusterParams creates a new UpdateClusterParams object diff --git a/pkg/client/kubernikus_generated/operations/update_cluster_responses.go b/pkg/api/client/operations/update_cluster_responses.go similarity index 98% rename from pkg/client/kubernikus_generated/operations/update_cluster_responses.go rename to pkg/api/client/operations/update_cluster_responses.go index 3e61adf611..5c2363617e 100644 --- a/pkg/client/kubernikus_generated/operations/update_cluster_responses.go +++ b/pkg/api/client/operations/update_cluster_responses.go @@ -13,7 +13,7 @@ import ( strfmt "github.com/go-openapi/strfmt" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" ) // UpdateClusterReader is a Reader for the UpdateCluster structure. diff --git a/pkg/api/models/api_versions.go b/pkg/api/models/api_versions.go index 766acc2424..90d0c9b1d6 100644 --- a/pkg/api/models/api_versions.go +++ b/pkg/api/models/api_versions.go @@ -15,6 +15,7 @@ import ( // APIVersions Api versions // swagger:model ApiVersions + type APIVersions struct { // versions are the api versions that are available. @@ -22,6 +23,8 @@ type APIVersions struct { Versions []string `json:"versions"` } +/* polymorph ApiVersions versions false */ + // Validate validates this Api versions func (m *APIVersions) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/api/models/cluster.go b/pkg/api/models/cluster.go index 08d0644e29..d676c70b17 100644 --- a/pkg/api/models/cluster.go +++ b/pkg/api/models/cluster.go @@ -17,6 +17,7 @@ import ( // Cluster cluster // swagger:model Cluster + type Cluster struct { // name of the cluster @@ -31,6 +32,12 @@ type Cluster struct { Status *ClusterStatus `json:"status,omitempty"` } +/* polymorph Cluster name false */ + +/* polymorph Cluster spec false */ + +/* polymorph Cluster status false */ + // Validate validates this cluster func (m *Cluster) Validate(formats strfmt.Registry) error { var res []error @@ -124,6 +131,7 @@ func (m *Cluster) UnmarshalBinary(b []byte) error { // ClusterSpec cluster spec // swagger:model ClusterSpec + type ClusterSpec struct { // CIDR Range for Pods in the cluster. Can not be updated. @@ -138,6 +146,8 @@ type ClusterSpec struct { ServiceCIDR string `json:"serviceCIDR,omitempty"` } +/* polymorph ClusterSpec nodePools false */ + // Validate validates this cluster spec func (m *ClusterSpec) Validate(formats strfmt.Registry) error { var res []error @@ -236,6 +246,7 @@ func (m *ClusterSpec) UnmarshalBinary(b []byte) error { // ClusterSpecNodePoolsItems0 cluster spec node pools items0 // swagger:model ClusterSpecNodePoolsItems0 + type ClusterSpecNodePoolsItems0 struct { // flavor @@ -257,6 +268,14 @@ type ClusterSpecNodePoolsItems0 struct { Size *int64 `json:"size"` } +/* polymorph ClusterSpecNodePoolsItems0 flavor false */ + +/* polymorph ClusterSpecNodePoolsItems0 image false */ + +/* polymorph ClusterSpecNodePoolsItems0 name false */ + +/* polymorph ClusterSpecNodePoolsItems0 size false */ + // Validate validates this cluster spec node pools items0 func (m *ClusterSpecNodePoolsItems0) Validate(formats strfmt.Registry) error { var res []error @@ -341,6 +360,7 @@ func (m *ClusterSpecNodePoolsItems0) UnmarshalBinary(b []byte) error { // ClusterStatus cluster status // swagger:model ClusterStatus + type ClusterStatus struct { // kluster @@ -350,6 +370,10 @@ type ClusterStatus struct { NodePools []*ClusterStatusNodePoolsItems0 `json:"nodePools"` } +/* polymorph ClusterStatus kluster false */ + +/* polymorph ClusterStatus nodePools false */ + // Validate validates this cluster status func (m *ClusterStatus) Validate(formats strfmt.Registry) error { var res []error @@ -436,6 +460,7 @@ func (m *ClusterStatus) UnmarshalBinary(b []byte) error { // ClusterStatusKluster cluster status kluster // swagger:model ClusterStatusKluster + type ClusterStatusKluster struct { // message @@ -445,6 +470,10 @@ type ClusterStatusKluster struct { State string `json:"state,omitempty"` } +/* polymorph ClusterStatusKluster message false */ + +/* polymorph ClusterStatusKluster state false */ + // Validate validates this cluster status kluster func (m *ClusterStatusKluster) Validate(formats strfmt.Registry) error { var res []error @@ -475,6 +504,7 @@ func (m *ClusterStatusKluster) UnmarshalBinary(b []byte) error { // ClusterStatusNodePoolsItems0 cluster status node pools items0 // swagger:model ClusterStatusNodePoolsItems0 + type ClusterStatusNodePoolsItems0 struct { // healthy @@ -498,6 +528,16 @@ type ClusterStatusNodePoolsItems0 struct { Size *int64 `json:"size"` } +/* polymorph ClusterStatusNodePoolsItems0 healthy false */ + +/* polymorph ClusterStatusNodePoolsItems0 name false */ + +/* polymorph ClusterStatusNodePoolsItems0 running false */ + +/* polymorph ClusterStatusNodePoolsItems0 schedulable false */ + +/* polymorph ClusterStatusNodePoolsItems0 size false */ + // Validate validates this cluster status node pools items0 func (m *ClusterStatusNodePoolsItems0) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/api/models/cluster_info.go b/pkg/api/models/cluster_info.go index 4b5f12701e..518c04f427 100644 --- a/pkg/api/models/cluster_info.go +++ b/pkg/api/models/cluster_info.go @@ -16,6 +16,7 @@ import ( // ClusterInfo cluster info // swagger:model ClusterInfo + type ClusterInfo struct { // binaries @@ -25,6 +26,10 @@ type ClusterInfo struct { SetupCommand string `json:"setupCommand,omitempty"` } +/* polymorph ClusterInfo binaries false */ + +/* polymorph ClusterInfo setupCommand false */ + // Validate validates this cluster info func (m *ClusterInfo) Validate(formats strfmt.Registry) error { var res []error @@ -87,6 +92,7 @@ func (m *ClusterInfo) UnmarshalBinary(b []byte) error { // ClusterInfoBinariesItems0 cluster info binaries items0 // swagger:model ClusterInfoBinariesItems0 + type ClusterInfoBinariesItems0 struct { // links @@ -96,6 +102,10 @@ type ClusterInfoBinariesItems0 struct { Name string `json:"name,omitempty"` } +/* polymorph ClusterInfoBinariesItems0 links false */ + +/* polymorph ClusterInfoBinariesItems0 name false */ + // Validate validates this cluster info binaries items0 func (m *ClusterInfoBinariesItems0) Validate(formats strfmt.Registry) error { var res []error @@ -158,6 +168,7 @@ func (m *ClusterInfoBinariesItems0) UnmarshalBinary(b []byte) error { // ClusterInfoBinariesItems0LinksItems0 cluster info binaries items0 links items0 // swagger:model ClusterInfoBinariesItems0LinksItems0 + type ClusterInfoBinariesItems0LinksItems0 struct { // link @@ -167,6 +178,10 @@ type ClusterInfoBinariesItems0LinksItems0 struct { Platform string `json:"platform,omitempty"` } +/* polymorph ClusterInfoBinariesItems0LinksItems0 link false */ + +/* polymorph ClusterInfoBinariesItems0LinksItems0 platform false */ + // Validate validates this cluster info binaries items0 links items0 func (m *ClusterInfoBinariesItems0LinksItems0) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/client/models/cluster_print.go b/pkg/api/models/cluster_print.go similarity index 100% rename from pkg/client/models/cluster_print.go rename to pkg/api/models/cluster_print.go diff --git a/pkg/api/models/credentials.go b/pkg/api/models/credentials.go index 2782782ef7..34e426f6d1 100644 --- a/pkg/api/models/credentials.go +++ b/pkg/api/models/credentials.go @@ -14,12 +14,15 @@ import ( // Credentials credentials // swagger:model Credentials + type Credentials struct { // kubeconfig Kubeconfig string `json:"kubeconfig,omitempty"` } +/* polymorph Credentials kubeconfig false */ + // Validate validates this credentials func (m *Credentials) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/api/models/error.go b/pkg/api/models/error.go index e5f97fdd70..043566388c 100644 --- a/pkg/api/models/error.go +++ b/pkg/api/models/error.go @@ -16,6 +16,7 @@ import ( // Error the error model is a model for all the error responses coming from Kubernikus // // swagger:model error + type Error struct { // The error code @@ -30,6 +31,12 @@ type Error struct { Message *string `json:"message"` } +/* polymorph error code false */ + +/* polymorph error helpUrl false */ + +/* polymorph error message false */ + // Validate validates this error func (m *Error) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/api/models/info.go b/pkg/api/models/info.go index 2559f21c15..014af180df 100644 --- a/pkg/api/models/info.go +++ b/pkg/api/models/info.go @@ -14,12 +14,15 @@ import ( // Info info // swagger:model Info + type Info struct { // version Version string `json:"version,omitempty"` } +/* polymorph Info version false */ + // Validate validates this info func (m *Info) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/api/models/principal.go b/pkg/api/models/principal.go index 3f031edd7d..d456658e18 100644 --- a/pkg/api/models/principal.go +++ b/pkg/api/models/principal.go @@ -14,6 +14,7 @@ import ( // Principal principal // swagger:model Principal + type Principal struct { // account id @@ -35,6 +36,18 @@ type Principal struct { Roles []string `json:"roles"` } +/* polymorph Principal account false */ + +/* polymorph Principal authUrl false */ + +/* polymorph Principal domain false */ + +/* polymorph Principal id false */ + +/* polymorph Principal name false */ + +/* polymorph Principal roles false */ + // Validate validates this principal func (m *Principal) Validate(formats strfmt.Registry) error { var res []error diff --git a/pkg/api/rest/configure_kubernikus.go b/pkg/api/rest/configure_kubernikus.go index 9b7f3bbfe0..0f4f60a54a 100644 --- a/pkg/api/rest/configure_kubernikus.go +++ b/pkg/api/rest/configure_kubernikus.go @@ -47,6 +47,9 @@ func configureAPI(api *operations.KubernikusAPI) http.Handler { // Applies when the "x-auth-token" header is set api.KeystoneAuth = keystoneAuth() + // Set your custom authorizer if needed. Default one is security.Authorized() + // api.APIAuthorizer = security.Authorized() + rt := &apipkg.Runtime{Namespace: namespace} rt.Kubernikus, rt.Kubernetes = NewKubeClients() diff --git a/pkg/api/rest/doc.go b/pkg/api/rest/doc.go index 8058bc86a3..20debbf32c 100644 --- a/pkg/api/rest/doc.go +++ b/pkg/api/rest/doc.go @@ -1,6 +1,7 @@ // Code generated by go-swagger; DO NOT EDIT. -/*Package rest Kubernikus +/* +Package rest Kubernikus Schemes: http @@ -11,11 +12,9 @@ Consumes: - application/json - Produces: - application/json - swagger:meta */ package rest diff --git a/pkg/api/rest/operations/kubernikus_api.go b/pkg/api/rest/operations/kubernikus_api.go index 8f048c66ef..c15ef9c2c2 100644 --- a/pkg/api/rest/operations/kubernikus_api.go +++ b/pkg/api/rest/operations/kubernikus_api.go @@ -69,6 +69,9 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { KeystoneAuth: func(token string) (*models.Principal, error) { return nil, errors.NotImplemented("api key auth (keystone) x-auth-token from header param [x-auth-token] has not yet been implemented") }, + + // default authorizer is authorized meaning no requests are blocked + APIAuthorizer: security.Authorized(), } } @@ -102,6 +105,9 @@ type KubernikusAPI struct { // it performs authentication based on an api key x-auth-token provided in the header KeystoneAuth func(string) (*models.Principal, error) + // APIAuthorizer provides access control (ACL/RBAC/ABAC) by providing access to the request and authenticated principal + APIAuthorizer runtime.Authorizer + // CreateClusterHandler sets the operation handler for the create cluster operation CreateClusterHandler CreateClusterHandler // GetClusterCredentialsHandler sets the operation handler for the get cluster credentials operation @@ -254,6 +260,13 @@ func (o *KubernikusAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme } +// Authorizer returns the registered authorizer +func (o *KubernikusAPI) Authorizer() runtime.Authorizer { + + return o.APIAuthorizer + +} + // ConsumersFor gets the consumers for the specified media types func (o *KubernikusAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { diff --git a/pkg/api/rest/operations/terminate_cluster.go b/pkg/api/rest/operations/terminate_cluster.go index 99966f82e5..09e1aa2c29 100644 --- a/pkg/api/rest/operations/terminate_cluster.go +++ b/pkg/api/rest/operations/terminate_cluster.go @@ -74,4 +74,5 @@ func (o *TerminateCluster) ServeHTTP(rw http.ResponseWriter, r *http.Request) { // TerminateClusterAcceptedBody terminate cluster accepted body // swagger:model TerminateClusterAcceptedBody + type TerminateClusterAcceptedBody interface{} diff --git a/pkg/client/models/api_versions.go b/pkg/client/models/api_versions.go deleted file mode 100644 index 766acc2424..0000000000 --- a/pkg/client/models/api_versions.go +++ /dev/null @@ -1,65 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" - "github.com/go-openapi/validate" -) - -// APIVersions Api versions -// swagger:model ApiVersions -type APIVersions struct { - - // versions are the api versions that are available. - // Required: true - Versions []string `json:"versions"` -} - -// Validate validates this Api versions -func (m *APIVersions) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateVersions(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *APIVersions) validateVersions(formats strfmt.Registry) error { - - if err := validate.Required("versions", "body", m.Versions); err != nil { - return err - } - - return nil -} - -// MarshalBinary interface implementation -func (m *APIVersions) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *APIVersions) UnmarshalBinary(b []byte) error { - var res APIVersions - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/client/models/cluster.go b/pkg/client/models/cluster.go deleted file mode 100644 index 08d0644e29..0000000000 --- a/pkg/client/models/cluster.go +++ /dev/null @@ -1,597 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "strconv" - - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" - "github.com/go-openapi/validate" -) - -// Cluster cluster -// swagger:model Cluster -type Cluster struct { - - // name of the cluster - // Required: true - // Pattern: ^[a-z]([-a-z0-9]*[a-z0-9])?$ - Name *string `json:"name"` - - // spec - Spec ClusterSpec `json:"spec,omitempty"` - - // status - Status *ClusterStatus `json:"status,omitempty"` -} - -// Validate validates this cluster -func (m *Cluster) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateName(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateSpec(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateStatus(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *Cluster) validateName(formats strfmt.Registry) error { - - if err := validate.Required("name", "body", m.Name); err != nil { - return err - } - - if err := validate.Pattern("name", "body", string(*m.Name), `^[a-z]([-a-z0-9]*[a-z0-9])?$`); err != nil { - return err - } - - return nil -} - -func (m *Cluster) validateSpec(formats strfmt.Registry) error { - - if swag.IsZero(m.Spec) { // not required - return nil - } - - if err := m.Spec.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("spec") - } - return err - } - - return nil -} - -func (m *Cluster) validateStatus(formats strfmt.Registry) error { - - if swag.IsZero(m.Status) { // not required - return nil - } - - if m.Status != nil { - - if err := m.Status.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("status") - } - return err - } - } - - return nil -} - -// MarshalBinary interface implementation -func (m *Cluster) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *Cluster) UnmarshalBinary(b []byte) error { - var res Cluster - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterSpec cluster spec -// swagger:model ClusterSpec -type ClusterSpec struct { - - // CIDR Range for Pods in the cluster. Can not be updated. - // Pattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ - ClusterCIDR string `json:"clusterCIDR,omitempty"` - - // node pools - NodePools []*ClusterSpecNodePoolsItems0 `json:"nodePools"` - - // CIDR Range for Services in the cluster. Can not be updated. - // Pattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ - ServiceCIDR string `json:"serviceCIDR,omitempty"` -} - -// Validate validates this cluster spec -func (m *ClusterSpec) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateClusterCIDR(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateNodePools(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateServiceCIDR(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *ClusterSpec) validateClusterCIDR(formats strfmt.Registry) error { - - if swag.IsZero(m.ClusterCIDR) { // not required - return nil - } - - if err := validate.Pattern("spec"+"."+"clusterCIDR", "body", string(m.ClusterCIDR), `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$`); err != nil { - return err - } - - return nil -} - -func (m *ClusterSpec) validateNodePools(formats strfmt.Registry) error { - - if swag.IsZero(m.NodePools) { // not required - return nil - } - - for i := 0; i < len(m.NodePools); i++ { - - if swag.IsZero(m.NodePools[i]) { // not required - continue - } - - if m.NodePools[i] != nil { - - if err := m.NodePools[i].Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("spec" + "." + "nodePools" + "." + strconv.Itoa(i)) - } - return err - } - } - - } - - return nil -} - -func (m *ClusterSpec) validateServiceCIDR(formats strfmt.Registry) error { - - if swag.IsZero(m.ServiceCIDR) { // not required - return nil - } - - if err := validate.Pattern("spec"+"."+"serviceCIDR", "body", string(m.ServiceCIDR), `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$`); err != nil { - return err - } - - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterSpec) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterSpec) UnmarshalBinary(b []byte) error { - var res ClusterSpec - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterSpecNodePoolsItems0 cluster spec node pools items0 -// swagger:model ClusterSpecNodePoolsItems0 -type ClusterSpecNodePoolsItems0 struct { - - // flavor - // Required: true - Flavor *string `json:"flavor"` - - // image - Image string `json:"image,omitempty"` - - // name - // Required: true - // Pattern: ^[a-z]([a-z0-9]*)?$ - Name *string `json:"name"` - - // size - // Required: true - // Maximum: 127 - // Minimum: 0 - Size *int64 `json:"size"` -} - -// Validate validates this cluster spec node pools items0 -func (m *ClusterSpecNodePoolsItems0) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateFlavor(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateName(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateSize(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *ClusterSpecNodePoolsItems0) validateFlavor(formats strfmt.Registry) error { - - if err := validate.Required("flavor", "body", m.Flavor); err != nil { - return err - } - - return nil -} - -func (m *ClusterSpecNodePoolsItems0) validateName(formats strfmt.Registry) error { - - if err := validate.Required("name", "body", m.Name); err != nil { - return err - } - - if err := validate.Pattern("name", "body", string(*m.Name), `^[a-z]([a-z0-9]*)?$`); err != nil { - return err - } - - return nil -} - -func (m *ClusterSpecNodePoolsItems0) validateSize(formats strfmt.Registry) error { - - if err := validate.Required("size", "body", m.Size); err != nil { - return err - } - - if err := validate.MinimumInt("size", "body", int64(*m.Size), 0, false); err != nil { - return err - } - - if err := validate.MaximumInt("size", "body", int64(*m.Size), 127, false); err != nil { - return err - } - - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterSpecNodePoolsItems0) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterSpecNodePoolsItems0) UnmarshalBinary(b []byte) error { - var res ClusterSpecNodePoolsItems0 - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterStatus cluster status -// swagger:model ClusterStatus -type ClusterStatus struct { - - // kluster - Kluster *ClusterStatusKluster `json:"kluster,omitempty"` - - // node pools - NodePools []*ClusterStatusNodePoolsItems0 `json:"nodePools"` -} - -// Validate validates this cluster status -func (m *ClusterStatus) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateKluster(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateNodePools(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *ClusterStatus) validateKluster(formats strfmt.Registry) error { - - if swag.IsZero(m.Kluster) { // not required - return nil - } - - if m.Kluster != nil { - - if err := m.Kluster.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("status" + "." + "kluster") - } - return err - } - } - - return nil -} - -func (m *ClusterStatus) validateNodePools(formats strfmt.Registry) error { - - if swag.IsZero(m.NodePools) { // not required - return nil - } - - for i := 0; i < len(m.NodePools); i++ { - - if swag.IsZero(m.NodePools[i]) { // not required - continue - } - - if m.NodePools[i] != nil { - - if err := m.NodePools[i].Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("status" + "." + "nodePools" + "." + strconv.Itoa(i)) - } - return err - } - } - - } - - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterStatus) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterStatus) UnmarshalBinary(b []byte) error { - var res ClusterStatus - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterStatusKluster cluster status kluster -// swagger:model ClusterStatusKluster -type ClusterStatusKluster struct { - - // message - Message string `json:"message,omitempty"` - - // status of the cluster - State string `json:"state,omitempty"` -} - -// Validate validates this cluster status kluster -func (m *ClusterStatusKluster) Validate(formats strfmt.Registry) error { - var res []error - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterStatusKluster) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterStatusKluster) UnmarshalBinary(b []byte) error { - var res ClusterStatusKluster - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterStatusNodePoolsItems0 cluster status node pools items0 -// swagger:model ClusterStatusNodePoolsItems0 -type ClusterStatusNodePoolsItems0 struct { - - // healthy - // Required: true - Healthy *int64 `json:"healthy"` - - // name - // Required: true - Name *string `json:"name"` - - // running - // Required: true - Running *int64 `json:"running"` - - // schedulable - // Required: true - Schedulable *int64 `json:"schedulable"` - - // size - // Required: true - Size *int64 `json:"size"` -} - -// Validate validates this cluster status node pools items0 -func (m *ClusterStatusNodePoolsItems0) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateHealthy(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateName(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateRunning(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateSchedulable(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateSize(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *ClusterStatusNodePoolsItems0) validateHealthy(formats strfmt.Registry) error { - - if err := validate.Required("healthy", "body", m.Healthy); err != nil { - return err - } - - return nil -} - -func (m *ClusterStatusNodePoolsItems0) validateName(formats strfmt.Registry) error { - - if err := validate.Required("name", "body", m.Name); err != nil { - return err - } - - return nil -} - -func (m *ClusterStatusNodePoolsItems0) validateRunning(formats strfmt.Registry) error { - - if err := validate.Required("running", "body", m.Running); err != nil { - return err - } - - return nil -} - -func (m *ClusterStatusNodePoolsItems0) validateSchedulable(formats strfmt.Registry) error { - - if err := validate.Required("schedulable", "body", m.Schedulable); err != nil { - return err - } - - return nil -} - -func (m *ClusterStatusNodePoolsItems0) validateSize(formats strfmt.Registry) error { - - if err := validate.Required("size", "body", m.Size); err != nil { - return err - } - - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterStatusNodePoolsItems0) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterStatusNodePoolsItems0) UnmarshalBinary(b []byte) error { - var res ClusterStatusNodePoolsItems0 - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/client/models/cluster_info.go b/pkg/client/models/cluster_info.go deleted file mode 100644 index 4b5f12701e..0000000000 --- a/pkg/client/models/cluster_info.go +++ /dev/null @@ -1,196 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "strconv" - - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" -) - -// ClusterInfo cluster info -// swagger:model ClusterInfo -type ClusterInfo struct { - - // binaries - Binaries []*ClusterInfoBinariesItems0 `json:"binaries"` - - // setup command - SetupCommand string `json:"setupCommand,omitempty"` -} - -// Validate validates this cluster info -func (m *ClusterInfo) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateBinaries(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *ClusterInfo) validateBinaries(formats strfmt.Registry) error { - - if swag.IsZero(m.Binaries) { // not required - return nil - } - - for i := 0; i < len(m.Binaries); i++ { - - if swag.IsZero(m.Binaries[i]) { // not required - continue - } - - if m.Binaries[i] != nil { - - if err := m.Binaries[i].Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("binaries" + "." + strconv.Itoa(i)) - } - return err - } - } - - } - - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterInfo) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterInfo) UnmarshalBinary(b []byte) error { - var res ClusterInfo - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterInfoBinariesItems0 cluster info binaries items0 -// swagger:model ClusterInfoBinariesItems0 -type ClusterInfoBinariesItems0 struct { - - // links - Links []*ClusterInfoBinariesItems0LinksItems0 `json:"links"` - - // name - Name string `json:"name,omitempty"` -} - -// Validate validates this cluster info binaries items0 -func (m *ClusterInfoBinariesItems0) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateLinks(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *ClusterInfoBinariesItems0) validateLinks(formats strfmt.Registry) error { - - if swag.IsZero(m.Links) { // not required - return nil - } - - for i := 0; i < len(m.Links); i++ { - - if swag.IsZero(m.Links[i]) { // not required - continue - } - - if m.Links[i] != nil { - - if err := m.Links[i].Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("links" + "." + strconv.Itoa(i)) - } - return err - } - } - - } - - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterInfoBinariesItems0) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterInfoBinariesItems0) UnmarshalBinary(b []byte) error { - var res ClusterInfoBinariesItems0 - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} - -// ClusterInfoBinariesItems0LinksItems0 cluster info binaries items0 links items0 -// swagger:model ClusterInfoBinariesItems0LinksItems0 -type ClusterInfoBinariesItems0LinksItems0 struct { - - // link - Link string `json:"link,omitempty"` - - // platform - Platform string `json:"platform,omitempty"` -} - -// Validate validates this cluster info binaries items0 links items0 -func (m *ClusterInfoBinariesItems0LinksItems0) Validate(formats strfmt.Registry) error { - var res []error - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -// MarshalBinary interface implementation -func (m *ClusterInfoBinariesItems0LinksItems0) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *ClusterInfoBinariesItems0LinksItems0) UnmarshalBinary(b []byte) error { - var res ClusterInfoBinariesItems0LinksItems0 - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/client/models/credentials.go b/pkg/client/models/credentials.go deleted file mode 100644 index 2782782ef7..0000000000 --- a/pkg/client/models/credentials.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" -) - -// Credentials credentials -// swagger:model Credentials -type Credentials struct { - - // kubeconfig - Kubeconfig string `json:"kubeconfig,omitempty"` -} - -// Validate validates this credentials -func (m *Credentials) Validate(formats strfmt.Registry) error { - var res []error - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -// MarshalBinary interface implementation -func (m *Credentials) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *Credentials) UnmarshalBinary(b []byte) error { - var res Credentials - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/client/models/error.go b/pkg/client/models/error.go deleted file mode 100644 index e5f97fdd70..0000000000 --- a/pkg/client/models/error.go +++ /dev/null @@ -1,87 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" - "github.com/go-openapi/validate" -) - -// Error the error model is a model for all the error responses coming from Kubernikus -// -// swagger:model error -type Error struct { - - // The error code - // Required: true - Code *int64 `json:"code"` - - // link to help page explaining the error in more detail - HelpURL strfmt.URI `json:"helpUrl,omitempty"` - - // The error message - // Required: true - Message *string `json:"message"` -} - -// Validate validates this error -func (m *Error) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateCode(formats); err != nil { - // prop - res = append(res, err) - } - - if err := m.validateMessage(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *Error) validateCode(formats strfmt.Registry) error { - - if err := validate.Required("code", "body", m.Code); err != nil { - return err - } - - return nil -} - -func (m *Error) validateMessage(formats strfmt.Registry) error { - - if err := validate.Required("message", "body", m.Message); err != nil { - return err - } - - return nil -} - -// MarshalBinary interface implementation -func (m *Error) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *Error) UnmarshalBinary(b []byte) error { - var res Error - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/client/models/info.go b/pkg/client/models/info.go deleted file mode 100644 index 2559f21c15..0000000000 --- a/pkg/client/models/info.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" -) - -// Info info -// swagger:model Info -type Info struct { - - // version - Version string `json:"version,omitempty"` -} - -// Validate validates this info -func (m *Info) Validate(formats strfmt.Registry) error { - var res []error - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -// MarshalBinary interface implementation -func (m *Info) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *Info) UnmarshalBinary(b []byte) error { - var res Info - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/client/models/principal.go b/pkg/client/models/principal.go deleted file mode 100644 index 3f031edd7d..0000000000 --- a/pkg/client/models/principal.go +++ /dev/null @@ -1,78 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - strfmt "github.com/go-openapi/strfmt" - - "github.com/go-openapi/errors" - "github.com/go-openapi/swag" -) - -// Principal principal -// swagger:model Principal -type Principal struct { - - // account id - Account string `json:"account,omitempty"` - - // Identity Endpoint - AuthURL string `json:"authUrl,omitempty"` - - // user's domain name - Domain string `json:"domain,omitempty"` - - // userid - ID string `json:"id,omitempty"` - - // username - Name string `json:"name,omitempty"` - - // list of roles the user has in the given scope - Roles []string `json:"roles"` -} - -// Validate validates this principal -func (m *Principal) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateRoles(formats); err != nil { - // prop - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *Principal) validateRoles(formats strfmt.Registry) error { - - if swag.IsZero(m.Roles) { // not required - return nil - } - - return nil -} - -// MarshalBinary interface implementation -func (m *Principal) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *Principal) UnmarshalBinary(b []byte) error { - var res Principal - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/pkg/cmd/kubernikusctl/common/kubernikus.go b/pkg/cmd/kubernikusctl/common/kubernikus.go index 88f4a53803..fd1fa7b401 100644 --- a/pkg/cmd/kubernikusctl/common/kubernikus.go +++ b/pkg/cmd/kubernikusctl/common/kubernikus.go @@ -7,9 +7,9 @@ import ( "github.com/go-openapi/strfmt" "github.com/pkg/errors" - kubernikus "github.com/sapcc/kubernikus/pkg/client/kubernikus_generated" - "github.com/sapcc/kubernikus/pkg/client/kubernikus_generated/operations" - "github.com/sapcc/kubernikus/pkg/client/models" + kubernikus "github.com/sapcc/kubernikus/pkg/api/client" + "github.com/sapcc/kubernikus/pkg/api/client/operations" + "github.com/sapcc/kubernikus/pkg/api/models" ) type KubernikusClient struct { diff --git a/pkg/cmd/kubernikusctl/create/cluster.go b/pkg/cmd/kubernikusctl/create/cluster.go index ce08f591cc..7c4a3a1705 100644 --- a/pkg/cmd/kubernikusctl/create/cluster.go +++ b/pkg/cmd/kubernikusctl/create/cluster.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/sapcc/kubernikus/pkg/client/models" + "github.com/sapcc/kubernikus/pkg/api/models" "github.com/sapcc/kubernikus/pkg/cmd" ) diff --git a/vendor/github.com/PuerkitoBio/purell/README.md b/vendor/github.com/PuerkitoBio/purell/README.md index a78a3df651..09e8a32cbe 100644 --- a/vendor/github.com/PuerkitoBio/purell/README.md +++ b/vendor/github.com/PuerkitoBio/purell/README.md @@ -12,6 +12,7 @@ Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc]. ## 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. @@ -172,6 +173,7 @@ And with `FlagsUnsafeGreedy`: @opennota @pchristopher1275 @zenovich +@beeker1121 ## License diff --git a/vendor/github.com/PuerkitoBio/purell/purell.go b/vendor/github.com/PuerkitoBio/purell/purell.go index b79da64b32..645e1b76f7 100644 --- a/vendor/github.com/PuerkitoBio/purell/purell.go +++ b/vendor/github.com/PuerkitoBio/purell/purell.go @@ -15,8 +15,8 @@ import ( "github.com/PuerkitoBio/urlesc" "golang.org/x/net/idna" - "golang.org/x/text/secure/precis" "golang.org/x/text/unicode/norm" + "golang.org/x/text/width" ) // A set of normalization flags determines how a URL will @@ -150,22 +150,26 @@ func MustNormalizeURLString(u string, f NormalizationFlags) string { // 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) { - if parsed, e := url.Parse(u); e != nil { - return "", e - } else { - options := make([]precis.Option, 1, 3) - options[0] = precis.IgnoreCase - if f&FlagLowercaseHost == FlagLowercaseHost { - options = append(options, precis.FoldCase()) - } - options = append(options, precis.Norm(norm.NFC)) - profile := precis.NewFreeform(options...) - if parsed.Host, e = idna.ToASCII(profile.NewTransformer().String(parsed.Host)); e != nil { - return "", e - } - return NormalizeURL(parsed, f), nil + parsed, err := url.Parse(u) + if err != nil { + return "", err + } + + if f&FlagLowercaseHost == FlagLowercaseHost { + parsed.Host = strings.ToLower(parsed.Host) } - panic("Unreachable code.") + + // 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. diff --git a/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go b/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go index a598fe9288..d1b2ca6c0e 100644 --- a/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go +++ b/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go @@ -35,6 +35,7 @@ func TestUrlnorm(t *testing.T) { "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", diff --git a/vendor/github.com/PuerkitoBio/urlesc/.travis.yml b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml index 478630e505..ba6b225f91 100644 --- a/vendor/github.com/PuerkitoBio/urlesc/.travis.yml +++ b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml @@ -1,7 +1,11 @@ language: go go: - - 1.4 + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x - tip install: diff --git a/vendor/github.com/PuerkitoBio/urlesc/README.md b/vendor/github.com/PuerkitoBio/urlesc/README.md index bebe305e0d..57aff0a539 100644 --- a/vendor/github.com/PuerkitoBio/urlesc/README.md +++ b/vendor/github.com/PuerkitoBio/urlesc/README.md @@ -1,4 +1,4 @@ -urlesc [![Build Status](https://travis-ci.org/PuerkitoBio/urlesc.png?branch=master)](https://travis-ci.org/PuerkitoBio/urlesc) [![GoDoc](http://godoc.org/github.com/PuerkitoBio/urlesc?status.svg)](http://godoc.org/github.com/PuerkitoBio/urlesc) +urlesc [![Build Status](https://travis-ci.org/PuerkitoBio/urlesc.svg?branch=master)](https://travis-ci.org/PuerkitoBio/urlesc) [![GoDoc](http://godoc.org/github.com/PuerkitoBio/urlesc?status.svg)](http://godoc.org/github.com/PuerkitoBio/urlesc) ====== Package urlesc implements query escaping as per RFC 3986. diff --git a/vendor/github.com/asaskevich/govalidator/README.md b/vendor/github.com/asaskevich/govalidator/README.md index ff488a3eec..9d2e1357b1 100644 --- a/vendor/github.com/asaskevich/govalidator/README.md +++ b/vendor/github.com/asaskevich/govalidator/README.md @@ -1,8 +1,7 @@ govalidator =========== -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![views](https://sourcegraph.com/api/repos/github.com/asaskevich/govalidator/.counters/views.png)](https://sourcegraph.com/github.com/asaskevich/govalidator) -[![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043) -[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043) +[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js). @@ -12,8 +11,13 @@ 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 @@ -43,16 +47,19 @@ Here's some code to explain it: 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)) @@ -89,28 +96,27 @@ govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator func Abs(value float64) float64 func BlackList(str, chars string) string func ByteLength(str string, params ...string) bool -func StringLength(str string, params ...string) bool -func StringMatches(s 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 IsHost(s string) bool 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 IsDNSName(str string) bool func IsDivisibleBy(str, num string) bool func IsEmail(str string) bool func IsFilePath(str string) (bool, int) @@ -119,6 +125,7 @@ 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 @@ -127,6 +134,10 @@ 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 @@ -144,11 +155,13 @@ 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 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 @@ -165,12 +178,20 @@ 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) @@ -183,10 +204,12 @@ 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 @@ -246,59 +269,65 @@ 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, -"ascii": IsASCII, -"base64": IsBase64, -"creditcard": IsCreditCard, -"datauri": IsDataURI, -"dialstring": IsDialString, -"dns": IsDNSName, -"email": IsEmail, -"float": IsFloat, -"fullwidth": IsFullWidth, -"halfwidth": IsHalfWidth, +"utfletternum": IsUTFLetterNumeric, +"numeric": IsNumeric, +"utfnumeric": IsUTFNumeric, +"utfdigit": IsUTFDigit, "hexadecimal": IsHexadecimal, "hexcolor": IsHexcolor, -"host": IsHost, +"rgbcolor": IsRGBcolor, +"lowercase": IsLowerCase, +"uppercase": IsUpperCase, "int": IsInt, -"ip": IsIP, -"ipv4": IsIPv4, -"ipv6": IsIPv6, +"float": IsFloat, +"null": IsNull, +"uuid": IsUUID, +"uuidv3": IsUUIDv3, +"uuidv4": IsUUIDv4, +"uuidv5": IsUUIDv5, +"creditcard": IsCreditCard, "isbn10": IsISBN10, "isbn13": IsISBN13, "json": IsJSON, -"latitude": IsLatitude, -"longitude": IsLongitude, -"lowercase": IsLowerCase, -"mac": IsMAC, "multibyte": IsMultibyte, -"null": IsNull, -"numeric": IsNumeric, -"port": IsPort, +"ascii": IsASCII, "printableascii": IsPrintableASCII, -"requri": IsRequestURI, -"requrl": IsRequestURL, -"rgbcolor": IsRGBcolor, +"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, -"uppercase": IsUpperCase, -"url": IsURL, -"utfdigit": IsUTFDigit, -"utfletter": IsUTFLetter, -"utfletternum": IsUTFLetterNumeric, -"utfnumeric": IsUTFNumeric, -"uuid": IsUUID, -"uuidv3": IsUUIDv3, -"uuidv4": IsUUIDv4, -"uuidv5": IsUUIDv5, -"variablewidth": IsVariableWidth, +"rfc3339": IsRFC3339, +"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: @@ -383,6 +412,7 @@ Full information about code coverage is also available here: [govalidator on goc If you do have a contribution for the package feel free to put up a Pull Request or open Issue. #### 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) @@ -391,5 +421,3 @@ If you do have a contribution for the package feel free to put up a Pull Request * [Nathan Davies](https://github.com/nathj07) * [Matt Sanford](https://github.com/mzsanford) * [Simon ccl1115](https://github.com/ccl1115) - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/asaskevich/govalidator/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/vendor/github.com/asaskevich/govalidator/converter.go b/vendor/github.com/asaskevich/govalidator/converter.go index 737a1f1799..d69114c4b7 100644 --- a/vendor/github.com/asaskevich/govalidator/converter.go +++ b/vendor/github.com/asaskevich/govalidator/converter.go @@ -41,9 +41,5 @@ func ToInt(str string) (int64, error) { // ToBoolean convert the input string to a boolean. func ToBoolean(str string) (bool, error) { - res, err := strconv.ParseBool(str) - if err != nil { - res = false - } - return res, err + return strconv.ParseBool(str) } diff --git a/vendor/github.com/asaskevich/govalidator/converter_test.go b/vendor/github.com/asaskevich/govalidator/converter_test.go index 875560ebab..ecc457be86 100644 --- a/vendor/github.com/asaskevich/govalidator/converter_test.go +++ b/vendor/github.com/asaskevich/govalidator/converter_test.go @@ -62,9 +62,9 @@ func TestToFloat(t *testing.T) { func TestToJSON(t *testing.T) { tests := []interface{}{"test", map[string]string{"a": "b", "b": "c"}, func() error { return fmt.Errorf("Error") }} expected := [][]string{ - []string{"\"test\"", ""}, - []string{"{\"a\":\"b\",\"b\":\"c\"}", ""}, - []string{"", "json: unsupported type: func() error"}, + {"\"test\"", ""}, + {"{\"a\":\"b\",\"b\":\"c\"}", ""}, + {"", "json: unsupported type: func() error"}, } for i, test := range tests { actual, err := ToJSON(test) diff --git a/vendor/github.com/asaskevich/govalidator/numerics.go b/vendor/github.com/asaskevich/govalidator/numerics.go index 737cd47ca6..5be281f249 100644 --- a/vendor/github.com/asaskevich/govalidator/numerics.go +++ b/vendor/github.com/asaskevich/govalidator/numerics.go @@ -4,7 +4,7 @@ import "math" // Abs returns absolute value of number func Abs(value float64) float64 { - return value * Sign(value) + return math.Abs(value) } // Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise @@ -48,7 +48,7 @@ func InRange(value, left, right float64) bool { // IsWhole returns true if value is whole number func IsWhole(value float64) bool { - return Abs(math.Remainder(value, 1)) == 0 + return math.Remainder(value, 1) == 0 } // IsNatural returns true if value is natural number (positive and whole) diff --git a/vendor/github.com/asaskevich/govalidator/patterns.go b/vendor/github.com/asaskevich/govalidator/patterns.go index 42389bda02..5297595591 100644 --- a/vendor/github.com/asaskevich/govalidator/patterns.go +++ b/vendor/github.com/asaskevich/govalidator/patterns.go @@ -4,7 +4,7 @@ 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}])))\\.?$" + 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})$" @@ -14,7 +14,7 @@ const ( 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]+$" + 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]+$" @@ -29,11 +29,19 @@ const ( 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_-]{1,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9_-]{1,62})*$` - URL string = `^((ftp|https?):\/\/)?(\S+(:\S*)?@)?((([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]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([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,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$` + 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*)?@)` + Hostname string = `` + 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 = `^((?:\/[a-zA-Z0-9\.\:]+(?:_[a-zA-Z0-9\:\.]+)*(?:\-[\:a-zA-Z0-9\.]+)*)+\/?)$` + 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" ) diff --git a/vendor/github.com/asaskevich/govalidator/types.go b/vendor/github.com/asaskevich/govalidator/types.go index 5131beb90f..9a5207c588 100644 --- a/vendor/github.com/asaskevich/govalidator/types.go +++ b/vendor/github.com/asaskevich/govalidator/types.go @@ -15,7 +15,7 @@ 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 tagOptions []string +type tagOptionsMap map[string]string // UnsupportedTypeError is a wrapper for reflect.Type type UnsupportedTypeError struct { @@ -29,15 +29,21 @@ 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, } // 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+)\\)$"), - "matches": regexp.MustCompile(`matches\(([^)]+)\)`), + "in": regexp.MustCompile(`^in\((.*)\)`), + "matches": regexp.MustCompile(`^matches\((.+)\)$`), } type customTypeTagMap struct { @@ -113,6 +119,10 @@ var TagMap = map[string]Validator{ "longitude": IsLongitude, "ssn": IsSSN, "semver": IsSemver, + "rfc3339": IsRFC3339, + "ISO3166Alpha2": IsISO3166Alpha2, + "ISO3166Alpha3": IsISO3166Alpha3, + "ISO4217": IsISO4217, } // ISO3166Entry stores country codes @@ -376,3 +386,228 @@ var ISO3166List = []ISO3166Entry{ {"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"}, +} diff --git a/vendor/github.com/asaskevich/govalidator/utils.go b/vendor/github.com/asaskevich/govalidator/utils.go index 200b9131de..888c12751c 100644 --- a/vendor/github.com/asaskevich/govalidator/utils.go +++ b/vendor/github.com/asaskevich/govalidator/utils.go @@ -4,10 +4,12 @@ import ( "errors" "fmt" "html" + "math" "path" "regexp" "strings" "unicode" + "unicode/utf8" ) // Contains check if the string contains the substring. @@ -25,27 +27,21 @@ func Matches(str, pattern string) bool { // 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 { - pattern := "" if chars == "" { - pattern = "^\\s+" - } else { - pattern = "^[" + chars + "]+" + return strings.TrimLeftFunc(str, unicode.IsSpace) } - r, _ := regexp.Compile(pattern) - return string(r.ReplaceAll([]byte(str), []byte(""))) + 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 { - pattern := "" if chars == "" { - pattern = "\\s+$" - } else { - pattern = "[" + chars + "]+$" + return strings.TrimRightFunc(str, unicode.IsSpace) } - r, _ := regexp.Compile(pattern) - return string(r.ReplaceAll([]byte(str), []byte(""))) + r, _ := regexp.Compile("[" + chars + "]+$") + return r.ReplaceAllString(str, "") } // Trim trim characters from both sides of the input. @@ -58,14 +54,14 @@ func Trim(str, chars string) string { func WhiteList(str, chars string) string { pattern := "[^" + chars + "]+" r, _ := regexp.Compile(pattern) - return string(r.ReplaceAll([]byte(str), []byte(""))) + 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 string(r.ReplaceAll([]byte(str), []byte(""))) + return r.ReplaceAllString(str, "") } // StripLow remove characters with a numerical value < 32 and 127, mostly control characters. @@ -83,7 +79,7 @@ func StripLow(str string, keepNewLines bool) string { // ReplacePattern replace regular expression pattern in string func ReplacePattern(str, pattern, replace string) string { r, _ := regexp.Compile(pattern) - return string(r.ReplaceAll([]byte(str), []byte(replace))) + return r.ReplaceAllString(str, replace) } // Escape replace <, >, & and " with HTML entities. @@ -211,3 +207,56 @@ func Truncate(str string, length int, ending string) string { 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 +} diff --git a/vendor/github.com/asaskevich/govalidator/utils_test.go b/vendor/github.com/asaskevich/govalidator/utils_test.go index 4a7ec24450..154f315945 100644 --- a/vendor/github.com/asaskevich/govalidator/utils_test.go +++ b/vendor/github.com/asaskevich/govalidator/utils_test.go @@ -426,3 +426,75 @@ func TestTruncate(t *testing.T) { } } } + +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) + } + } +} diff --git a/vendor/github.com/asaskevich/govalidator/validator.go b/vendor/github.com/asaskevich/govalidator/validator.go index d0fec2c357..b699e44490 100644 --- a/vendor/github.com/asaskevich/govalidator/validator.go +++ b/vendor/github.com/asaskevich/govalidator/validator.go @@ -11,12 +11,19 @@ import ( "sort" "strconv" "strings" - + "time" "unicode" "unicode/utf8" ) -var fieldsRequiredByDefault bool +var ( + fieldsRequiredByDefault bool + notNumberRegexp = regexp.MustCompile("[^0-9]+") + whiteSpacesAndMinus = regexp.MustCompile("[\\s-]+") +) + +const maxURLRuneCount = 2083 +const minURLRuneCount = 3 // 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"`). @@ -44,7 +51,7 @@ func IsEmail(str string) bool { // IsURL check if the string is an URL. func IsURL(str string) bool { - if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { + if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") { return false } u, err := url.Parse(str) @@ -62,7 +69,7 @@ func IsURL(str string) bool { } // IsRequestURL check if the string rawurl, assuming -// it was recieved in an HTTP request, is a valid +// it was received in an HTTP request, is a valid // URL confirm to RFC 3986 func IsRequestURL(rawurl string) bool { url, err := url.ParseRequestURI(rawurl) @@ -76,7 +83,7 @@ func IsRequestURL(rawurl string) bool { } // IsRequestURI check if the string rawurl, assuming -// it was recieved in an HTTP request, is an +// it was received in an HTTP request, is an // absolute URI or an absolute path. func IsRequestURI(rawurl string) bool { _, err := url.ParseRequestURI(rawurl) @@ -269,9 +276,8 @@ func IsUUID(str string) bool { // IsCreditCard check if the string is a credit card. func IsCreditCard(str string) bool { - r, _ := regexp.Compile("[^0-9]+") - sanitized := r.ReplaceAll([]byte(str), []byte("")) - if !rxCreditCard.MatchString(string(sanitized)) { + sanitized := notNumberRegexp.ReplaceAllString(str, "") + if !rxCreditCard.MatchString(sanitized) { return false } var sum int64 @@ -279,7 +285,7 @@ func IsCreditCard(str string) bool { var tmpNum int64 var shouldDouble bool for i := len(sanitized) - 1; i >= 0; i-- { - digit = string(sanitized[i:(i + 1)]) + digit = sanitized[i:(i + 1)] tmpNum, _ = ToInt(digit) if shouldDouble { tmpNum *= 2 @@ -313,12 +319,11 @@ func IsISBN13(str string) bool { // IsISBN check if the string is an ISBN (version 10 or 13). // If version value is not equal to 10 or 13, it will be check both variants. func IsISBN(str string, version int) bool { - r, _ := regexp.Compile("[\\s-]+") - sanitized := r.ReplaceAll([]byte(str), []byte("")) + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") var checksum int32 var i int32 if version == 10 { - if !rxISBN10.MatchString(string(sanitized)) { + if !rxISBN10.MatchString(sanitized) { return false } for i = 0; i < 9; i++ { @@ -334,7 +339,7 @@ func IsISBN(str string, version int) bool { } return false } else if version == 13 { - if !rxISBN13.MatchString(string(sanitized)) { + if !rxISBN13.MatchString(sanitized) { return false } factor := []int32{1, 3} @@ -452,13 +457,33 @@ func IsISO3166Alpha3(str string) bool { return false } +// IsISO693Alpha2 checks if a string is valid two-letter language code +func IsISO693Alpha2(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO693Alpha3b checks if a string is valid three-letter language code +func IsISO693Alpha3b(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha3bCode { + return true + } + } + return false +} + // IsDNSName will validate the given string as a DNS name func IsDNSName(str string) bool { if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { // constraints already violated return false } - return rxDNSName.MatchString(str) + return !IsIP(str) && rxDNSName.MatchString(str) } // IsDialString validates the given string for usage with the various Dial() functions @@ -496,6 +521,12 @@ func IsIPv6(str string) bool { return ip != nil && strings.Contains(str, ":") } +// IsCIDR check if the string is an valid CIDR notiation (IPV4 & IPV6) +func IsCIDR(str string) bool { + _, _, err := net.ParseCIDR(str) + return err == nil +} + // IsMAC check if a string is valid MAC address. // Possible MAC formats: // 01:23:45:67:89:ab @@ -529,6 +560,17 @@ func IsLongitude(str string) bool { return rxLongitude.MatchString(str) } +func toJSONName(tag string) string { + if tag == "" { + return "" + } + + // JSON name always comes first. If there's no options then split[0] is + // JSON name, if JSON name is not set, then split[0] is an empty string. + split := strings.SplitN(tag, ",", 2) + return split[0] +} + // ValidateStruct use tags for fields. // result will be equal to `false` if there are any errors. func ValidateStruct(s interface{}) (bool, error) { @@ -552,11 +594,32 @@ func ValidateStruct(s interface{}) (bool, error) { if typeField.PkgPath != "" { continue // Private field } - resultField, err2 := typeCheck(valueField, typeField, val) + structResult := true + if valueField.Kind() == reflect.Struct && typeField.Tag.Get(tagName) != "-" { + var err error + structResult, err = ValidateStruct(valueField.Interface()) + if err != nil { + errs = append(errs, err) + } + } + resultField, err2 := typeCheck(valueField, typeField, val, nil) if err2 != nil { + + // Replace structure name with JSON name if there is a tag on the variable + jsonTag := toJSONName(typeField.Tag.Get("json")) + if jsonTag != "" { + switch jsonError := err2.(type) { + case Error: + jsonError.Name = jsonTag + err2 = jsonError + case Errors: + err2 = jsonError + } + } + errs = append(errs, err2) } - result = result && resultField + result = result && resultField && structResult } if len(errs) > 0 { err = errs @@ -564,11 +627,22 @@ func ValidateStruct(s interface{}) (bool, error) { return result, err } -// parseTag splits a struct field's tag into its -// comma-separated options. -func parseTag(tag string) tagOptions { - split := strings.SplitN(tag, ",", -1) - return tagOptions(split) +// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""} +func parseTagIntoMap(tag string) tagOptionsMap { + optionsMap := make(tagOptionsMap) + options := strings.SplitN(tag, ",", -1) + for _, option := range options { + validationOptions := strings.Split(option, "~") + if !isValidTag(validationOptions[0]) { + continue + } + if len(validationOptions) == 2 { + optionsMap[validationOptions[0]] = validationOptions[1] + } else { + optionsMap[validationOptions[0]] = "" + } + } + return optionsMap } func isValidTag(s string) bool { @@ -577,7 +651,7 @@ func isValidTag(s string) bool { } for _, c := range s { switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. @@ -603,6 +677,28 @@ func IsSemver(str string) bool { return rxSemver.MatchString(str) } +// IsTime check if string is valid according to given format +func IsTime(str string, format string) bool { + _, err := time.Parse(format, str) + return err == nil +} + +// IsRFC3339 check if string is valid timestamp value according to RFC3339 +func IsRFC3339(str string) bool { + return IsTime(str, time.RFC3339) +} + +// IsISO4217 check if string is valid ISO currency code +func IsISO4217(str string) bool { + for _, currency := range ISO4217List { + if str == currency { + return true + } + } + + return false +} + // ByteLength check string's length func ByteLength(str string, params ...string) bool { if len(params) == 2 { @@ -614,6 +710,12 @@ func ByteLength(str string, params ...string) bool { return false } +// RuneLength check string's length +// Alias for StringLength +func RuneLength(str string, params ...string) bool { + return StringLength(str, params...) +} + // StringMatches checks if a string matches a given pattern. func StringMatches(s string, params ...string) bool { if len(params) == 1 { @@ -636,52 +738,55 @@ func StringLength(str string, params ...string) bool { return false } -// Contains returns whether checks that a comma-separated list of options -// contains a particular substr flag. substr must be surrounded by a -// string boundary or commas. -func (opts tagOptions) contains(optionName string) bool { - for i := range opts { - tagOpt := opts[i] - if tagOpt == optionName { - return true - } +// Range check string's length +func Range(str string, params ...string) bool { + if len(params) == 2 { + value, _ := ToFloat(str) + min, _ := ToFloat(params[0]) + max, _ := ToFloat(params[1]) + return InRange(value, min, max) + } + + return false +} + +func isInRaw(str string, params ...string) bool { + if len(params) == 1 { + rawParams := params[0] + + parsedParams := strings.Split(rawParams, "|") + + return IsIn(str, parsedParams...) } + return false } -func searchOption(limit int, predicate func(counter int) bool) int { - for counter := 0; counter < limit; counter++ { - if predicate(counter) { - return counter +// IsIn check if string str is a member of the set of strings params +func IsIn(str string, params ...string) bool { + for _, param := range params { + if str == param { + return true } } - return -1 + + return false } -func checkRequired(v reflect.Value, t reflect.StructField, options tagOptions) (bool, error) { - var err error - var customErrorMessageExists bool - requiredIndex := searchOption(len(options), func(index int) bool { return strings.HasPrefix(options[index], "required") }) - optionalIndex := searchOption(len(options), func(index int) bool { return strings.HasPrefix(options[index], "optional") }) - if requiredIndex > -1 { - validationOptions := strings.Split(options[requiredIndex], "~") - if len(validationOptions) == 2 { - err = fmt.Errorf(strings.Split(options[requiredIndex], "~")[1]) - customErrorMessageExists = true - } else { - err = fmt.Errorf("non zero value required") +func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) { + if requiredOption, isRequired := options["required"]; isRequired { + if len(requiredOption) > 0 { + return false, Error{t.Name, fmt.Errorf(requiredOption), true} } - return false, Error{t.Name, err, customErrorMessageExists} - } else if fieldsRequiredByDefault && optionalIndex == -1 { - err := fmt.Errorf("All fields are required to at least have one validation defined") - return false, Error{t.Name, err, customErrorMessageExists} + return false, Error{t.Name, fmt.Errorf("non zero value required"), false} + } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional { + return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false} } // not required and empty is valid return true, nil } -func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, error) { - var customErrorMessageExists bool +func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) { if !v.IsValid() { return false, nil } @@ -694,41 +799,56 @@ func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, e if !fieldsRequiredByDefault { return true, nil } - err := fmt.Errorf("All fields are required to at least have one validation defined") - return false, Error{t.Name, err, customErrorMessageExists} + return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false} case "-": return true, nil } - options := parseTag(tag) + isRootType := false + if options == nil { + isRootType = true + options = parseTagIntoMap(tag) + } + + if isEmptyValue(v) { + // an empty value is not validated, check only required + return checkRequired(v, t, options) + } + var customTypeErrors Errors - var customTypeValidatorsExist bool - for _, tagOpt := range options { - tagOpts := strings.Split(tagOpt, "~") - if ok := isValidTag(tagOpts[0]); !ok { - continue - } - if validatefunc, ok := CustomTypeTagMap.Get(tagOpts[0]); ok { - customTypeValidatorsExist = true + for validatorName, customErrorMessage := range options { + if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok { + delete(options, validatorName) + if result := validatefunc(v.Interface(), o.Interface()); !result { - if len(tagOpts) == 2 { - customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf(tagOpts[1]), CustomErrorMessageExists: true}) + if len(customErrorMessage) > 0 { + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf(customErrorMessage), CustomErrorMessageExists: true}) continue } - customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOpts[0]), CustomErrorMessageExists: false}) + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false}) } } } - if customTypeValidatorsExist { - if len(customTypeErrors.Errors()) > 0 { - return false, customTypeErrors - } - return true, nil + + if len(customTypeErrors.Errors()) > 0 { + return false, customTypeErrors } - if isEmptyValue(v) { - // an empty value is not validated, check only required - return checkRequired(v, t, options) + if isRootType { + // Ensure that we've checked the value by all specified validators before report that the value is valid + defer func() { + delete(options, "optional") + delete(options, "required") + + if isValid && resultErr == nil && len(options) != 0 { + for validator := range options { + isValid = false + resultErr = Error{t.Name, fmt.Errorf( + "The following validator is invalid or can't be applied to the field: %q", validator), false} + return + } + } + }() } switch v.Kind() { @@ -738,82 +858,67 @@ func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, e reflect.Float32, reflect.Float64, reflect.String: // for each tag option check the map of validator functions - for i := range options { - tagOpt := options[i] - tagOptions := strings.Split(tagOpt, "~") - negate := false - customMsgExists := (len(tagOptions) == 2) - // Check wether the tag looks like '!something' or 'something' - if len(tagOptions[0]) > 0 && tagOptions[0][0] == '!' { - tagOpt = string(tagOptions[0][1:]) - tagOptions[0] = tagOpt + for validatorSpec, customErrorMessage := range options { + var negate bool + validator := validatorSpec + customMsgExists := len(customErrorMessage) > 0 + + // Check whether the tag looks like '!something' or 'something' + if validator[0] == '!' { + validator = validator[1:] negate = true } - if ok := isValidTag(tagOptions[0]); !ok { - err := fmt.Errorf("Unknown Validator %s", tagOptions[0]) - return false, Error{t.Name, err, false} - } // Check for param validators for key, value := range ParamTagRegexMap { - ps := value.FindStringSubmatch(tagOptions[0]) - if len(ps) > 0 { - if validatefunc, ok := ParamTagMap[key]; ok { - switch v.Kind() { - case reflect.String: - field := fmt.Sprint(v) // make value into string, then validate with regex - if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { - var err error - if !negate { - if customMsgExists { - err = fmt.Errorf(tagOptions[1]) - } else { - err = fmt.Errorf("%s does not validate as %s", field, tagOpt) - } - - } else { - if customMsgExists { - err = fmt.Errorf(tagOptions[1]) - } else { - err = fmt.Errorf("%s does validate as %s", field, tagOpt) - } - } - return false, Error{t.Name, err, customMsgExists} - } - default: - //Not Yet Supported Types (Fail here!) - err := fmt.Errorf("Validator %s doesn't support kind %s", tagOptions[0], v.Kind()) - return false, Error{t.Name, err, false} + ps := value.FindStringSubmatch(validator) + if len(ps) == 0 { + continue + } + + validatefunc, ok := ParamTagMap[key] + if !ok { + continue + } + + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String: + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { + if customMsgExists { + return false, Error{t.Name, fmt.Errorf(customErrorMessage), customMsgExists} } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists} } + default: + // type not yet supported, fail + return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false} } } - if validatefunc, ok := TagMap[tagOptions[0]]; ok { + if validatefunc, ok := TagMap[validator]; ok { + delete(options, validatorSpec) + switch v.Kind() { case reflect.String: field := fmt.Sprint(v) // make value into string, then validate with regex if result := validatefunc(field); !result && !negate || result && negate { - var err error - - if !negate { - if customMsgExists { - err = fmt.Errorf(tagOptions[1]) - } else { - err = fmt.Errorf("%s does not validate as %s", field, tagOpt) - } - } else { - if customMsgExists { - err = fmt.Errorf(tagOptions[1]) - } else { - err = fmt.Errorf("%s does validate as %s", field, tagOpt) - } + if customMsgExists { + return false, Error{t.Name, fmt.Errorf(customErrorMessage), customMsgExists} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists} } - return false, Error{t.Name, err, customMsgExists} + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists} } default: //Not Yet Supported Types (Fail here!) - err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", tagOptions[0], v.Kind(), v) + err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v) return false, Error{t.Name, err, false} } } @@ -835,32 +940,13 @@ func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, e result = result && resultItem } return result, nil - case reflect.Slice: - result := true - for i := 0; i < v.Len(); i++ { - var resultItem bool - var err error - if v.Index(i).Kind() != reflect.Struct { - resultItem, err = typeCheck(v.Index(i), t, o) - if err != nil { - return false, err - } - } else { - resultItem, err = ValidateStruct(v.Index(i).Interface()) - if err != nil { - return false, err - } - } - result = result && resultItem - } - return result, nil - case reflect.Array: + case reflect.Slice, reflect.Array: result := true for i := 0; i < v.Len(); i++ { var resultItem bool var err error if v.Index(i).Kind() != reflect.Struct { - resultItem, err = typeCheck(v.Index(i), t, o) + resultItem, err = typeCheck(v.Index(i), t, o, options) if err != nil { return false, err } @@ -884,7 +970,7 @@ func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, e if v.IsNil() { return true, nil } - return typeCheck(v.Elem(), t, o) + return typeCheck(v.Elem(), t, o, options) case reflect.Struct: return ValidateStruct(v.Interface()) default: @@ -937,7 +1023,10 @@ func ErrorsByField(e error) map[string]string { m[e.(Error).Name] = e.(Error).Err.Error() case Errors: for _, item := range e.(Errors).Errors() { - m[item.(Error).Name] = item.(Error).Err.Error() + n := ErrorsByField(item) + for k, v := range n { + m[k] = v + } } } diff --git a/vendor/github.com/asaskevich/govalidator/validator_test.go b/vendor/github.com/asaskevich/govalidator/validator_test.go index 2d6b77191e..3537232d55 100644 --- a/vendor/github.com/asaskevich/govalidator/validator_test.go +++ b/vendor/github.com/asaskevich/govalidator/validator_test.go @@ -4,8 +4,18 @@ import ( "fmt" "strings" "testing" + "time" ) +func init() { + CustomTypeTagMap.Set("customFalseValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return false + })) + CustomTypeTagMap.Set("customTrueValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return true + })) +} + func TestIsAlpha(t *testing.T) { t.Parallel() @@ -274,10 +284,10 @@ func TestIsNumeric(t *testing.T) { {"\u0030", true}, //UTF-8(ASCII): 0 {"123", true}, {"0123", true}, - {"-00123", true}, - {"+00123", true}, + {"-00123", false}, + {"+00123", false}, {"0", true}, - {"-0", true}, + {"-0", false}, {"123.123", false}, {" ", false}, {".", false}, @@ -296,7 +306,7 @@ func TestIsNumeric(t *testing.T) { {"1+1", false}, {"+", false}, {"++", false}, - {"+1", true}, + {"+1", false}, } for _, test := range tests { actual := IsNumeric(test.param) @@ -539,6 +549,8 @@ func TestIsEmail(t *testing.T) { {"foo@bar.com.au", true}, {"foo+bar@bar.com", true}, {"foo@bar.coffee", true}, + {"foo@bar.coffee..coffee", false}, + {"foo@bar.bar.coffee", true}, {"foo@bar.中文网", true}, {"invalidemail@", false}, {"invalid.com", false}, @@ -589,7 +601,7 @@ func TestIsURL(t *testing.T) { {"http://foobar.c_o_m", false}, {"", false}, {"xyz://foobar.com", false}, - {"invalid.", false}, + // {"invalid.", false}, is it false like "localhost."? {".com", false}, {"rtmp://foobar.com", false}, {"http://www.foo_bar.com/", false}, @@ -599,6 +611,7 @@ func TestIsURL(t *testing.T) { {"http://www.foobar.com/~foobar", true}, {"http://www.-foobar.com/", false}, {"http://www.foo---bar.com/", false}, + {"http://r6---snnvoxuioq6.googlevideo.com", true}, {"mailto:someone@example.com", true}, {"irc://irc.server.org/channel", false}, {"irc://#channel@network", true}, @@ -615,6 +628,7 @@ func TestIsURL(t *testing.T) { {"http://.foo.com", false}, {"http://,foo.com", false}, {",foo.com", false}, + {"http://myservice.:9093/", true}, // according to issues #62 #66 {"https://pbs.twimg.com/profile_images/560826135676588032/j8fWrmYY_normal.jpeg", true}, // according to #125 @@ -630,6 +644,23 @@ func TestIsURL(t *testing.T) { {"http://cant-end-with-hyphen-.example.com", false}, {"http://-cant-start-with-hyphen.example.com", false}, {"http://www.domain-can-have-dashes.com", true}, + {"http://m.abcd.com/test.html", true}, + {"http://m.abcd.com/a/b/c/d/test.html?args=a&b=c", true}, + {"http://[::1]:9093", true}, + {"http://[::1]:909388", false}, + {"1200::AB00:1234::2552:7777:1313", false}, + {"http://[2001:db8:a0b:12f0::1]/index.html", true}, + {"http://[1200:0000:AB00:1234:0000:2552:7777:1313]", true}, + {"http://user:pass@[::1]:9093/a/b/c/?a=v#abc", true}, + {"https://127.0.0.1/a/b/c?a=v&c=11d", true}, + {"https://foo_bar.example.com", true}, + {"http://foo_bar.example.com", true}, + {"http://foo_bar_fizz_buzz.example.com", true}, + {"http://_cant_start_with_underescore", false}, + {"http://cant_end_with_underescore_", false}, + {"foo_bar.example.com", true}, + {"foo_bar_fizz_buzz.example.com", true}, + {"http://hello_world.example.com", true}, } for _, test := range tests { actual := IsURL(test.param) @@ -1380,6 +1411,64 @@ func TestIsISO3166Alpha3(t *testing.T) { } } +func TestIsISO693Alpha2(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"abcd", false}, + {"a", false}, + {"ac", false}, + {"ap", false}, + {"de", true}, + {"DE", false}, + {"mk", true}, + {"mac", false}, + {"sw", true}, + {"SW", false}, + {"ger", false}, + {"deu", false}, + } + for _, test := range tests { + actual := IsISO693Alpha2(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO693Alpha2(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO693Alpha3b(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"abcd", false}, + {"a", false}, + {"ac", false}, + {"ap", false}, + {"de", false}, + {"DE", false}, + {"mkd", false}, + {"mac", true}, + {"sw", false}, + {"SW", false}, + {"ger", true}, + {"deu", false}, + } + for _, test := range tests { + actual := IsISO693Alpha3b(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO693Alpha3b(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + func TestIsIP(t *testing.T) { t.Parallel() @@ -1477,19 +1566,27 @@ func TestIsDNSName(t *testing.T) { expected bool }{ {"localhost", true}, + {"a.bc", true}, + {"a.b.", true}, + {"a.b..", false}, {"localhost.local", true}, {"localhost.localdomain.intern", true}, + {"l.local.intern", true}, + {"ru.link.n.svpncloud.com", true}, {"-localhost", false}, {"localhost.-localdomain", false}, {"localhost.localdomain.-int", false}, - {"_localhost", false}, - {"localhost._localdomain", false}, - {"localhost.localdomain._int", false}, + {"_localhost", true}, + {"localhost._localdomain", true}, + {"localhost.localdomain._int", true}, {"lÖcalhost", false}, {"localhost.lÖcaldomain", false}, {"localhost.localdomain.üntern", false}, + {"__", true}, + {"localhost/", false}, {"127.0.0.1", false}, {"[::1]", false}, + {"50.50.50.50", false}, {"localhost.localdomain.intern:65535", false}, {"漢字汉字", false}, {"www.jubfvq1v3p38i51622y0dvmdk1mymowjyeu26gbtw9andgynj1gg8z3msb1kl5z6906k846pj3sulm4kiyk82ln5teqj9nsht59opr0cs5ssltx78lfyvml19lfq1wp4usbl0o36cmiykch1vywbttcus1p9yu0669h8fj4ll7a6bmop505908s1m83q2ec2qr9nbvql2589adma3xsq2o38os2z3dmfh2tth4is4ixyfasasasefqwe4t2ub2fz1rme.de", false}, @@ -1598,6 +1695,13 @@ func TestFilePath(t *testing.T) { {"/path/file:SAMPLE/", true, Unix}, {"/path/file:/.txt", true, Unix}, {"/path", true, Unix}, + {"/path/__bc/file.txt", true, Unix}, + {"/path/a--ac/file.txt", true, Unix}, + {"/_path/file.txt", true, Unix}, + {"/path/__bc/file.txt", true, Unix}, + {"/path/a--ac/file.txt", true, Unix}, + {"/__path/--file.txt", true, Unix}, + {"/path/a bc", true, Unix}, } for _, test := range tests { actual, osType := IsFilePath(test.param) @@ -1729,6 +1833,79 @@ func TestIsSemver(t *testing.T) { } } +func TestIsTime(t *testing.T) { + t.Parallel() + var tests = []struct { + param string + format string + expected bool + }{ + {"2016-12-31 11:00", time.RFC3339, false}, + {"2016-12-31 11:00:00", time.RFC3339, false}, + {"2016-12-31T11:00", time.RFC3339, false}, + {"2016-12-31T11:00:00", time.RFC3339, false}, + {"2016-12-31T11:00:00Z", time.RFC3339, true}, + {"2016-12-31T11:00:00+01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00-01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00.05Z", time.RFC3339, true}, + {"2016-12-31T11:00:00.05-01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00.05+01:00", time.RFC3339, true}, + } + for _, test := range tests { + actual := IsTime(test.param, test.format) + if actual != test.expected { + t.Errorf("Expected IsTime(%q, time.RFC3339) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsRFC3339(t *testing.T) { + t.Parallel() + var tests = []struct { + param string + expected bool + }{ + {"2016-12-31 11:00", false}, + {"2016-12-31 11:00:00", false}, + {"2016-12-31T11:00", false}, + {"2016-12-31T11:00:00", false}, + {"2016-12-31T11:00:00Z", true}, + {"2016-12-31T11:00:00+01:00", true}, + {"2016-12-31T11:00:00-01:00", true}, + {"2016-12-31T11:00:00.05Z", true}, + {"2016-12-31T11:00:00.05-01:00", true}, + {"2016-12-31T11:00:00.05+01:00", true}, + } + for _, test := range tests { + actual := IsRFC3339(test.param) + if actual != test.expected { + t.Errorf("Expected IsRFC3339(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO4217(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"ABCD", false}, + {"A", false}, + {"ZZZ", false}, + {"usd", false}, + {"USD", true}, + } + for _, test := range tests { + actual := IsISO4217(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO4217(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + func TestByteLength(t *testing.T) { t.Parallel() @@ -1742,6 +1919,7 @@ func TestByteLength(t *testing.T) { {"1239999", "0", "0", false}, {"1239asdfasf99", "100", "200", false}, {"1239999asdff29", "10", "30", true}, + {"你", "0", "1", false}, } for _, test := range tests { actual := ByteLength(test.value, test.min, test.max) @@ -1751,6 +1929,29 @@ func TestByteLength(t *testing.T) { } } +func TestRuneLength(t *testing.T) { + t.Parallel() + + var tests = []struct { + value string + min string + max string + expected bool + }{ + {"123456", "0", "100", true}, + {"1239999", "0", "0", false}, + {"1239asdfasf99", "100", "200", false}, + {"1239999asdff29", "10", "30", true}, + {"你", "0", "1", true}, + } + for _, test := range tests { + actual := RuneLength(test.value, test.min, test.max) + if actual != test.expected { + t.Errorf("Expected RuneLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual) + } + } +} + func TestStringLength(t *testing.T) { t.Parallel() @@ -1777,6 +1978,30 @@ func TestStringLength(t *testing.T) { } } +func TestIsIn(t *testing.T) { + t.Parallel() + + var tests = []struct { + value string + params []string + expected bool + }{ + {"PRESENT", []string{"PRESENT"}, true}, + {"PRESENT", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, true}, + {"PRÉSENTE", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, true}, + {"PRESENT", []string{}, false}, + {"PRESENT", nil, false}, + {"ABSENT", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, false}, + {"", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, false}, + } + for _, test := range tests { + actual := IsIn(test.value, test.params...) + if actual != test.expected { + t.Errorf("Expected IsIn(%s, %v) to be %v, got %v", test.value, test.params, test.expected, actual) + } + } +} + type Address struct { Street string `valid:"-"` Zip string `json:"zip" valid:"numeric,required"` @@ -1797,7 +2022,7 @@ type UserValid struct { Password string `valid:"required"` Age int `valid:"required"` Home *Address - Work []Address + Work []Address `valid:"required"` } type PrivateStruct struct { @@ -1827,13 +2052,22 @@ type StringMatchesStruct struct { StringMatches string `valid:"matches(^[0-9]{3}$)"` } +// TODO: this testcase should be fixed +// type StringMatchesComplexStruct struct { +// StringMatches string `valid:"matches(^\\$\\([\"']\\w+[\"']\\)$)"` +// } + +type IsInStruct struct { + IsIn string `valid:"in(PRESENT|PRÉSENTE|NOTABSENT)"` +} + type Post struct { Title string `valid:"alpha,required"` Message string `valid:"ascii"` AuthorIP string `valid:"ipv4"` } -type MissingValidationDeclationStruct struct { +type MissingValidationDeclarationStruct struct { Name string `` Email string `valid:"required,email"` } @@ -1853,13 +2087,13 @@ type MessageWithSeveralFieldsStruct struct { Body string `valid:"length(1|10)"` } -func TestValidateMissingValidationDeclationStruct(t *testing.T) { +func TestValidateMissingValidationDeclarationStruct(t *testing.T) { var tests = []struct { - param MissingValidationDeclationStruct + param MissingValidationDeclarationStruct expected bool }{ - {MissingValidationDeclationStruct{}, false}, - {MissingValidationDeclationStruct{Name: "TEST", Email: "test@example.com"}, false}, + {MissingValidationDeclarationStruct{}, false}, + {MissingValidationDeclarationStruct{Name: "TEST", Email: "test@example.com"}, false}, } SetFieldsRequiredByDefault(true) for _, test := range tests { @@ -1921,6 +2155,50 @@ func TestFieldsRequiredByDefaultButExemptOrOptionalStruct(t *testing.T) { SetFieldsRequiredByDefault(false) } +func TestInvalidValidator(t *testing.T) { + type InvalidStruct struct { + Field int `valid:"someInvalidValidator"` + } + + invalidStruct := InvalidStruct{1} + if valid, err := ValidateStruct(&invalidStruct); valid || err == nil || + err.Error() != `Field: The following validator is invalid or can't be applied to the field: "someInvalidValidator";` { + t.Errorf("Got an unexpected result for struct with invalid validator: %t %s", valid, err) + } +} + +func TestCustomValidator(t *testing.T) { + type ValidStruct struct { + Field int `valid:"customTrueValidator"` + } + + type InvalidStruct struct { + Field int `valid:"customFalseValidator~Custom validator error"` + } + + type StructWithCustomAndBuiltinValidator struct { + Field int `valid:"customTrueValidator,required"` + } + + if valid, err := ValidateStruct(&ValidStruct{Field: 1}); !valid || err != nil { + t.Errorf("Got an unexpected result for struct with custom always true validator: %t %s", valid, err) + } + + if valid, err := ValidateStruct(&InvalidStruct{Field: 1}); valid || err == nil || err.Error() != "Custom validator error;;" { + t.Errorf("Got an unexpected result for struct with custom always false validator: %t %s", valid, err) + } + + mixedStruct := StructWithCustomAndBuiltinValidator{} + if valid, err := ValidateStruct(&mixedStruct); valid || err == nil || err.Error() != "Field: non zero value required;" { + t.Errorf("Got an unexpected result for invalid struct with custom and built-in validators: %t %s", valid, err) + } + + mixedStruct.Field = 1 + if valid, err := ValidateStruct(&mixedStruct); !valid || err != nil { + t.Errorf("Got an unexpected result for valid struct with custom and built-in validators: %t %s", valid, err) + } +} + type CustomByteArray [6]byte type StructWithCustomByteArray struct { @@ -2077,18 +2355,144 @@ func TestStringMatchesStruct(t *testing.T) { } } +func TestIsInStruct(t *testing.T) { + var tests = []struct { + param interface{} + expected bool + }{ + {IsInStruct{"PRESENT"}, true}, + {IsInStruct{""}, true}, + {IsInStruct{" "}, false}, + {IsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestRequiredIsInStruct(t *testing.T) { + type RequiredIsInStruct struct { + IsIn string `valid:"in(PRESENT|PRÉSENTE|NOTABSENT),required"` + } + + var tests = []struct { + param interface{} + expected bool + }{ + {RequiredIsInStruct{"PRESENT"}, true}, + {RequiredIsInStruct{""}, false}, + {RequiredIsInStruct{" "}, false}, + {RequiredIsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestEmptyRequiredIsInStruct(t *testing.T) { + type EmptyRequiredIsInStruct struct { + IsIn string `valid:"in(),required"` + } + + var tests = []struct { + param interface{} + expected bool + }{ + {EmptyRequiredIsInStruct{"PRESENT"}, false}, + {EmptyRequiredIsInStruct{""}, false}, + {EmptyRequiredIsInStruct{" "}, false}, + {EmptyRequiredIsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestFunkyIsInStruct(t *testing.T) { + type FunkyIsInStruct struct { + IsIn string `valid:"in(PRESENT|| |PRÉSENTE|NOTABSENT)"` + } + + var tests = []struct { + param interface{} + expected bool + }{ + {FunkyIsInStruct{"PRESENT"}, true}, + {FunkyIsInStruct{""}, true}, + {FunkyIsInStruct{" "}, true}, + {FunkyIsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +// TODO: test case broken +// func TestStringMatchesComplexStruct(t *testing.T) { +// var tests = []struct { +// param interface{} +// expected bool +// }{ +// {StringMatchesComplexStruct{"$()"}, false}, +// {StringMatchesComplexStruct{"$('AZERTY')"}, true}, +// {StringMatchesComplexStruct{`$("AZERTY")`}, true}, +// {StringMatchesComplexStruct{`$("")`}, false}, +// {StringMatchesComplexStruct{"AZERTY"}, false}, +// {StringMatchesComplexStruct{"$AZERTY"}, false}, +// } + +// for _, test := range tests { +// actual, err := ValidateStruct(test.param) +// if actual != test.expected { +// t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) +// if err != nil { +// t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) +// } +// } +// } +// } + func TestValidateStruct(t *testing.T) { var tests = []struct { param interface{} expected bool }{ - {User{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, false}, - {User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, - {User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, - {UserValid{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, true}, - {UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, - {UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, + {User{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{{"Street", "123456"}, {"Street", "123456"}}}, false}, + {User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {UserValid{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{{"Street", "123456"}, {"Street", "123456"}}}, true}, + {UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{}}, false}, + {UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, {nil, true}, {User{"John", "john@yahoo.com", "123G#678", 0, &Address{"Street", "123456"}, []Address{}}, false}, {"im not a struct", false}, @@ -2106,8 +2510,8 @@ func TestValidateStruct(t *testing.T) { TagMap["d_k"] = Validator(func(str string) bool { return str == "d_k" }) - result, err := ValidateStruct(PrivateStruct{"d_k", 0, []int{1, 2}, []string{"hi", "super"}, [2]Address{Address{"Street", "123456"}, - Address{"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"address": Address{"Street", "123456"}}}) + result, err := ValidateStruct(PrivateStruct{"d_k", 0, []int{1, 2}, []string{"hi", "super"}, [2]Address{{"Street", "123456"}, + {"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"address": {"Street", "123456"}}}) if result != true { t.Log("Case ", 6, ": expected ", true, " when result is ", result) t.Error(err) @@ -2286,10 +2690,10 @@ func TestErrorsByField(t *testing.T) { param string expected string }{ - {"CustomField", "An error occured"}, + {"CustomField", "An error occurred"}, } - err = Error{"CustomField", fmt.Errorf("An error occured"), false} + err = Error{"CustomField", fmt.Errorf("An error occurred"), false} errs = ErrorsByField(err) if len(errs) != 1 { @@ -2301,6 +2705,35 @@ func TestErrorsByField(t *testing.T) { t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual) } } + + type StructWithCustomValidation struct { + Email string `valid:"email"` + ID string `valid:"falseValidation"` + } + + CustomTypeTagMap.Set("falseValidation", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return false + })) + + tests = []struct { + param string + expected string + }{ + {"Email", "My123 does not validate as email"}, + {"ID", "duck13126 does not validate as falseValidation"}, + } + s := &StructWithCustomValidation{Email: "My123", ID: "duck13126"} + _, err = ValidateStruct(s) + errs = ErrorsByField(err) + if len(errs) != 2 { + t.Errorf("There should only be 2 errors but got %v", len(errs)) + } + + for _, test := range tests { + if actual, ok := errs[test.param]; !ok || actual != test.expected { + t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } } func TestValidateStructPointers(t *testing.T) { @@ -2356,3 +2789,76 @@ func ExampleValidateStruct() { } println(result) } + +func TestIsCIDR(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"193.168.3.20/7", true}, + {"2001:db8::/32", true}, + {"2001:0db8:85a3:0000:0000:8a2e:0370:7334/64", true}, + {"193.138.3.20/60", false}, + {"500.323.2.23/43", false}, + {"", false}, + } + for _, test := range tests { + actual := IsCIDR(test.param) + if actual != test.expected { + t.Errorf("Expected IsCIDR(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestOptionalCustomValidators(t *testing.T) { + + CustomTypeTagMap.Set("f2", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return false + })) + + var val struct { + WithCustomError string `valid:"f2~boom,optional"` + WithoutCustomError string `valid:"f2,optional"` + OptionalFirst string `valid:"optional,f2"` + } + + ok, err := ValidateStruct(val) + + if err != nil { + t.Errorf("Expected nil err with optional validation, got %v", err) + } + + if !ok { + t.Error("Expected validation to return true, got false") + } +} + +func TestJSONValidator(t *testing.T) { + + var val struct { + WithJSONName string `json:"with_json_name" valid:"-,required"` + WithoutJSONName string `valid:"-,required"` + WithJSONOmit string `json:"with_other_json_name,omitempty" valid:"-,required"` + WithJSONOption string `json:",omitempty" valid:"-,required"` + } + + _, err := ValidateStruct(val) + + if err == nil { + t.Error("Expected error but got no error") + } + + if Contains(err.Error(), "WithJSONName") { + t.Errorf("Expected error message to contain with_json_name but actual error is: %s", err.Error()) + } + + if Contains(err.Error(), "WithoutJSONName") == false { + t.Errorf("Expected error message to contain WithoutJSONName but actual error is: %s", err.Error()) + } + + if Contains(err.Error(), "omitempty") { + t.Errorf("Expected error message to not contain ',omitempty' but actual error is: %s", err.Error()) + } +} diff --git a/vendor/github.com/asaskevich/govalidator/wercker.yml b/vendor/github.com/asaskevich/govalidator/wercker.yml index 4840449099..cac7a5fcf0 100644 --- a/vendor/github.com/asaskevich/govalidator/wercker.yml +++ b/vendor/github.com/asaskevich/govalidator/wercker.yml @@ -1,4 +1,4 @@ -box: wercker/golang +box: golang build: steps: - setup-go-workspace diff --git a/vendor/github.com/go-openapi/analysis/.gitignore b/vendor/github.com/go-openapi/analysis/.gitignore index dd91ed6a04..c96f0b231a 100644 --- a/vendor/github.com/go-openapi/analysis/.gitignore +++ b/vendor/github.com/go-openapi/analysis/.gitignore @@ -1,2 +1,3 @@ secrets.yml coverage.out +.idea diff --git a/vendor/github.com/go-openapi/analysis/.travis.yml b/vendor/github.com/go-openapi/analysis/.travis.yml index 5a1353dab5..3aa42ab3a6 100644 --- a/vendor/github.com/go-openapi/analysis/.travis.yml +++ b/vendor/github.com/go-openapi/analysis/.travis.yml @@ -1,12 +1,13 @@ language: go go: -- 1.7.1 +- 1.7 install: - go get -u github.com/stretchr/testify/assert - go get -u gopkg.in/yaml.v2 - go get -u github.com/go-openapi/swag - go get -u github.com/go-openapi/jsonpointer - go get -u github.com/go-openapi/spec +- go get -u github.com/go-openapi/strfmt - go get -u github.com/go-openapi/loads/fmts script: - go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/analysis/README.md b/vendor/github.com/go-openapi/analysis/README.md index b6e526c006..d32c30d455 100644 --- a/vendor/github.com/go-openapi/analysis/README.md +++ b/vendor/github.com/go-openapi/analysis/README.md @@ -1,4 +1,4 @@ -# OpenAPI initiative analysis [![Build Status](https://ci.vmware.run/api/badges/go-openapi/analysis/status.svg)](https://ci.vmware.run/go-openapi/analysis) [![Coverage](https://coverage.vmware.run/badges/go-openapi/analysis/coverage.svg)](https://coverage.vmware.run/go-openapi/analysis) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +# OpenAPI initiative analysis [![Build Status](https://travis-ci.org/go-openapi/analysis.svg?branch=master)](https://travis-ci.org/go-openapi/analysis) [![codecov](https://codecov.io/gh/go-openapi/analysis/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/analysis) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/analysis/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/analysis?status.svg)](http://godoc.org/github.com/go-openapi/analysis) diff --git a/vendor/github.com/go-openapi/analysis/analyzer.go b/vendor/github.com/go-openapi/analysis/analyzer.go index ee84f9a000..77323a58e3 100644 --- a/vendor/github.com/go-openapi/analysis/analyzer.go +++ b/vendor/github.com/go-openapi/analysis/analyzer.go @@ -26,25 +26,28 @@ import ( ) type referenceAnalysis struct { - schemas map[string]spec.Ref - responses map[string]spec.Ref - parameters map[string]spec.Ref - items map[string]spec.Ref - allRefs map[string]spec.Ref - referenced struct { - schemas map[string]SchemaRef - responses map[string]*spec.Response - parameters map[string]*spec.Parameter - } + schemas map[string]spec.Ref + responses map[string]spec.Ref + parameters map[string]spec.Ref + items map[string]spec.Ref + headerItems map[string]spec.Ref + parameterItems map[string]spec.Ref + allRefs map[string]spec.Ref + pathItems map[string]spec.Ref } func (r *referenceAnalysis) addRef(key string, ref spec.Ref) { r.allRefs["#"+key] = ref } -func (r *referenceAnalysis) addItemsRef(key string, items *spec.Items) { +func (r *referenceAnalysis) addItemsRef(key string, items *spec.Items, location string) { r.items["#"+key] = items.Ref r.addRef(key, items.Ref) + if location == "header" { + r.headerItems["#"+key] = items.Ref + } else { + r.parameterItems["#"+key] = items.Ref + } } func (r *referenceAnalysis) addSchemaRef(key string, ref SchemaRef) { @@ -62,6 +65,43 @@ func (r *referenceAnalysis) addParamRef(key string, param *spec.Parameter) { r.addRef(key, param.Ref) } +func (r *referenceAnalysis) addPathItemRef(key string, pathItem *spec.PathItem) { + r.pathItems["#"+key] = pathItem.Ref + r.addRef(key, pathItem.Ref) +} + +type patternAnalysis struct { + parameters map[string]string + headers map[string]string + items map[string]string + schemas map[string]string + allPatterns map[string]string +} + +func (p *patternAnalysis) addPattern(key, pattern string) { + p.allPatterns["#"+key] = pattern +} + +func (p *patternAnalysis) addParameterPattern(key, pattern string) { + p.parameters["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addHeaderPattern(key, pattern string) { + p.headers["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addItemsPattern(key, pattern string) { + p.items["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addSchemaPattern(key, pattern string) { + p.schemas["#"+key] = pattern + p.addPattern(key, pattern) +} + // New takes a swagger spec object and returns an analyzed spec document. // The analyzed document contains a number of indices that make it easier to // reason about semantics of a swagger specification for use in code generation @@ -76,16 +116,23 @@ func New(doc *spec.Swagger) *Spec { allSchemas: make(map[string]SchemaRef, 150), allOfs: make(map[string]SchemaRef, 150), references: referenceAnalysis{ - schemas: make(map[string]spec.Ref, 150), - responses: make(map[string]spec.Ref, 150), - parameters: make(map[string]spec.Ref, 150), - items: make(map[string]spec.Ref, 150), - allRefs: make(map[string]spec.Ref, 150), + schemas: make(map[string]spec.Ref, 150), + pathItems: make(map[string]spec.Ref, 150), + responses: make(map[string]spec.Ref, 150), + parameters: make(map[string]spec.Ref, 150), + items: make(map[string]spec.Ref, 150), + headerItems: make(map[string]spec.Ref, 150), + parameterItems: make(map[string]spec.Ref, 150), + allRefs: make(map[string]spec.Ref, 150), + }, + patterns: patternAnalysis{ + parameters: make(map[string]string, 150), + headers: make(map[string]string, 150), + items: make(map[string]string, 150), + schemas: make(map[string]string, 150), + allPatterns: make(map[string]string, 150), }, } - a.references.referenced.schemas = make(map[string]SchemaRef, 150) - a.references.referenced.responses = make(map[string]*spec.Response, 150) - a.references.referenced.parameters = make(map[string]*spec.Parameter, 150) a.initialize() return a } @@ -99,10 +146,38 @@ type Spec struct { authSchemes map[string]struct{} operations map[string]map[string]*spec.Operation references referenceAnalysis + patterns patternAnalysis allSchemas map[string]SchemaRef allOfs map[string]SchemaRef } +func (s *Spec) reset() { + s.consumes = make(map[string]struct{}, 150) + s.produces = make(map[string]struct{}, 150) + s.authSchemes = make(map[string]struct{}, 150) + s.operations = make(map[string]map[string]*spec.Operation, 150) + s.allSchemas = make(map[string]SchemaRef, 150) + s.allOfs = make(map[string]SchemaRef, 150) + s.references.schemas = make(map[string]spec.Ref, 150) + s.references.pathItems = make(map[string]spec.Ref, 150) + s.references.responses = make(map[string]spec.Ref, 150) + s.references.parameters = make(map[string]spec.Ref, 150) + s.references.items = make(map[string]spec.Ref, 150) + s.references.headerItems = make(map[string]spec.Ref, 150) + s.references.parameterItems = make(map[string]spec.Ref, 150) + s.references.allRefs = make(map[string]spec.Ref, 150) + s.patterns.parameters = make(map[string]string, 150) + s.patterns.headers = make(map[string]string, 150) + s.patterns.items = make(map[string]string, 150) + s.patterns.schemas = make(map[string]string, 150) + s.patterns.allPatterns = make(map[string]string, 150) +} + +func (s *Spec) reload() { + s.reset() + s.initialize() +} + func (s *Spec) initialize() { for _, c := range s.spec.Consumes { s.consumes[c] = struct{}{} @@ -122,18 +197,25 @@ func (s *Spec) initialize() { for name, parameter := range s.spec.Parameters { refPref := slashpath.Join("/parameters", jsonpointer.Escape(name)) if parameter.Items != nil { - s.analyzeItems("items", parameter.Items, refPref) + s.analyzeItems("items", parameter.Items, refPref, "parameter") } if parameter.In == "body" && parameter.Schema != nil { s.analyzeSchema("schema", *parameter.Schema, refPref) } + if parameter.Pattern != "" { + s.patterns.addParameterPattern(refPref, parameter.Pattern) + } } for name, response := range s.spec.Responses { refPref := slashpath.Join("/responses", jsonpointer.Escape(name)) - for _, v := range response.Headers { + for k, v := range response.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) if v.Items != nil { - s.analyzeItems("items", v.Items, refPref) + s.analyzeItems("items", v.Items, hRefPref, "header") + } + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) } } if response.Schema != nil { @@ -152,6 +234,10 @@ func (s *Spec) initialize() { func (s *Spec) analyzeOperations(path string, pi *spec.PathItem) { // TODO: resolve refs here? op := pi + if pi.Ref.String() != "" { + key := slashpath.Join("/paths", jsonpointer.Escape(path)) + s.references.addPathItemRef(key, pi) + } s.analyzeOperation("GET", path, op.Get) s.analyzeOperation("PUT", path, op.Put) s.analyzeOperation("POST", path, op.Post) @@ -164,8 +250,11 @@ func (s *Spec) analyzeOperations(path string, pi *spec.PathItem) { if param.Ref.String() != "" { s.references.addParamRef(refPref, ¶m) } + if param.Pattern != "" { + s.patterns.addParameterPattern(refPref, param.Pattern) + } if param.Items != nil { - s.analyzeItems("items", param.Items, refPref) + s.analyzeItems("items", param.Items, refPref, "parameter") } if param.Schema != nil { s.analyzeSchema("schema", *param.Schema, refPref) @@ -173,14 +262,17 @@ func (s *Spec) analyzeOperations(path string, pi *spec.PathItem) { } } -func (s *Spec) analyzeItems(name string, items *spec.Items, prefix string) { +func (s *Spec) analyzeItems(name string, items *spec.Items, prefix, location string) { if items == nil { return } refPref := slashpath.Join(prefix, name) - s.analyzeItems(name, items.Items, refPref) + s.analyzeItems(name, items.Items, refPref, location) if items.Ref.String() != "" { - s.references.addItemsRef(refPref, items) + s.references.addItemsRef(refPref, items, location) + } + if items.Pattern != "" { + s.patterns.addItemsPattern(refPref, items.Pattern) } } @@ -210,7 +302,10 @@ func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) { if param.Ref.String() != "" { s.references.addParamRef(refPref, ¶m) } - s.analyzeItems("items", param.Items, refPref) + if param.Pattern != "" { + s.patterns.addParameterPattern(refPref, param.Pattern) + } + s.analyzeItems("items", param.Items, refPref, "parameter") if param.In == "body" && param.Schema != nil { s.analyzeSchema("schema", *param.Schema, refPref) } @@ -221,8 +316,12 @@ func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) { if op.Responses.Default.Ref.String() != "" { s.references.addResponseRef(refPref, op.Responses.Default) } - for _, v := range op.Responses.Default.Headers { - s.analyzeItems("items", v.Items, refPref) + for k, v := range op.Responses.Default.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + s.analyzeItems("items", v.Items, hRefPref, "header") + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } } if op.Responses.Default.Schema != nil { s.analyzeSchema("schema", *op.Responses.Default.Schema, refPref) @@ -233,8 +332,12 @@ func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) { if res.Ref.String() != "" { s.references.addResponseRef(refPref, &res) } - for _, v := range res.Headers { - s.analyzeItems("items", v.Items, refPref) + for k, v := range res.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + s.analyzeItems("items", v.Items, hRefPref, "header") + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } } if res.Schema != nil { s.analyzeSchema("schema", *res.Schema, refPref) @@ -246,14 +349,21 @@ func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) { func (s *Spec) analyzeSchema(name string, schema spec.Schema, prefix string) { refURI := slashpath.Join(prefix, jsonpointer.Escape(name)) schRef := SchemaRef{ - Name: name, - Schema: &schema, - Ref: spec.MustCreateRef("#" + refURI), + Name: name, + Schema: &schema, + Ref: spec.MustCreateRef("#" + refURI), + TopLevel: prefix == "/definitions", } + s.allSchemas["#"+refURI] = schRef + if schema.Ref.String() != "" { s.references.addSchemaRef(refURI, schRef) } + if schema.Pattern != "" { + s.patterns.addSchemaPattern(refURI, schema.Pattern) + } + for k, v := range schema.Definitions { s.analyzeSchema(k, v, slashpath.Join(refURI, "definitions")) } @@ -267,7 +377,7 @@ func (s *Spec) analyzeSchema(name string, schema spec.Schema, prefix string) { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "allOf")) } if len(schema.AllOf) > 0 { - s.allOfs["#"+refURI] = SchemaRef{Name: name, Schema: &schema, Ref: spec.MustCreateRef("#" + refURI)} + s.allOfs["#"+refURI] = schRef } for i, v := range schema.AnyOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "anyOf")) @@ -549,9 +659,10 @@ func (s *Spec) RequiredSecuritySchemes() []string { // SchemaRef is a reference to a schema type SchemaRef struct { - Name string - Ref spec.Ref - Schema *spec.Schema + Name string + Ref spec.Ref + Schema *spec.Schema + TopLevel bool } // SchemasWithAllOf returns schema references to all schemas that are defined @@ -595,6 +706,14 @@ func (s *Spec) AllResponseReferences() (result []string) { return } +// AllPathItemReferences returns the references for all the items +func (s *Spec) AllPathItemReferences() (result []string) { + for _, v := range s.references.pathItems { + result = append(result, v.String()) + } + return +} + // AllItemsReferences returns the references for all the items func (s *Spec) AllItemsReferences() (result []string) { for _, v := range s.references.items { @@ -626,3 +745,41 @@ func (s *Spec) AllRefs() (result []spec.Ref) { } return } + +func cloneStringMap(source map[string]string) map[string]string { + res := make(map[string]string, len(source)) + for k, v := range source { + res[k] = v + } + return res +} + +// ParameterPatterns returns all the patterns found in parameters +// the map is cloned to avoid accidental changes +func (s *Spec) ParameterPatterns() map[string]string { + return cloneStringMap(s.patterns.parameters) +} + +// HeaderPatterns returns all the patterns found in response headers +// the map is cloned to avoid accidental changes +func (s *Spec) HeaderPatterns() map[string]string { + return cloneStringMap(s.patterns.headers) +} + +// ItemsPatterns returns all the patterns found in simple array items +// the map is cloned to avoid accidental changes +func (s *Spec) ItemsPatterns() map[string]string { + return cloneStringMap(s.patterns.items) +} + +// SchemaPatterns returns all the patterns found in schemas +// the map is cloned to avoid accidental changes +func (s *Spec) SchemaPatterns() map[string]string { + return cloneStringMap(s.patterns.schemas) +} + +// AllPatterns returns all the patterns found in the spec +// the map is cloned to avoid accidental changes +func (s *Spec) AllPatterns() map[string]string { + return cloneStringMap(s.patterns.allPatterns) +} diff --git a/vendor/github.com/go-openapi/analysis/analyzer_test.go b/vendor/github.com/go-openapi/analysis/analyzer_test.go index d13a595550..70b80d7f9d 100644 --- a/vendor/github.com/go-openapi/analysis/analyzer_test.go +++ b/vendor/github.com/go-openapi/analysis/analyzer_test.go @@ -23,6 +23,7 @@ import ( "github.com/go-openapi/loads/fmts" "github.com/go-openapi/spec" + "github.com/go-openapi/swag" "github.com/stretchr/testify/assert" ) @@ -182,12 +183,22 @@ func TestDefinitionAnalysis(t *testing.T) { assertSchemaRefExists(t, definitions, "#/definitions/withAllOf/allOf/1") allOfs := analyzer.allOfs assert.Len(t, allOfs, 1) - _, hasAllOf := allOfs["#/definitions/withAllOf"] - assert.True(t, hasAllOf) + assert.Contains(t, allOfs, "#/definitions/withAllOf") } } func loadSpec(path string) (*spec.Swagger, error) { + spec.PathLoader = func(path string) (json.RawMessage, error) { + ext := filepath.Ext(path) + if ext == ".yml" || ext == ".yaml" { + return fmts.YAMLDoc(path) + } + data, err := swag.LoadFromFileOrHTTP(path) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil + } data, err := fmts.YAMLDoc(path) if err != nil { return nil, err @@ -209,6 +220,9 @@ func TestReferenceAnalysis(t *testing.T) { assertRefExists(t, definitions.parameters, "#/paths/~1some~1where~1{id}/parameters/0") assertRefExists(t, definitions.parameters, "#/paths/~1some~1where~1{id}/get/parameters/0") + // path items + assertRefExists(t, definitions.pathItems, "#/paths/~1other~1place") + // responses assertRefExists(t, definitions.responses, "#/paths/~1some~1where~1{id}/get/responses/404") @@ -235,3 +249,36 @@ func assertSchemaRefExists(t testing.TB, data map[string]SchemaRef, key string) } return true } + +func TestPatternAnalysis(t *testing.T) { + doc, err := loadSpec(filepath.Join("fixtures", "patterns.yml")) + if assert.NoError(t, err) { + pt := New(doc).patterns + + // parameters + assertPattern(t, pt.parameters, "#/parameters/idParam", "a[A-Za-Z0-9]+") + assertPattern(t, pt.parameters, "#/paths/~1some~1where~1{id}/parameters/1", "b[A-Za-z0-9]+") + assertPattern(t, pt.parameters, "#/paths/~1some~1where~1{id}/get/parameters/0", "[abc][0-9]+") + + // responses + assertPattern(t, pt.headers, "#/responses/notFound/headers/ContentLength", "[0-9]+") + assertPattern(t, pt.headers, "#/paths/~1some~1where~1{id}/get/responses/200/headers/X-Request-Id", "d[A-Za-z0-9]+") + + // definitions + assertPattern(t, pt.schemas, "#/paths/~1other~1place/post/parameters/0/schema/properties/value", "e[A-Za-z0-9]+") + assertPattern(t, pt.schemas, "#/paths/~1other~1place/post/responses/200/schema/properties/data", "[0-9]+[abd]") + assertPattern(t, pt.schemas, "#/definitions/named", "f[A-Za-z0-9]+") + assertPattern(t, pt.schemas, "#/definitions/tag/properties/value", "g[A-Za-z0-9]+") + + // items + assertPattern(t, pt.items, "#/paths/~1some~1where~1{id}/get/parameters/1/items", "c[A-Za-z0-9]+") + assertPattern(t, pt.items, "#/paths/~1other~1place/post/responses/default/headers/Via/items", "[A-Za-z]+") + } +} + +func assertPattern(t testing.TB, data map[string]string, key, pattern string) bool { + if assert.Contains(t, data, key) { + return assert.Equal(t, pattern, data[key]) + } + return false +} diff --git a/vendor/github.com/go-openapi/analysis/fixtures/bar-crud.yml b/vendor/github.com/go-openapi/analysis/fixtures/bar-crud.yml new file mode 100644 index 0000000000..90000f46bc --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/bar-crud.yml @@ -0,0 +1,180 @@ +--- +swagger: '2.0' +info: + title: bar CRUD API + version: 4.2.0 +schemes: + - http +basePath: /api +consumes: + - application/json +produces: + - application/json +paths: + /common: + get: + operationId: commonGet + summary: here to test path collisons + responses: + '200': + description: OK + schema: + $ref: "#/definitions/bar" + /bars: + post: + operationId: create + summary: Create a new bar + parameters: + - name: info + in: body + schema: + $ref: "#/definitions/bar" + responses: + '201': + description: created + schema: + $ref: "#/definitions/barId" + default: + description: error + schema: + $ref: "#/definitions/error" + /bars/{barid}: + get: + operationId: get + summary: Get a bar by id + parameters: + - $ref: "#/parameters/barid" + responses: + '200': + description: OK + schema: + $ref: "#/definitions/bar" + '401': + $ref: "#/responses/401" + '404': + $ref: "#/responses/404" + default: + description: error + schema: + $ref: "#/definitions/error" + delete: + operationId: delete + summary: delete a bar by id + parameters: + - name: barid + in: path + required: true + type: string + responses: + '200': + description: OK + '401': + description: unauthorized + schema: + $ref: "#/definitions/error" + '404': + description: resource not found + schema: + $ref: "#/definitions/error" + default: + description: error + schema: + $ref: "#/definitions/error" + post: + operationId: update + summary: update a bar by id + parameters: + - name: barid + in: path + required: true + type: string + - name: info + in: body + schema: + $ref: "#/definitions/bar" + responses: + '200': + description: OK + '401': + description: unauthorized + schema: + $ref: "#/definitions/error" + '404': + description: resource not found + schema: + $ref: "#/definitions/error" + default: + description: error + schema: + $ref: "#/definitions/error" + +definitions: + common: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 + bar: + type: object + required: + - name + - description + properties: + id: + type: string + format: string + readOnly: true + name: + type: string + format: string + minLength: 1 + description: + type: string + format: string + minLength: 1 + barId: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 + error: + type: object + required: + - message + properties: + code: + type: string + format: string + message: + type: string + fields: + type: string + +parameters: + common: + name: common + in: query + type: string + barid: + name: barid + in: path + required: true + type: string + +responses: + 401: + description: bar unauthorized + schema: + $ref: "#/definitions/error" + 404: + description: bar resource not found + schema: + $ref: "#/definitions/error" diff --git a/vendor/github.com/go-openapi/analysis/fixtures/empty-paths.json b/vendor/github.com/go-openapi/analysis/fixtures/empty-paths.json new file mode 100644 index 0000000000..30e6009232 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/empty-paths.json @@ -0,0 +1,8 @@ +{ + "swagger": "2.0", + "info": { + "title": "empty-paths", + "version": "79.2.1" + }, + "paths": {} +} diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/definitions.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/definitions.yml new file mode 100644 index 0000000000..009cba27cb --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/definitions.yml @@ -0,0 +1,50 @@ +definitions: + named: + type: string + tag: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + audit: + $ref: "#/definitions/record" + record: + type: object + properties: + createdAt: + type: string + format: date-time + + nestedThing: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + additionalProperties: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + properties: + value: + type: string + name: + $ref: "definitions2.yml#/coordinate" \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/definitions2.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/definitions2.yml new file mode 100644 index 0000000000..08f70c0ea7 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/definitions2.yml @@ -0,0 +1,9 @@ +coordinate: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/errors.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/errors.yml new file mode 100644 index 0000000000..d37a1bdd40 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/errors.yml @@ -0,0 +1,13 @@ +error: + type: object + required: + - id + - message + properties: + id: + type: integer + format: int64 + readOnly: true + message: + type: string + readOnly: true diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/nestedParams.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/nestedParams.yml new file mode 100644 index 0000000000..c11c0d8f0c --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/nestedParams.yml @@ -0,0 +1,35 @@ +bodyParam: + name: body + in: body + schema: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + name: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/nestedResponses.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/nestedResponses.yml new file mode 100644 index 0000000000..2d6583c051 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/nestedResponses.yml @@ -0,0 +1,32 @@ +genericResponse: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + name: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/parameters.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/parameters.yml new file mode 100644 index 0000000000..b31a4485ad --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/parameters.yml @@ -0,0 +1,12 @@ +parameters: + idParam: + name: id + in: path + type: integer + format: int32 + limitParam: + name: limit + in: query + type: integer + format: int32 + required: false \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/pathItem.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/pathItem.yml new file mode 100644 index 0000000000..22a7aa6342 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/pathItem.yml @@ -0,0 +1,9 @@ +get: + operationId: modelOp + summary: many model variations + description: Used to see if a codegen can render all the possible parameter variations for a header param + tags: + - testcgen + responses: + default: + description: Generic Out \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external/responses.yml b/vendor/github.com/go-openapi/analysis/fixtures/external/responses.yml new file mode 100644 index 0000000000..8730d64c18 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external/responses.yml @@ -0,0 +1,4 @@ +responses: + notFound: + schema: + $ref: "errors.yml#/error" \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/external_definitions.yml b/vendor/github.com/go-openapi/analysis/fixtures/external_definitions.yml new file mode 100644 index 0000000000..032f2f3204 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/external_definitions.yml @@ -0,0 +1,95 @@ +--- +swagger: "2.0" +info: + version: "0.1.0" + title: reference analysis + +parameters: + someParam: + name: someParam + in: body + schema: + $ref: "external/definitions.yml#/definitions/record" +responses: + someResponse: + schema: + $ref: "external/definitions.yml#/definitions/record" +paths: + "/some/where/{id}": + parameters: + - $ref: "external/parameters.yml#/parameters/idParam" + + - name: bodyId + in: body + schema: + $ref: "external/definitions.yml#/definitions/record" + get: + parameters: + - $ref: "external/parameters.yml#/parameters/limitParam" + - name: other + in: query + type: array + items: + $ref: "external/definitions.yml#/definitions/named" + - name: body + in: body + schema: + $ref: "external/definitions.yml#/definitions/record" + responses: + default: + schema: + $ref: "external/definitions.yml#/definitions/record" + 404: + $ref: "external/responses.yml#/responses/notFound" + 200: + schema: + $ref: "external/definitions.yml#/definitions/tag" + "/other/place": + $ref: "external/pathItem.yml" + +definitions: + namedAgain: + $ref: "external/definitions.yml#/definitions/named" + + datedTag: + allOf: + - type: string + format: date + - $ref: "external/definitions.yml#/definitions/tag" + + records: + type: array + items: + - $ref: "external/definitions.yml#/definitions/record" + + datedRecords: + type: array + items: + - type: string + format: date-time + - $ref: "external/definitions.yml#/definitions/record" + + datedTaggedRecords: + type: array + items: + - type: string + format: date-time + - $ref: "external/definitions.yml#/definitions/record" + additionalItems: + $ref: "external/definitions.yml#/definitions/tag" + + otherRecords: + type: array + items: + $ref: "external/definitions.yml#/definitions/record" + + tags: + type: object + additionalProperties: + $ref: "external/definitions.yml#/definitions/tag" + + namedThing: + type: object + properties: + name: + $ref: "external/definitions.yml#/definitions/named" \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/flatten.yml b/vendor/github.com/go-openapi/analysis/fixtures/flatten.yml new file mode 100644 index 0000000000..d3bd8d9a53 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/flatten.yml @@ -0,0 +1,85 @@ +--- +swagger: "2.0" +info: + version: "0.1.0" + title: reference analysis + +parameters: + someParam: + name: some + in: query + type: string +responses: + notFound: + description: "Not Found" + schema: + $ref: "external/errors.yml#/error" + +paths: + "/some/where/{id}": + parameters: + - $ref: "external/parameters.yml#/parameters/idParam" + + get: + parameters: + - $ref: "external/parameters.yml#/parameters/limitParam" + - $ref: "#/parameters/someParam" + - name: other + in: query + type: string + - $ref: "external/nestedParams.yml#/bodyParam" + + responses: + default: + $ref: "external/nestedResponses.yml#/genericResponse" + 404: + $ref: "#/responses/notFound" + 200: + description: "RecordHolder" + schema: + type: object + properties: + record: + $ref: "external/definitions.yml#/definitions/nestedThing" + "/other/place": + $ref: "external/pathItem.yml" + +definitions: + namedAgain: + $ref: "external/definitions.yml#/definitions/named" + + datedTag: + allOf: + - type: string + format: date + - $ref: "external/definitions.yml#/definitions/tag" + + records: + type: array + items: + - $ref: "external/definitions.yml#/definitions/record" + + datedRecords: + type: array + items: + - type: string + format: date-time + - $ref: "external/definitions.yml#/definitions/record" + + otherRecords: + type: array + items: + $ref: "external/definitions.yml#/definitions/record" + + tags: + type: object + additionalProperties: + $ref: "external/definitions.yml#/definitions/tag" + + namedThing: + type: object + properties: + name: + $ref: "external/definitions.yml#/definitions/named" + namedAgain: + $ref: "#/definitions/namedAgain" diff --git a/vendor/github.com/go-openapi/analysis/fixtures/foo-crud.yml b/vendor/github.com/go-openapi/analysis/fixtures/foo-crud.yml new file mode 100644 index 0000000000..2e3d1f60f2 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/foo-crud.yml @@ -0,0 +1,180 @@ +--- +swagger: '2.0' +info: + title: foo CRUD API + version: 4.2.0 +schemes: + - http +basePath: /api +consumes: + - application/json +produces: + - application/json +paths: + /common: + get: + operationId: commonGet + summary: here to test path collisons + responses: + '200': + description: OK + schema: + $ref: "#/definitions/foo" + /foos: + post: + operationId: create + summary: Create a new foo + parameters: + - name: info + in: body + schema: + $ref: "#/definitions/foo" + responses: + '201': + description: created + schema: + $ref: "#/definitions/fooId" + default: + description: error + schema: + $ref: "#/definitions/error" + /foos/{fooid}: + get: + operationId: get + summary: Get a foo by id + parameters: + - $ref: "#/parameters/fooid" + responses: + '200': + description: OK + schema: + $ref: "#/definitions/foo" + '401': + $ref: "#/responses/401" + '404': + $ref: "#/responses/404" + default: + description: error + schema: + $ref: "#/definitions/error" + delete: + operationId: delete + summary: delete a foo by id + parameters: + - name: fooid + in: path + required: true + type: string + responses: + '200': + description: OK + '401': + description: unauthorized + schema: + $ref: "#/definitions/error" + '404': + description: resource not found + schema: + $ref: "#/definitions/error" + default: + description: error + schema: + $ref: "#/definitions/error" + post: + operationId: update + summary: update a foo by id + parameters: + - name: fooid + in: path + required: true + type: string + - name: info + in: body + schema: + $ref: "#/definitions/foo" + responses: + '200': + description: OK + '401': + description: unauthorized + schema: + $ref: "#/definitions/error" + '404': + description: resource not found + schema: + $ref: "#/definitions/error" + default: + description: error + schema: + $ref: "#/definitions/error" + +definitions: + common: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 + foo: + type: object + required: + - name + - description + properties: + id: + type: string + format: string + readOnly: true + name: + type: string + format: string + minLength: 1 + description: + type: string + format: string + minLength: 1 + fooId: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 + error: + type: object + required: + - message + properties: + code: + type: string + format: string + message: + type: string + fields: + type: string + +parameters: + common: + name: common + in: query + type: string + fooid: + name: fooid + in: path + required: true + type: string + +responses: + 401: + description: foo unauthorized + schema: + $ref: "#/definitions/error" + 404: + description: foo resource not found + schema: + $ref: "#/definitions/error" diff --git a/vendor/github.com/go-openapi/analysis/fixtures/inline_schemas.yml b/vendor/github.com/go-openapi/analysis/fixtures/inline_schemas.yml new file mode 100644 index 0000000000..59699571e8 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/inline_schemas.yml @@ -0,0 +1,187 @@ +--- +swagger: "2.0" +info: + version: "0.1.0" + title: reference analysis + +parameters: + someParam: + name: someParam + in: body + schema: + type: object + properties: + createdAt: + type: string + format: date-time +responses: + someResponse: + schema: + type: object + properties: + createdAt: + type: string + format: date-time +paths: + "/some/where/{id}": + parameters: + - name: id + in: path + type: integer + format: int32 + + - name: bodyId + in: body + schema: + type: object + properties: + createdAt: + type: string + format: date-time + post: + responses: + default: + description: all good + get: + parameters: + - name: limit + in: query + type: integer + format: int32 + required: false + - name: other + in: query + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + - name: body + in: body + schema: + type: object + properties: + createdAt: + type: string + format: date-time + responses: + default: + schema: + type: object + properties: + createdAt: + type: string + format: date-time + 404: + schema: + $ref: "errors.yml#/error" + 200: + schema: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + "/other/place": + $ref: "external/pathItem.yml" + +definitions: + namedAgain: + type: object + properties: + id: + type: integer + format: int64 + + datedTag: + allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + + records: + type: array + items: + - type: object + properties: + createdAt: + type: string + format: date-time + + datedRecords: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + + datedTaggedRecords: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + additionalItems: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + + otherRecords: + type: array + items: + type: object + properties: + createdAt: + type: string + format: date-time + + tags: + type: object + additionalProperties: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + + namedThing: + type: object + properties: + name: + type: object + properties: + id: + type: integer + format: int64 + + # depth first should have this at the bottom, it's just a very long name + pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism: + type: object + properties: + floccinaucinihilipilificationCreatedAt: + type: integer + format: int64 diff --git a/vendor/github.com/go-openapi/analysis/fixtures/nested_inline_schemas.yml b/vendor/github.com/go-openapi/analysis/fixtures/nested_inline_schemas.yml new file mode 100644 index 0000000000..6acf3b3400 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/nested_inline_schemas.yml @@ -0,0 +1,298 @@ +--- +swagger: "2.0" +info: + version: "0.1.0" + title: reference analysis + +parameters: + someParam: + name: someParam + in: body + schema: + type: object + properties: + createdAt: + type: string + format: date-time +responses: + someResponse: + schema: + type: object + properties: + createdAt: + type: string + format: date-time +paths: + "/some/where/{id}": + parameters: + - name: id + in: path + type: integer + format: int32 + + - name: bodyId + in: body + schema: + type: array + items: + type: object + properties: + createdAt: + type: string + format: date-time + post: + responses: + default: + description: all good + get: + parameters: + - name: limit + in: query + type: integer + format: int32 + required: false + - name: other + in: query + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + - name: body + in: body + schema: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + name: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time + responses: + default: + schema: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + name: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time + 404: + schema: + $ref: "errors.yml#/error" + 200: + schema: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + name: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time + "/other/place": + $ref: "external/pathItem.yml" + +definitions: + namedAgain: + type: object + properties: + id: + type: integer + format: int64 + + datedTag: + allOf: + - type: string + format: date + - type: object + properties: + id: + type: integer + format: int64 + value: + type: string + + records: + type: array + items: + - type: object + properties: + createdAt: + type: string + format: date-time + + datedRecords: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + + datedTaggedRecords: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + additionalItems: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + + otherRecords: + type: array + items: + type: object + properties: + createdAt: + type: string + format: date-time + + tags: + type: object + additionalProperties: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + + namedThing: + type: object + properties: + name: + type: object + properties: + id: + type: integer + format: int64 + + nestedThing: + type: object + properties: + record: + type: array + items: + - type: string + format: date-time + - type: object + properties: + createdAt: + type: string + format: date-time + - allOf: + - type: string + format: date + - type: object + additionalProperties: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + properties: + id: + type: integer + format: int64 + value: + type: string + name: + type: object + properties: + id: + type: integer + format: int64 + createdAt: + type: string + format: date-time \ No newline at end of file diff --git a/vendor/github.com/go-openapi/analysis/fixtures/no-paths.yml b/vendor/github.com/go-openapi/analysis/fixtures/no-paths.yml new file mode 100644 index 0000000000..783f59c420 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/no-paths.yml @@ -0,0 +1,38 @@ +--- +swagger: '2.0' +info: + title: no paths API + version: 4.1.7 +schemes: + - http +basePath: /wooble +consumes: + - application/json +produces: + - application/json +paths: +definitions: + common: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 +parameters: + common: + name: common + in: query + type: string + +responses: + 401: + description: bar unauthorized + schema: + $ref: "#/definitions/error" + 404: + description: bar resource not found + schema: + $ref: "#/definitions/error" diff --git a/vendor/github.com/go-openapi/analysis/fixtures/patterns.yml b/vendor/github.com/go-openapi/analysis/fixtures/patterns.yml new file mode 100644 index 0000000000..3fe1ab5162 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/patterns.yml @@ -0,0 +1,124 @@ +--- +swagger: "2.0" +info: + version: "0.1.0" + title: reference analysis + +parameters: + idParam: + name: id + in: path + type: string + pattern: 'a[A-Za-Z0-9]+' + +responses: + notFound: + headers: + ContentLength: + type: string + pattern: '[0-9]+' + schema: + $ref: "#/definitions/error" + +paths: + "/some/where/{id}": + parameters: + - $ref: "#/parameters/idParam" + - name: name + in: query + pattern: 'b[A-Za-z0-9]+' + - name: bodyId + in: body + schema: + type: object + get: + parameters: + - name: filter + in: query + type: string + pattern: "[abc][0-9]+" + - name: other + in: query + type: array + items: + type: string + pattern: 'c[A-Za-z0-9]+' + - name: body + in: body + schema: + type: object + + responses: + default: + schema: + type: object + 404: + $ref: "#/responses/notFound" + 200: + headers: + X-Request-Id: + type: string + pattern: 'd[A-Za-z0-9]+' + schema: + $ref: "#/definitions/tag" + "/other/place": + post: + parameters: + - name: body + in: body + schema: + type: object + properties: + value: + type: string + pattern: 'e[A-Za-z0-9]+' + responses: + default: + headers: + Via: + type: array + items: + type: string + pattern: '[A-Za-z]+' + 200: + schema: + type: object + properties: + data: + type: string + pattern: "[0-9]+[abd]" + +definitions: + named: + type: string + pattern: 'f[A-Za-z0-9]+' + tag: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + pattern: 'g[A-Za-z0-9]+' + audit: + $ref: "#/definitions/record" + record: + type: object + properties: + createdAt: + type: string + format: date-time + error: + type: object + required: + - id + - message + properties: + id: + type: integer + format: int64 + readOnly: true + message: + type: string + readOnly: true diff --git a/vendor/github.com/go-openapi/analysis/fixtures/references.yml b/vendor/github.com/go-openapi/analysis/fixtures/references.yml index 64c3b83273..b571766352 100644 --- a/vendor/github.com/go-openapi/analysis/fixtures/references.yml +++ b/vendor/github.com/go-openapi/analysis/fixtures/references.yml @@ -52,6 +52,9 @@ paths: 200: schema: $ref: "#/definitions/tag" + "/other/place": + $ref: "#/x-shared-path/getItems" + definitions: named: type: string diff --git a/vendor/github.com/go-openapi/analysis/fixtures/widget-crud.yml b/vendor/github.com/go-openapi/analysis/fixtures/widget-crud.yml new file mode 100644 index 0000000000..d1acb92775 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixtures/widget-crud.yml @@ -0,0 +1,181 @@ +--- +swagger: '2.0' +info: + title: widget CRUD API + version: 4.2.0 +schemes: + - http +basePath: /api +consumes: + - application/json +produces: + - application/json +paths: + /common: + get: + operationId: commonGet + summary: here to test path collisons + responses: + '200': + description: OK + schema: + $ref: "#/definitions/widget" + + /widgets: + post: + operationId: create + summary: Create a new widget + parameters: + - name: info + in: body + schema: + $ref: "#/definitions/widget" + responses: + '201': + description: created + schema: + $ref: "#/definitions/widgetId" + default: + description: error + schema: + $ref: "#/definitions/error" + /widgets/{widgetid}: + get: + operationId: get + summary: Get a widget by id + parameters: + - $ref: "#/parameters/widgetid" + responses: + '200': + description: OK + schema: + $ref: "#/definitions/widget" + '401': + $ref: "#/responses/401" + '404': + $ref: "#/responses/404" + default: + description: error + schema: + $ref: "#/definitions/error" + delete: + operationId: delete + summary: delete a widget by id + parameters: + - name: widgetid + in: path + required: true + type: string + responses: + '200': + description: OK + '401': + description: unauthorized + schema: + $ref: "#/definitions/error" + '404': + description: resource not found + schema: + $ref: "#/definitions/error" + default: + description: error + schema: + $ref: "#/definitions/error" + post: + operationId: update + summary: update a widget by id + parameters: + - name: widgetid + in: path + required: true + type: string + - name: info + in: body + schema: + $ref: "#/definitions/widget" + responses: + '200': + description: OK + '401': + description: unauthorized + schema: + $ref: "#/definitions/error" + '404': + description: resource not found + schema: + $ref: "#/definitions/error" + default: + description: error + schema: + $ref: "#/definitions/error" + +definitions: + common: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 + widget: + type: object + required: + - name + - description + properties: + id: + type: string + format: string + readOnly: true + name: + type: string + format: string + minLength: 1 + description: + type: string + format: string + minLength: 1 + widgetId: + type: object + required: + - id + properties: + id: + type: string + format: string + minLength: 1 + error: + type: object + required: + - message + properties: + code: + type: string + format: string + message: + type: string + fields: + type: string + +parameters: + common: + name: common + in: query + type: string + widgetid: + name: widgetid + in: path + required: true + type: string + +responses: + 401: + description: widget unauthorized + schema: + $ref: "#/definitions/error" + 404: + description: widget resource not found + schema: + $ref: "#/definitions/error" diff --git a/vendor/github.com/go-openapi/analysis/flatten.go b/vendor/github.com/go-openapi/analysis/flatten.go new file mode 100644 index 0000000000..23d7242f45 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/flatten.go @@ -0,0 +1,756 @@ +package analysis + +import ( + "fmt" + "log" + "net/http" + "path" + "sort" + "strings" + + "strconv" + + "github.com/go-openapi/jsonpointer" + swspec "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +// FlattenOpts configuration for flattening a swagger specification. +type FlattenOpts struct { + Spec *Spec + BasePath string + + _ struct{} // require keys +} + +// ExpandOpts creates a spec.ExpandOptions to configure expanding a specification document. +func (f *FlattenOpts) ExpandOpts(skipSchemas bool) *swspec.ExpandOptions { + return &swspec.ExpandOptions{RelativeBase: f.BasePath, SkipSchemas: skipSchemas} +} + +// Swagger gets the swagger specification for this flatten operation +func (f *FlattenOpts) Swagger() *swspec.Swagger { + return f.Spec.spec +} + +// Flatten an analyzed spec. +// +// To flatten a spec means: +// +// Expand the parameters, responses, path items, parameter items and header items. +// Import external (http, file) references so they become internal to the document. +// Move every inline schema to be a definition with an auto-generated name in a depth-first fashion. +// Rewritten schemas get a vendor extension x-go-gen-location so we know in which package they need to be rendered. +func Flatten(opts FlattenOpts) error { + // recursively expand responses, parameters, path items and items + err := swspec.ExpandSpec(opts.Swagger(), opts.ExpandOpts(true)) + if err != nil { + return err + } + opts.Spec.reload() // re-analyze + + // at this point there are no other references left but schemas + if err := importExternalReferences(&opts); err != nil { + return err + } + opts.Spec.reload() // re-analyze + + // rewrite the inline schemas (schemas that aren't simple types or arrays of simple types) + if err := nameInlinedSchemas(&opts); err != nil { + return err + } + opts.Spec.reload() // re-analyze + + // TODO: simplifiy known schema patterns to flat objects with properties? + return nil +} + +func nameInlinedSchemas(opts *FlattenOpts) error { + namer := &inlineSchemaNamer{Spec: opts.Swagger(), Operations: opRefsByRef(gatherOperations(opts.Spec, nil))} + depthFirst := sortDepthFirst(opts.Spec.allSchemas) + + for _, key := range depthFirst { + sch := opts.Spec.allSchemas[key] + if sch.Schema != nil && sch.Schema.Ref.String() == "" && !sch.TopLevel { // inline schema + asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if err != nil { + return fmt.Errorf("schema analysis [%s]: %v", sch.Ref.String(), err) + } + + if !asch.IsSimpleSchema { // complex schemas get moved + if err := namer.Name(key, sch.Schema, asch); err != nil { + return err + } + } + } + } + return nil +} + +var depthGroupOrder = []string{"sharedOpParam", "opParam", "codeResponse", "defaultResponse", "definition"} + +func sortDepthFirst(data map[string]SchemaRef) (sorted []string) { + // group by category (shared params, op param, statuscode response, default response, definitions) + // sort groups internally by number of parts in the key and lexical names + // flatten groups into a single list of keys + grouped := make(map[string]keys, len(data)) + for k := range data { + split := keyParts(k) + var pk string + if split.IsSharedOperationParam() { + pk = "sharedOpParam" + } + if split.IsOperationParam() { + pk = "opParam" + } + if split.IsStatusCodeResponse() { + pk = "codeResponse" + } + if split.IsDefaultResponse() { + pk = "defaultResponse" + } + if split.IsDefinition() { + pk = "definition" + } + grouped[pk] = append(grouped[pk], key{len(split), k}) + } + + for _, pk := range depthGroupOrder { + res := grouped[pk] + sort.Sort(res) + for _, v := range res { + sorted = append(sorted, v.Key) + } + } + + return +} + +type key struct { + Segments int + Key string +} +type keys []key + +func (k keys) Len() int { return len(k) } +func (k keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k keys) Less(i, j int) bool { + return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key) +} + +type inlineSchemaNamer struct { + Spec *swspec.Swagger + Operations map[string]opRef +} + +func opRefsByRef(oprefs map[string]opRef) map[string]opRef { + result := make(map[string]opRef, len(oprefs)) + for _, v := range oprefs { + result[v.Ref.String()] = v + } + return result +} + +func (isn *inlineSchemaNamer) Name(key string, schema *swspec.Schema, aschema *AnalyzedSchema) error { + if swspec.Debug { + log.Printf("naming inlined schema at %s", key) + } + + parts := keyParts(key) + for _, name := range namesFromKey(parts, aschema, isn.Operations) { + if name != "" { + // create unique name + newName := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name)) + + // clone schema + sch, err := cloneSchema(schema) + if err != nil { + return err + } + + // replace values on schema + if err := rewriteSchemaToRef(isn.Spec, key, swspec.MustCreateRef("#/definitions/"+newName)); err != nil { + return fmt.Errorf("name inlined schema: %v", err) + } + + sch.AddExtension("x-go-gen-location", genLocation(parts)) + // fmt.Printf("{\n %q,\n \"\",\n spec.MustCreateRef(%q),\n \"\",\n},\n", key, "#/definitions/"+newName) + // save cloned schema to definitions + saveSchema(isn.Spec, newName, sch) + } + } + return nil +} + +func genLocation(parts splitKey) string { + if parts.IsOperation() { + return "operations" + } + if parts.IsDefinition() { + return "models" + } + return "" +} + +func uniqifyName(definitions swspec.Definitions, name string) string { + if name == "" { + name = "oaiGen" + } + if len(definitions) == 0 { + return name + } + + if _, ok := definitions[name]; !ok { + return name + } + name += "OAIGen" + var idx int + unique := name + _, known := definitions[unique] + for known { + idx++ + unique = fmt.Sprintf("%s%d", name, idx) + _, known = definitions[unique] + } + return unique +} + +func namesFromKey(parts splitKey, aschema *AnalyzedSchema, operations map[string]opRef) []string { + var baseNames [][]string + var startIndex int + if parts.IsOperation() { + // params + if parts.IsOperationParam() || parts.IsSharedOperationParam() { + piref := parts.PathItemRef() + if piref.String() != "" && parts.IsOperationParam() { + if op, ok := operations[piref.String()]; ok { + startIndex = 5 + baseNames = append(baseNames, []string{op.ID, "params", "body"}) + } + } else if parts.IsSharedOperationParam() { + pref := parts.PathRef() + for k, v := range operations { + if strings.HasPrefix(k, pref.String()) { + startIndex = 4 + baseNames = append(baseNames, []string{v.ID, "params", "body"}) + } + } + } + } + // responses + if parts.IsOperationResponse() { + piref := parts.PathItemRef() + if piref.String() != "" { + if op, ok := operations[piref.String()]; ok { + startIndex = 6 + baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"}) + } + } + } + } + + // definitions + if parts.IsDefinition() { + nm := parts.DefinitionName() + if nm != "" { + startIndex = 2 + baseNames = append(baseNames, []string{parts.DefinitionName()}) + } + } + + var result []string + for _, segments := range baseNames { + nm := parts.BuildName(segments, startIndex, aschema) + if nm != "" { + result = append(result, nm) + } + } + sort.Strings(result) + return result +} + +const ( + pths = "paths" + responses = "responses" + parameters = "parameters" + definitions = "definitions" +) + +var ignoredKeys map[string]struct{} + +func init() { + ignoredKeys = map[string]struct{}{ + "schema": {}, + "properties": {}, + "not": {}, + "anyOf": {}, + "oneOf": {}, + } +} + +type splitKey []string + +func (s splitKey) IsDefinition() bool { + return len(s) > 1 && s[0] == definitions +} + +func (s splitKey) DefinitionName() string { + if !s.IsDefinition() { + return "" + } + return s[1] +} + +func (s splitKey) BuildName(segments []string, startIndex int, aschema *AnalyzedSchema) string { + for _, part := range s[startIndex:] { + if _, ignored := ignoredKeys[part]; !ignored { + if part == "items" || part == "additionalItems" { + if aschema.IsTuple || aschema.IsTupleWithExtra { + segments = append(segments, "tuple") + } else { + segments = append(segments, "items") + } + if part == "additionalItems" { + segments = append(segments, part) + } + continue + } + segments = append(segments, part) + } + } + return strings.Join(segments, " ") +} + +func (s splitKey) IsOperation() bool { + return len(s) > 1 && s[0] == pths +} + +func (s splitKey) IsSharedOperationParam() bool { + return len(s) > 2 && s[0] == pths && s[2] == parameters +} + +func (s splitKey) IsOperationParam() bool { + return len(s) > 3 && s[0] == pths && s[3] == parameters +} + +func (s splitKey) IsOperationResponse() bool { + return len(s) > 3 && s[0] == pths && s[3] == responses +} + +func (s splitKey) IsDefaultResponse() bool { + return len(s) > 4 && s[0] == pths && s[3] == responses && s[4] == "default" +} + +func (s splitKey) IsStatusCodeResponse() bool { + isInt := func() bool { + _, err := strconv.Atoi(s[4]) + return err == nil + } + return len(s) > 4 && s[0] == pths && s[3] == responses && isInt() +} + +func (s splitKey) ResponseName() string { + if s.IsStatusCodeResponse() { + code, _ := strconv.Atoi(s[4]) + return http.StatusText(code) + } + if s.IsDefaultResponse() { + return "Default" + } + return "" +} + +var validMethods map[string]struct{} + +func init() { + validMethods = map[string]struct{}{ + "GET": {}, + "HEAD": {}, + "OPTIONS": {}, + "PATCH": {}, + "POST": {}, + "PUT": {}, + "DELETE": {}, + } +} + +func (s splitKey) PathItemRef() swspec.Ref { + if len(s) < 3 { + return swspec.Ref{} + } + pth, method := s[1], s[2] + if _, validMethod := validMethods[strings.ToUpper(method)]; !validMethod && !strings.HasPrefix(method, "x-") { + return swspec.Ref{} + } + return swspec.MustCreateRef("#" + path.Join("/", pths, jsonpointer.Escape(pth), strings.ToUpper(method))) +} + +func (s splitKey) PathRef() swspec.Ref { + if !s.IsOperation() { + return swspec.Ref{} + } + return swspec.MustCreateRef("#" + path.Join("/", pths, jsonpointer.Escape(s[1]))) +} + +func keyParts(key string) splitKey { + var res []string + for _, part := range strings.Split(key[1:], "/") { + if part != "" { + res = append(res, jsonpointer.Unescape(part)) + } + } + return res +} + +func rewriteSchemaToRef(spec *swspec.Swagger, key string, ref swspec.Ref) error { + if swspec.Debug { + log.Printf("rewriting schema to ref for %s with %s", key, ref.String()) + } + pth := key[1:] + ptr, err := jsonpointer.New(pth) + if err != nil { + return err + } + + value, _, err := ptr.Get(spec) + if err != nil { + return err + } + + switch refable := value.(type) { + case *swspec.Schema: + return rewriteParentRef(spec, key, ref) + case *swspec.SchemaOrBool: + if refable.Schema != nil { + refable.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + } + case *swspec.SchemaOrArray: + if refable.Schema != nil { + refable.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + } + case swspec.Schema: + return rewriteParentRef(spec, key, ref) + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +func rewriteParentRef(spec *swspec.Swagger, key string, ref swspec.Ref) error { + pth := key[1:] + parent, entry := path.Dir(pth), path.Base(pth) + if swspec.Debug { + log.Println("getting schema holder at:", parent) + } + + pptr, err := jsonpointer.New(parent) + if err != nil { + return err + } + pvalue, _, err := pptr.Get(spec) + if err != nil { + return fmt.Errorf("can't get parent for %s: %v", parent, err) + } + if swspec.Debug { + log.Printf("rewriting holder for %T", pvalue) + } + + switch container := pvalue.(type) { + case swspec.Response: + if err := rewriteParentRef(spec, "#"+parent, ref); err != nil { + return err + } + + case *swspec.Response: + container.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case *swspec.Responses: + statusCode, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + resp := container.StatusCodeResponses[statusCode] + resp.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container.StatusCodeResponses[statusCode] = resp + + case map[string]swspec.Response: + resp := container[entry] + resp.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container[entry] = resp + + case swspec.Parameter: + if err := rewriteParentRef(spec, "#"+parent, ref); err != nil { + return err + } + + case map[string]swspec.Parameter: + param := container[entry] + param.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container[entry] = param + + case []swspec.Parameter: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + param := container[idx] + param.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container[idx] = param + + case swspec.Definitions: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case map[string]swspec.Schema: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case []swspec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case *swspec.SchemaOrArray: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container.Schemas[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + default: + return fmt.Errorf("unhandled parent schema rewrite %s (%T)", key, pvalue) + } + return nil +} + +func cloneSchema(schema *swspec.Schema) (*swspec.Schema, error) { + var sch swspec.Schema + if err := swag.FromDynamicJSON(schema, &sch); err != nil { + return nil, fmt.Errorf("name inlined schema: %v", err) + } + return &sch, nil +} + +func importExternalReferences(opts *FlattenOpts) error { + groupedRefs := reverseIndexForSchemaRefs(opts) + + for refStr, entry := range groupedRefs { + if !entry.Ref.HasFragmentOnly { + if swspec.Debug { + log.Printf("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr) + } + // resolve to actual schema + sch, err := swspec.ResolveRefWithBase(opts.Swagger(), &entry.Ref, opts.ExpandOpts(false)) + if err != nil { + return err + } + if sch == nil { + return fmt.Errorf("no schema found at %s for [%s]", refStr, strings.Join(entry.Keys, ", ")) + } + if swspec.Debug { + log.Printf("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr) + } + + // generate a unique name + newName := uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref)) + if swspec.Debug { + log.Printf("new name for [%s]: %s", strings.Join(entry.Keys, ", "), newName) + } + + // rewrite the external refs to local ones + for _, key := range entry.Keys { + if err := updateRef(opts.Swagger(), key, swspec.MustCreateRef("#"+path.Join("/definitions", newName))); err != nil { + return err + } + } + + // add the resolved schema to the definitions + saveSchema(opts.Swagger(), newName, sch) + } + } + return nil +} + +type refRevIdx struct { + Ref swspec.Ref + Keys []string +} + +func reverseIndexForSchemaRefs(opts *FlattenOpts) map[string]refRevIdx { + collected := make(map[string]refRevIdx) + for key, schRef := range opts.Spec.references.schemas { + if entry, ok := collected[schRef.String()]; ok { + entry.Keys = append(entry.Keys, key) + collected[schRef.String()] = entry + } else { + collected[schRef.String()] = refRevIdx{ + Ref: schRef, + Keys: []string{key}, + } + } + } + return collected +} + +func nameFromRef(ref swspec.Ref) string { + u := ref.GetURL() + if u.Fragment != "" { + return swag.ToJSONName(path.Base(u.Fragment)) + } + if u.Path != "" { + bn := path.Base(u.Path) + if bn != "" && bn != "/" { + ext := path.Ext(bn) + if ext != "" { + return swag.ToJSONName(bn[:len(bn)-len(ext)]) + } + return swag.ToJSONName(bn) + } + } + return swag.ToJSONName(strings.Replace(u.Host, ".", " ", -1)) +} + +func saveSchema(spec *swspec.Swagger, name string, schema *swspec.Schema) { + if schema == nil { + return + } + if spec.Definitions == nil { + spec.Definitions = make(map[string]swspec.Schema, 150) + } + spec.Definitions[name] = *schema +} + +func updateRef(spec *swspec.Swagger, key string, ref swspec.Ref) error { + if swspec.Debug { + log.Printf("updating ref for %s with %s", key, ref.String()) + } + pth := key[1:] + ptr, err := jsonpointer.New(pth) + if err != nil { + return err + } + + value, _, err := ptr.Get(spec) + if err != nil { + return err + } + + switch refable := value.(type) { + case *swspec.Schema: + refable.Ref = ref + case *swspec.SchemaOrBool: + if refable.Schema != nil { + refable.Schema.Ref = ref + } + case *swspec.SchemaOrArray: + if refable.Schema != nil { + refable.Schema.Ref = ref + } + case swspec.Schema: + parent, entry := path.Dir(pth), path.Base(pth) + if swspec.Debug { + log.Println("getting schema holder at:", parent) + } + + pptr, err := jsonpointer.New(parent) + if err != nil { + return err + } + pvalue, _, err := pptr.Get(spec) + if err != nil { + return fmt.Errorf("can't get parent for %s: %v", parent, err) + } + + switch container := pvalue.(type) { + case swspec.Definitions: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case map[string]swspec.Schema: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case []swspec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case *swspec.SchemaOrArray: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container.Schemas[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + } + + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +func containsString(names []string, name string) bool { + for _, nm := range names { + if nm == name { + return true + } + } + return false +} + +type opRef struct { + Method string + Path string + Key string + ID string + Op *swspec.Operation + Ref swspec.Ref +} + +type opRefs []opRef + +func (o opRefs) Len() int { return len(o) } +func (o opRefs) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o opRefs) Less(i, j int) bool { return o[i].Key < o[j].Key } + +func gatherOperations(specDoc *Spec, operationIDs []string) map[string]opRef { + var oprefs opRefs + + for method, pathItem := range specDoc.Operations() { + for pth, operation := range pathItem { + vv := *operation + oprefs = append(oprefs, opRef{ + Key: swag.ToGoName(strings.ToLower(method) + " " + pth), + Method: method, + Path: pth, + ID: vv.ID, + Op: &vv, + Ref: swspec.MustCreateRef("#" + path.Join("/paths", jsonpointer.Escape(pth), method)), + }) + } + } + + sort.Sort(oprefs) + + operations := make(map[string]opRef) + for _, opr := range oprefs { + nm := opr.ID + if nm == "" { + nm = opr.Key + } + + oo, found := operations[nm] + if found && oo.Method != opr.Method && oo.Path != opr.Path { + nm = opr.Key + } + if len(operationIDs) == 0 || containsString(operationIDs, opr.ID) || containsString(operationIDs, nm) { + opr.ID = nm + opr.Op.ID = nm + operations[nm] = opr + } + } + + return operations +} diff --git a/vendor/github.com/go-openapi/analysis/flatten_test.go b/vendor/github.com/go-openapi/analysis/flatten_test.go new file mode 100644 index 0000000000..6ccdf77513 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/flatten_test.go @@ -0,0 +1,805 @@ +package analysis + +import ( + "path/filepath" + "strings" + "testing" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" + "github.com/stretchr/testify/assert" +) + +func TestSaveDefinition(t *testing.T) { + sp := &spec.Swagger{} + saveSchema(sp, "theName", spec.StringProperty()) + assert.Contains(t, sp.Definitions, "theName") +} + +func TestNameFromRef(t *testing.T) { + values := []struct{ Source, Expected string }{ + {"#/definitions/errorModel", "errorModel"}, + {"http://somewhere.com/definitions/errorModel", "errorModel"}, + {"http://somewhere.com/definitions/errorModel.json", "errorModel"}, + {"/definitions/errorModel", "errorModel"}, + {"/definitions/errorModel.json", "errorModel"}, + {"http://somewhere.com", "somewhereCom"}, + {"#", ""}, + } + + for _, v := range values { + assert.Equal(t, v.Expected, nameFromRef(spec.MustCreateRef(v.Source))) + } +} + +func TestDefinitionName(t *testing.T) { + values := []struct { + Source, Expected string + Definitions spec.Definitions + }{ + {"#/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)}, + {"http://somewhere.com/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)}, + {"#/definitions/errorModel", "errorModel", map[string]spec.Schema{"apples": *spec.StringProperty()}}, + {"#/definitions/errorModel", "errorModelOAIGen", map[string]spec.Schema{"errorModel": *spec.StringProperty()}}, + {"#/definitions/errorModel", "errorModelOAIGen1", map[string]spec.Schema{"errorModel": *spec.StringProperty(), "errorModelOAIGen": *spec.StringProperty()}}, + {"#", "oaiGen", nil}, + } + + for _, v := range values { + assert.Equal(t, v.Expected, uniqifyName(v.Definitions, nameFromRef(spec.MustCreateRef(v.Source)))) + } +} + +func TestUpdateRef(t *testing.T) { + bp := filepath.Join("fixtures", "external_definitions.yml") + sp, err := loadSpec(bp) + if assert.NoError(t, err) { + + values := []struct { + Key string + Ref spec.Ref + }{ + {"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")}, + {"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")}, + } + + for _, v := range values { + err := updateRef(sp, v.Key, v.Ref) + if assert.NoError(t, err) { + ptr, err := jsonpointer.New(v.Key[1:]) + if assert.NoError(t, err) { + vv, _, err := ptr.Get(sp) + + if assert.NoError(t, err) { + switch tv := vv.(type) { + case *spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String()) + case spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String()) + case *spec.SchemaOrBool: + assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String()) + case *spec.SchemaOrArray: + assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String()) + default: + assert.Fail(t, "unknown type", "got %T", vv) + } + } + } + } + } + } +} + +func TestImportExternalReferences(t *testing.T) { + bp := filepath.Join(".", "fixtures", "external_definitions.yml") + sp, err := loadSpec(bp) + if assert.NoError(t, err) { + + values := []struct { + Key string + Ref spec.Ref + }{ + {"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")}, + {"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")}, + } + for _, v := range values { + // technically not necessary to run for each value, but if things go right + // this is idempotent, so having it repeat shouldn't matter + // this validates that behavior + err := importExternalReferences(&FlattenOpts{ + Spec: New(sp), + BasePath: bp, + }) + + if assert.NoError(t, err) { + + ptr, err := jsonpointer.New(v.Key[1:]) + if assert.NoError(t, err) { + vv, _, err := ptr.Get(sp) + + if assert.NoError(t, err) { + switch tv := vv.(type) { + case *spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key) + case spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key) + case *spec.SchemaOrBool: + assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key) + case *spec.SchemaOrArray: + assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key) + default: + assert.Fail(t, "unknown type", "got %T", vv) + } + } + } + } + } + assert.Len(t, sp.Definitions, 11) + assert.Contains(t, sp.Definitions, "tag") + assert.Contains(t, sp.Definitions, "named") + assert.Contains(t, sp.Definitions, "record") + } +} + +func TestRewriteSchemaRef(t *testing.T) { + bp := filepath.Join("fixtures", "inline_schemas.yml") + sp, err := loadSpec(bp) + if assert.NoError(t, err) { + + values := []struct { + Key string + Ref spec.Ref + }{ + {"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")}, + {"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")}, + {"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")}, + {"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")}, + } + + for i, v := range values { + err := rewriteSchemaToRef(sp, v.Key, v.Ref) + if assert.NoError(t, err) { + ptr, err := jsonpointer.New(v.Key[1:]) + if assert.NoError(t, err) { + vv, _, err := ptr.Get(sp) + + if assert.NoError(t, err) { + switch tv := vv.(type) { + case *spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) + case spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) + case *spec.SchemaOrBool: + assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "at %d for %s", i, v.Key) + case *spec.SchemaOrArray: + assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "at %d for %s", i, v.Key) + default: + assert.Fail(t, "unknown type", "got %T", vv) + } + } + } + } + } + } +} + +func TestSplitKey(t *testing.T) { + + type KeyFlag uint64 + + const ( + isOperation KeyFlag = 1 << iota + isDefinition + isSharedOperationParam + isOperationParam + isOperationResponse + isDefaultResponse + isStatusCodeResponse + ) + + values := []struct { + Key string + Flags KeyFlag + PathItemRef spec.Ref + PathRef spec.Ref + Name string + }{ + { + "#/paths/~1some~1where~1{id}/parameters/1/schema", + isOperation | isSharedOperationParam, + spec.Ref{}, + spec.MustCreateRef("#/paths/~1some~1where~1{id}"), + "", + }, + { + "#/paths/~1some~1where~1{id}/get/parameters/2/schema", + isOperation | isOperationParam, + spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), + spec.MustCreateRef("#/paths/~1some~1where~1{id}"), + "", + }, + { + "#/paths/~1some~1where~1{id}/get/responses/default/schema", + isOperation | isOperationResponse | isDefaultResponse, + spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), + spec.MustCreateRef("#/paths/~1some~1where~1{id}"), + "Default", + }, + { + "#/paths/~1some~1where~1{id}/get/responses/200/schema", + isOperation | isOperationResponse | isStatusCodeResponse, + spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), + spec.MustCreateRef("#/paths/~1some~1where~1{id}"), + "OK", + }, + { + "#/definitions/namedAgain", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "namedAgain", + }, + { + "#/definitions/datedRecords/items/1", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "datedRecords", + }, + { + "#/definitions/datedRecords/items/1", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "datedRecords", + }, + { + "#/definitions/datedTaggedRecords/items/1", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "datedTaggedRecords", + }, + { + "#/definitions/datedTaggedRecords/additionalItems", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "datedTaggedRecords", + }, + { + "#/definitions/otherRecords/items", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "otherRecords", + }, + { + "#/definitions/tags/additionalProperties", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "tags", + }, + { + "#/definitions/namedThing/properties/name", + isDefinition, + spec.Ref{}, + spec.Ref{}, + "namedThing", + }, + } + + for i, v := range values { + parts := keyParts(v.Key) + pref := parts.PathRef() + piref := parts.PathItemRef() + assert.Equal(t, v.PathRef.String(), pref.String(), "pathRef: %s at %d", v.Key, i) + assert.Equal(t, v.PathItemRef.String(), piref.String(), "pathItemRef: %s at %d", v.Key, i) + + if v.Flags&isOperation != 0 { + assert.True(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i) + } + if v.Flags&isDefinition != 0 { + assert.True(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i) + assert.Equal(t, v.Name, parts.DefinitionName(), "definition name: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i) + if v.Name != "" { + assert.Equal(t, v.Name, parts.ResponseName(), "response name: %s at %d", v.Key, i) + } + } + if v.Flags&isOperationParam != 0 { + assert.True(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i) + } + if v.Flags&isSharedOperationParam != 0 { + assert.True(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i) + } + if v.Flags&isOperationResponse != 0 { + assert.True(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i) + } + if v.Flags&isDefaultResponse != 0 { + assert.True(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i) + } + if v.Flags&isStatusCodeResponse != 0 { + assert.True(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i) + } else { + assert.False(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i) + } + } +} + +func definitionPtr(key string) string { + if !strings.HasPrefix(key, "#/definitions") { + return key + } + return strings.Join(strings.Split(key, "/")[:3], "/") +} + +func TestNamesFromKey(t *testing.T) { + bp := filepath.Join("fixtures", "inline_schemas.yml") + sp, err := loadSpec(bp) + if assert.NoError(t, err) { + + values := []struct { + Key string + Names []string + }{ + {"#/paths/~1some~1where~1{id}/parameters/1/schema", []string{"GetSomeWhereID params body", "PostSomeWhereID params body"}}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", []string{"GetSomeWhereID params body"}}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema", []string{"GetSomeWhereID Default body"}}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema", []string{"GetSomeWhereID OK body"}}, + {"#/definitions/namedAgain", []string{"namedAgain"}}, + {"#/definitions/datedTag/allOf/1", []string{"datedTag allOf 1"}}, + {"#/definitions/datedRecords/items/1", []string{"datedRecords tuple 1"}}, + {"#/definitions/datedTaggedRecords/items/1", []string{"datedTaggedRecords tuple 1"}}, + {"#/definitions/datedTaggedRecords/additionalItems", []string{"datedTaggedRecords tuple additionalItems"}}, + {"#/definitions/otherRecords/items", []string{"otherRecords items"}}, + {"#/definitions/tags/additionalProperties", []string{"tags additionalProperties"}}, + {"#/definitions/namedThing/properties/name", []string{"namedThing name"}}, + } + + for i, v := range values { + ptr, err := jsonpointer.New(definitionPtr(v.Key)[1:]) + if assert.NoError(t, err) { + vv, _, err := ptr.Get(sp) + if assert.NoError(t, err) { + switch tv := vv.(type) { + case *spec.Schema: + aschema, err := Schema(SchemaOpts{Schema: tv, Root: sp, BasePath: bp}) + if assert.NoError(t, err) { + names := namesFromKey(keyParts(v.Key), aschema, opRefsByRef(gatherOperations(New(sp), nil))) + assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i) + } + case spec.Schema: + aschema, err := Schema(SchemaOpts{Schema: &tv, Root: sp, BasePath: bp}) + if assert.NoError(t, err) { + names := namesFromKey(keyParts(v.Key), aschema, opRefsByRef(gatherOperations(New(sp), nil))) + assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i) + } + default: + assert.Fail(t, "unknown type", "got %T", vv) + } + } + } + } + } +} + +func TestDepthFirstSort(t *testing.T) { + bp := filepath.Join("fixtures", "inline_schemas.yml") + sp, err := loadSpec(bp) + values := []string{ + "#/paths/~1some~1where~1{id}/parameters/1/schema/properties/createdAt", + "#/paths/~1some~1where~1{id}/parameters/1/schema", + "#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/createdAt", + "#/paths/~1some~1where~1{id}/get/parameters/2/schema", + "#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/id", + "#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/value", + "#/paths/~1some~1where~1{id}/get/responses/200/schema", + "#/paths/~1some~1where~1{id}/get/responses/404/schema", + "#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/createdAt", + "#/paths/~1some~1where~1{id}/get/responses/default/schema", + "#/definitions/datedRecords/items/1/properties/createdAt", + "#/definitions/datedTaggedRecords/items/1/properties/createdAt", + "#/definitions/namedThing/properties/name/properties/id", + "#/definitions/records/items/0/properties/createdAt", + "#/definitions/datedTaggedRecords/additionalItems/properties/id", + "#/definitions/datedTaggedRecords/additionalItems/properties/value", + "#/definitions/otherRecords/items/properties/createdAt", + "#/definitions/tags/additionalProperties/properties/id", + "#/definitions/tags/additionalProperties/properties/value", + "#/definitions/datedRecords/items/0", + "#/definitions/datedRecords/items/1", + "#/definitions/datedTag/allOf/0", + "#/definitions/datedTag/allOf/1", + "#/definitions/datedTag/properties/id", + "#/definitions/datedTag/properties/value", + "#/definitions/datedTaggedRecords/items/0", + "#/definitions/datedTaggedRecords/items/1", + "#/definitions/namedAgain/properties/id", + "#/definitions/namedThing/properties/name", + "#/definitions/pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism/properties/floccinaucinihilipilificationCreatedAt", + "#/definitions/records/items/0", + "#/definitions/datedTaggedRecords/additionalItems", + "#/definitions/otherRecords/items", + "#/definitions/tags/additionalProperties", + "#/definitions/datedRecords", + "#/definitions/datedTag", + "#/definitions/datedTaggedRecords", + "#/definitions/namedAgain", + "#/definitions/namedThing", + "#/definitions/otherRecords", + "#/definitions/pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism", + "#/definitions/records", + "#/definitions/tags", + } + if assert.NoError(t, err) { + a := New(sp) + result := sortDepthFirst(a.allSchemas) + assert.Equal(t, values, result) + } +} + +func TestNameInlinedSchemas(t *testing.T) { + bp := filepath.Join(".", "fixtures", "nested_inline_schemas.yml") + sp, err := loadSpec(bp) + values := []struct { + Key string + Location string + Ref spec.Ref + }{ + {"#/paths/~1some~1where~1{id}/parameters/1/schema/items", "#/definitions/postSomeWhereIdParamsBody/items", spec.MustCreateRef("#/definitions/postSomeWhereIdParamsBodyItems")}, + {"#/paths/~1some~1where~1{id}/parameters/1/schema", "#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/postSomeWhereIdParamsBody")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2/properties/name", "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/1", "#/definitions/getSomeWhereIdParamsBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2", "#/definitions/getSomeWhereIdParamsBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record", "#/definitions/getSomeWhereIdParamsBodyOAIGen/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord")}, + {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", "#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyOAIGen")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2/properties/name", "#/definitions/getSomeWhereIdOKBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2Name")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/1", "#/definitions/getSomeWhereIdOKBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems1")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2", "#/definitions/getSomeWhereIdOKBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record", "#/definitions/getSomeWhereIdOKBody/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecord")}, + {"#/paths/~1some~1where~1{id}/get/responses/200/schema", "#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2/properties/name", "#/definitions/getSomeWhereIdDefaultBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2Name")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/1", "#/definitions/getSomeWhereIdDefaultBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems1")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2", "#/definitions/getSomeWhereIdDefaultBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record", "#/definitions/getSomeWhereIdDefaultBody/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecord")}, + {"#/paths/~1some~1where~1{id}/get/responses/default/schema", "#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBody")}, + {"#/definitions/nestedThing/properties/record/items/2/allOf/1/additionalProperties", "#/definitions/nestedThingRecordItems2AllOf1/additionalProperties", spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1AdditionalProperties")}, + {"#/definitions/nestedThing/properties/record/items/2/allOf/1", "#/definitions/nestedThingRecordItems2/allOf/1", spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1")}, + {"#/definitions/nestedThing/properties/record/items/2/properties/name", "#/definitions/nestedThingRecordItems2/properties/name", spec.MustCreateRef("#/definitions/nestedThingRecordItems2Name")}, + {"#/definitions/nestedThing/properties/record/items/1", "#/definitions/nestedThingRecord/items/1", spec.MustCreateRef("#/definitions/nestedThingRecordItems1")}, + {"#/definitions/nestedThing/properties/record/items/2", "#/definitions/nestedThingRecord/items/2", spec.MustCreateRef("#/definitions/nestedThingRecordItems2")}, + {"#/definitions/datedRecords/items/1", "#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/datedRecordsItems1")}, + {"#/definitions/datedTaggedRecords/items/1", "#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/datedTaggedRecordsItems1")}, + {"#/definitions/namedThing/properties/name", "#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/namedThingName")}, + {"#/definitions/nestedThing/properties/record", "#/definitions/nestedThing/properties/record", spec.MustCreateRef("#/definitions/nestedThingRecord")}, + {"#/definitions/records/items/0", "#/definitions/records/items/0", spec.MustCreateRef("#/definitions/recordsItems0")}, + {"#/definitions/datedTaggedRecords/additionalItems", "#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/datedTaggedRecordsItemsAdditionalItems")}, + {"#/definitions/otherRecords/items", "#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/otherRecordsItems")}, + {"#/definitions/tags/additionalProperties", "#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tagsAdditionalProperties")}, + } + if assert.NoError(t, err) { + err := nameInlinedSchemas(&FlattenOpts{ + Spec: New(sp), + BasePath: bp, + }) + + if assert.NoError(t, err) { + for i, v := range values { + ptr, err := jsonpointer.New(v.Location[1:]) + if assert.NoError(t, err, "at %d for %s", i, v.Key) { + vv, _, err := ptr.Get(sp) + + if assert.NoError(t, err, "at %d for %s", i, v.Key) { + switch tv := vv.(type) { + case *spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) + case spec.Schema: + assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) + case *spec.SchemaOrBool: + var sRef spec.Ref + if tv != nil && tv.Schema != nil { + sRef = tv.Schema.Ref + } + assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) + case *spec.SchemaOrArray: + var sRef spec.Ref + if tv != nil && tv.Schema != nil { + sRef = tv.Schema.Ref + } + assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) + default: + assert.Fail(t, "unknown type", "got %T", vv) + } + } + } + } + } + + for k, rr := range New(sp).allSchemas { + if !strings.HasPrefix(k, "#/responses") && !strings.HasPrefix(k, "#/parameters") { + if rr.Schema != nil && rr.Schema.Ref.String() == "" && !rr.TopLevel { + asch, err := Schema(SchemaOpts{Schema: rr.Schema, Root: sp, BasePath: bp}) + if assert.NoError(t, err, "for key: %s", k) { + if !asch.IsSimpleSchema { + assert.Fail(t, "not a top level schema", "for key: %s", k) + } + } + } + } + } + } +} + +func TestFlatten(t *testing.T) { + bp := filepath.Join(".", "fixtures", "flatten.yml") + sp, err := loadSpec(bp) + values := []struct { + Key string + Location string + Ref spec.Ref + Expected interface{} + }{ + { + "#/responses/notFound/schema", + "#/responses/notFound/schema", + spec.MustCreateRef("#/definitions/error"), + nil, + }, + { + "#/paths/~1some~1where~1{id}/parameters/0", + "#/paths/~1some~1where~1{id}/parameters/0/name", + spec.Ref{}, + "id", + }, + { + "#/paths/~1other~1place", + "#/paths/~1other~1place/get/operationId", + spec.Ref{}, + "modelOp", + }, + { + "#/paths/~1some~1where~1{id}/get/parameters/0", + "#/paths/~1some~1where~1{id}/get/parameters/0/name", + spec.Ref{}, + "limit", + }, + { + "#/paths/~1some~1where~1{id}/get/parameters/1", + "#/paths/~1some~1where~1{id}/get/parameters/1/name", + spec.Ref{}, + "some", + }, + { + "#/paths/~1some~1where~1{id}/get/parameters/2", + "#/paths/~1some~1where~1{id}/get/parameters/2/name", + spec.Ref{}, + "other", + }, + { + "#/paths/~1some~1where~1{id}/get/parameters/3", + "#/paths/~1some~1where~1{id}/get/parameters/3/schema", + spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBody"), + "", + }, + { + "#/paths/~1some~1where~1{id}/get/responses/200", + "#/paths/~1some~1where~1{id}/get/responses/200/schema", + spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"), + "", + }, + { + "#/definitions/namedAgain", + "", + spec.MustCreateRef("#/definitions/named"), + "", + }, + { + "#/definitions/namedThing/properties/name", + "", + spec.MustCreateRef("#/definitions/named"), + "", + }, + { + "#/definitions/namedThing/properties/namedAgain", + "", + spec.MustCreateRef("#/definitions/namedAgain"), + "", + }, + { + "#/definitions/datedRecords/items/1", + "", + spec.MustCreateRef("#/definitions/record"), + "", + }, + { + "#/definitions/otherRecords/items", + "", + spec.MustCreateRef("#/definitions/record"), + "", + }, + { + "#/definitions/tags/additionalProperties", + "", + spec.MustCreateRef("#/definitions/tag"), + "", + }, + { + "#/definitions/datedTag/allOf/1", + "", + spec.MustCreateRef("#/definitions/tag"), + "", + }, + { + "#/definitions/nestedThingRecordItems2/allOf/1", + "", + spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"), + "", + }, + { + "#/definitions/nestedThingRecord/items/1", + "", + spec.MustCreateRef("#/definitions/nestedThingRecordItems1"), + "", + }, + { + "#/definitions/nestedThingRecord/items/2", + "", + spec.MustCreateRef("#/definitions/nestedThingRecordItems2"), + "", + }, + { + "#/definitions/nestedThing/properties/record", + "", + spec.MustCreateRef("#/definitions/nestedThingRecord"), + "", + }, + { + "#/definitions/named", + "#/definitions/named/type", + spec.Ref{}, + spec.StringOrArray{"string"}, + }, + { + "#/definitions/error", + "#/definitions/error/properties/id/type", + spec.Ref{}, + spec.StringOrArray{"integer"}, + }, + { + "#/definitions/record", + "#/definitions/record/properties/createdAt/format", + spec.Ref{}, + "date-time", + }, + { + "#/definitions/getSomeWhereIdOKBody", + "#/definitions/getSomeWhereIdOKBody/properties/record", + spec.MustCreateRef("#/definitions/nestedThing"), + nil, + }, + { + "#/definitions/getSomeWhereIdParamsBody", + "#/definitions/getSomeWhereIdParamsBody/properties/record", + spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord"), + nil, + }, + { + "#/definitions/getSomeWhereIdParamsBodyRecord", + "#/definitions/getSomeWhereIdParamsBodyRecord/items/1", + spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"), + nil, + }, + { + "#/definitions/getSomeWhereIdParamsBodyRecord", + "#/definitions/getSomeWhereIdParamsBodyRecord/items/2", + spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"), + nil, + }, + { + "#/definitions/getSomeWhereIdParamsBodyRecordItems2", + "#/definitions/getSomeWhereIdParamsBodyRecordItems2/allOf/0/format", + spec.Ref{}, + "date", + }, + { + "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name", + "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name/properties/createdAt/format", + spec.Ref{}, + "date-time", + }, + { + "#/definitions/getSomeWhereIdParamsBodyRecordItems2", + "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name", + spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"), + "date", + }, + } + if assert.NoError(t, err) { + err := Flatten(FlattenOpts{Spec: New(sp), BasePath: bp}) + if assert.NoError(t, err) { + for i, v := range values { + pk := v.Key[1:] + if v.Location != "" { + pk = v.Location[1:] + } + ptr, err := jsonpointer.New(pk) + if assert.NoError(t, err, "at %d for %s", i, v.Key) { + d, _, err := ptr.Get(sp) + if assert.NoError(t, err) { + if v.Ref.String() != "" { + switch s := d.(type) { + case *spec.Schema: + assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key) + case spec.Schema: + assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key) + case *spec.SchemaOrArray: + var sRef spec.Ref + if s != nil && s.Schema != nil { + sRef = s.Schema.Ref + } + assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) + case *spec.SchemaOrBool: + var sRef spec.Ref + if s != nil && s.Schema != nil { + sRef = s.Schema.Ref + } + assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) + default: + assert.Fail(t, "unknown type", "got %T at %d for %s", d, i, v.Key) + } + } else { + assert.Equal(t, v.Expected, d) + } + } + } + } + } + } +} diff --git a/vendor/github.com/go-openapi/analysis/mixin.go b/vendor/github.com/go-openapi/analysis/mixin.go new file mode 100644 index 0000000000..a547433ba8 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/mixin.go @@ -0,0 +1,199 @@ +package analysis + +import ( + "fmt" + + "github.com/go-openapi/spec" +) + +// Mixin modifies the primary swagger spec by adding the paths and +// definitions from the mixin specs. Top level parameters and +// responses from the mixins are also carried over. Operation id +// collisions are avoided by appending "Mixin" but only if +// needed. No other parts of primary are modified. Consider calling +// FixEmptyResponseDescriptions() on the modified primary if you read +// them from storage and they are valid to start with. +// +// Entries in "paths", "definitions", "parameters" and "responses" are +// added to the primary in the order of the given mixins. If the entry +// already exists in primary it is skipped with a warning message. +// +// The count of skipped entries (from collisions) is returned so any +// deviation from the number expected can flag warning in your build +// scripts. Carefully review the collisions before accepting them; +// consider renaming things if possible. +// +// No normalization of any keys takes place (paths, type defs, +// etc). Ensure they are canonical if your downstream tools do +// key normalization of any form. +func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string { + var skipped []string + opIds := getOpIds(primary) + if primary.Paths == nil { + primary.Paths = &spec.Paths{Paths: make(map[string]spec.PathItem)} + } + if primary.Paths.Paths == nil { + primary.Paths.Paths = make(map[string]spec.PathItem) + } + if primary.Definitions == nil { + primary.Definitions = make(spec.Definitions) + } + if primary.Parameters == nil { + primary.Parameters = make(map[string]spec.Parameter) + } + if primary.Responses == nil { + primary.Responses = make(map[string]spec.Response) + } + + for i, m := range mixins { + for k, v := range m.Definitions { + // assume name collisions represent IDENTICAL type. careful. + if _, exists := primary.Definitions[k]; exists { + warn := fmt.Sprintf("definitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.Definitions[k] = v + } + if m.Paths != nil { + for k, v := range m.Paths.Paths { + if _, exists := primary.Paths.Paths[k]; exists { + warn := fmt.Sprintf("paths entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + + // Swagger requires that operationIds be + // unique within a spec. If we find a + // collision we append "Mixin0" to the + // operatoinId we are adding, where 0 is mixin + // index. We assume that operationIds with + // all the proivded specs are already unique. + piops := pathItemOps(v) + for _, piop := range piops { + if opIds[piop.ID] { + piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", i) + } + opIds[piop.ID] = true + } + primary.Paths.Paths[k] = v + } + } + for k, v := range m.Parameters { + // could try to rename on conflict but would + // have to fix $refs in the mixin. Complain + // for now + if _, exists := primary.Parameters[k]; exists { + warn := fmt.Sprintf("top level parameters entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.Parameters[k] = v + } + for k, v := range m.Responses { + // could try to rename on conflict but would + // have to fix $refs in the mixin. Complain + // for now + if _, exists := primary.Responses[k]; exists { + warn := fmt.Sprintf("top level responses entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.Responses[k] = v + } + } + return skipped +} + +// FixEmptyResponseDescriptions replaces empty ("") response +// descriptions in the input with "(empty)" to ensure that the +// resulting Swagger is stays valid. The problem appears to arise +// from reading in valid specs that have a explicit response +// description of "" (valid, response.description is required), but +// due to zero values being omitted upon re-serializing (omitempty) we +// lose them unless we stick some chars in there. +func FixEmptyResponseDescriptions(s *spec.Swagger) { + if s.Paths != nil { + for _, v := range s.Paths.Paths { + if v.Get != nil { + FixEmptyDescs(v.Get.Responses) + } + if v.Put != nil { + FixEmptyDescs(v.Put.Responses) + } + if v.Post != nil { + FixEmptyDescs(v.Post.Responses) + } + if v.Delete != nil { + FixEmptyDescs(v.Delete.Responses) + } + if v.Options != nil { + FixEmptyDescs(v.Options.Responses) + } + if v.Head != nil { + FixEmptyDescs(v.Head.Responses) + } + if v.Patch != nil { + FixEmptyDescs(v.Patch.Responses) + } + } + } + for k, v := range s.Responses { + FixEmptyDesc(&v) + s.Responses[k] = v + } +} + +// FixEmptyDescs adds "(empty)" as the description for any Response in +// the given Responses object that doesn't already have one. +func FixEmptyDescs(rs *spec.Responses) { + FixEmptyDesc(rs.Default) + for k, v := range rs.StatusCodeResponses { + FixEmptyDesc(&v) + rs.StatusCodeResponses[k] = v + } +} + +// FixEmptyDesc adds "(empty)" as the description to the given +// Response object if it doesn't already have one and isn't a +// ref. No-op on nil input. +func FixEmptyDesc(rs *spec.Response) { + if rs == nil || rs.Description != "" || rs.Ref.Ref.GetURL() != nil { + return + } + rs.Description = "(empty)" +} + +// getOpIds extracts all the paths..operationIds from the given +// spec and returns them as the keys in a map with 'true' values. +func getOpIds(s *spec.Swagger) map[string]bool { + rv := make(map[string]bool) + if s.Paths == nil { + return rv + } + for _, v := range s.Paths.Paths { + piops := pathItemOps(v) + for _, op := range piops { + rv[op.ID] = true + } + } + return rv +} + +func pathItemOps(p spec.PathItem) []*spec.Operation { + var rv []*spec.Operation + rv = appendOp(rv, p.Get) + rv = appendOp(rv, p.Put) + rv = appendOp(rv, p.Post) + rv = appendOp(rv, p.Delete) + rv = appendOp(rv, p.Head) + rv = appendOp(rv, p.Patch) + return rv +} + +func appendOp(ops []*spec.Operation, op *spec.Operation) []*spec.Operation { + if op == nil { + return ops + } + return append(ops, op) +} diff --git a/vendor/github.com/go-openapi/analysis/mixin_test.go b/vendor/github.com/go-openapi/analysis/mixin_test.go new file mode 100644 index 0000000000..1d8028217f --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/mixin_test.go @@ -0,0 +1,64 @@ +package analysis + +import "testing" + +const ( + widgetFile = "fixtures/widget-crud.yml" + fooFile = "fixtures/foo-crud.yml" + barFile = "fixtures/bar-crud.yml" + noPathsFile = "fixtures/no-paths.yml" + emptyPathsFile = "fixtures/empty-paths.json" +) + +func TestMixin(t *testing.T) { + + primary, err := loadSpec(widgetFile) + if err != nil { + t.Fatalf("Could not load '%v': %v\n", widgetFile, err) + } + mixin1, err := loadSpec(fooFile) + if err != nil { + t.Fatalf("Could not load '%v': %v\n", fooFile, err) + } + mixin2, err := loadSpec(barFile) + if err != nil { + t.Fatalf("Could not load '%v': %v\n", barFile, err) + } + mixin3, err := loadSpec(noPathsFile) + if err != nil { + t.Fatalf("Could not load '%v': %v\n", noPathsFile, err) + } + + collisions := Mixin(primary, mixin1, mixin2, mixin3) + if len(collisions) != 16 { + t.Errorf("TestMixin: Expected 16 collisions, got %v\n%v", len(collisions), collisions) + } + + if len(primary.Paths.Paths) != 7 { + t.Errorf("TestMixin: Expected 7 paths in merged, got %v\n", len(primary.Paths.Paths)) + } + + if len(primary.Definitions) != 8 { + t.Errorf("TestMixin: Expected 8 definitions in merged, got %v\n", len(primary.Definitions)) + } + + if len(primary.Parameters) != 4 { + t.Errorf("TestMixin: Expected 4 top level parameters in merged, got %v\n", len(primary.Parameters)) + } + + if len(primary.Responses) != 2 { + t.Errorf("TestMixin: Expected 2 top level responses in merged, got %v\n", len(primary.Responses)) + } + + // test that adding paths to a primary with no paths works (was NPE) + emptyPaths, err := loadSpec(emptyPathsFile) + if err != nil { + t.Fatalf("Could not load '%v': %v\n", emptyPathsFile, err) + } + + collisions = Mixin(emptyPaths, primary) + if len(collisions) != 0 { + t.Errorf("TestMixin: Expected 0 collisions, got %v\n%v", len(collisions), collisions) + } + +} diff --git a/vendor/github.com/go-openapi/analysis/schema.go b/vendor/github.com/go-openapi/analysis/schema.go new file mode 100644 index 0000000000..b616288bb6 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/schema.go @@ -0,0 +1,233 @@ +package analysis + +import ( + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// SchemaOpts configures the schema analyzer +type SchemaOpts struct { + Schema *spec.Schema + Root interface{} + BasePath string + _ struct{} +} + +// Schema analysis, will classify the schema according to known +// patterns. +func Schema(opts SchemaOpts) (*AnalyzedSchema, error) { + a := &AnalyzedSchema{ + schema: opts.Schema, + root: opts.Root, + basePath: opts.BasePath, + } + + a.initializeFlags() + a.inferKnownType() + a.inferEnum() + a.inferBaseType() + + if err := a.inferMap(); err != nil { + return nil, err + } + if err := a.inferArray(); err != nil { + return nil, err + } + + if err := a.inferTuple(); err != nil { + return nil, err + } + + if err := a.inferFromRef(); err != nil { + return nil, err + } + + a.inferSimpleSchema() + return a, nil +} + +// AnalyzedSchema indicates what the schema represents +type AnalyzedSchema struct { + schema *spec.Schema + root interface{} + basePath string + + hasProps bool + hasAllOf bool + hasItems bool + hasAdditionalProps bool + hasAdditionalItems bool + hasRef bool + + IsKnownType bool + IsSimpleSchema bool + IsArray bool + IsSimpleArray bool + IsMap bool + IsSimpleMap bool + IsExtendedObject bool + IsTuple bool + IsTupleWithExtra bool + IsBaseType bool + IsEnum bool +} + +// Inherits copies value fields from other onto this schema +func (a *AnalyzedSchema) inherits(other *AnalyzedSchema) { + if other == nil { + return + } + a.hasProps = other.hasProps + a.hasAllOf = other.hasAllOf + a.hasItems = other.hasItems + a.hasAdditionalItems = other.hasAdditionalItems + a.hasAdditionalProps = other.hasAdditionalProps + a.hasRef = other.hasRef + + a.IsKnownType = other.IsKnownType + a.IsSimpleSchema = other.IsSimpleSchema + a.IsArray = other.IsArray + a.IsSimpleArray = other.IsSimpleArray + a.IsMap = other.IsMap + a.IsSimpleMap = other.IsSimpleMap + a.IsExtendedObject = other.IsExtendedObject + a.IsTuple = other.IsTuple + a.IsTupleWithExtra = other.IsTupleWithExtra + a.IsBaseType = other.IsBaseType + a.IsEnum = other.IsEnum +} + +func (a *AnalyzedSchema) inferFromRef() error { + if a.hasRef { + opts := &spec.ExpandOptions{RelativeBase: a.basePath} + sch, err := spec.ResolveRefWithBase(a.root, &a.schema.Ref, opts) + if err != nil { + return err + } + if sch != nil { + rsch, err := Schema(SchemaOpts{ + Schema: sch, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.inherits(rsch) + } + } + return nil +} + +func (a *AnalyzedSchema) inferSimpleSchema() { + a.IsSimpleSchema = a.IsKnownType || a.IsSimpleArray || a.IsSimpleMap +} + +func (a *AnalyzedSchema) inferKnownType() { + tpe := a.schema.Type + format := a.schema.Format + a.IsKnownType = tpe.Contains("boolean") || + tpe.Contains("integer") || + tpe.Contains("number") || + tpe.Contains("string") || + (format != "" && strfmt.Default.ContainsName(format)) || + (a.isObjectType() && !a.hasProps && !a.hasAllOf && !a.hasAdditionalProps && !a.hasAdditionalItems) +} + +func (a *AnalyzedSchema) inferMap() error { + if a.isObjectType() { + hasExtra := a.hasProps || a.hasAllOf + a.IsMap = a.hasAdditionalProps && !hasExtra + a.IsExtendedObject = a.hasAdditionalProps && hasExtra + if a.IsMap { + if a.schema.AdditionalProperties.Schema != nil { + msch, err := Schema(SchemaOpts{ + Schema: a.schema.AdditionalProperties.Schema, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.IsSimpleMap = msch.IsSimpleSchema + } else if a.schema.AdditionalProperties.Allows { + a.IsSimpleMap = true + } + } + } + return nil +} + +func (a *AnalyzedSchema) inferArray() error { + fromValid := a.isArrayType() && (a.schema.Items == nil || a.schema.Items.Len() < 2) + a.IsArray = fromValid || (a.hasItems && a.schema.Items.Len() < 2) + if a.IsArray && a.hasItems { + if a.schema.Items.Schema != nil { + itsch, err := Schema(SchemaOpts{ + Schema: a.schema.Items.Schema, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.IsSimpleArray = itsch.IsSimpleSchema + } + if len(a.schema.Items.Schemas) > 0 { + itsch, err := Schema(SchemaOpts{ + Schema: &a.schema.Items.Schemas[0], + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.IsSimpleArray = itsch.IsSimpleSchema + } + } + if a.IsArray && !a.hasItems { + a.IsSimpleArray = true + } + return nil +} + +func (a *AnalyzedSchema) inferTuple() error { + tuple := a.hasItems && a.schema.Items.Len() > 1 + a.IsTuple = tuple && !a.hasAdditionalItems + a.IsTupleWithExtra = tuple && a.hasAdditionalItems + return nil +} + +func (a *AnalyzedSchema) inferBaseType() { + if a.isObjectType() { + a.IsBaseType = a.schema.Discriminator != "" + } +} + +func (a *AnalyzedSchema) inferEnum() { + a.IsEnum = len(a.schema.Enum) > 0 +} + +func (a *AnalyzedSchema) initializeFlags() { + a.hasProps = len(a.schema.Properties) > 0 + a.hasAllOf = len(a.schema.AllOf) > 0 + a.hasRef = a.schema.Ref.String() != "" + + a.hasItems = a.schema.Items != nil && + (a.schema.Items.Schema != nil || len(a.schema.Items.Schemas) > 0) + + a.hasAdditionalProps = a.schema.AdditionalProperties != nil && + (a.schema.AdditionalProperties != nil || a.schema.AdditionalProperties.Allows) + + a.hasAdditionalItems = a.schema.AdditionalItems != nil && + (a.schema.AdditionalItems.Schema != nil || a.schema.AdditionalItems.Allows) + +} + +func (a *AnalyzedSchema) isObjectType() bool { + return !a.hasRef && (a.schema.Type == nil || a.schema.Type.Contains("") || a.schema.Type.Contains("object")) +} + +func (a *AnalyzedSchema) isArrayType() bool { + return !a.hasRef && (a.schema.Type != nil && a.schema.Type.Contains("array")) +} diff --git a/vendor/github.com/go-openapi/analysis/schema_test.go b/vendor/github.com/go-openapi/analysis/schema_test.go new file mode 100644 index 0000000000..0c386b2a4a --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/schema_test.go @@ -0,0 +1,266 @@ +package analysis + +import ( + "encoding/json" + "fmt" + "path" + "testing" + + "net/http" + "net/http/httptest" + + "github.com/go-openapi/spec" + "github.com/stretchr/testify/assert" +) + +var knownSchemas = []*spec.Schema{ + spec.BoolProperty(), // 0 + spec.StringProperty(), // 1 + spec.Int8Property(), // 2 + spec.Int16Property(), // 3 + spec.Int32Property(), // 4 + spec.Int64Property(), // 5 + spec.Float32Property(), // 6 + spec.Float64Property(), // 7 + spec.DateProperty(), // 8 + spec.DateTimeProperty(), // 9 + (&spec.Schema{}), // 10 + (&spec.Schema{}).Typed("object", ""), // 11 + (&spec.Schema{}).Typed("", ""), // 12 + (&spec.Schema{}).Typed("", "uuid"), // 13 +} + +func newCObj() *spec.Schema { + return (&spec.Schema{}).Typed("object", "").SetProperty("id", *spec.Int64Property()) +} + +var complexObject = newCObj() + +var complexSchemas = []*spec.Schema{ + complexObject, + spec.ArrayProperty(complexObject), + spec.MapProperty(complexObject), +} + +func knownRefs(base string) []spec.Ref { + urls := []string{"bool", "string", "integer", "float", "date", "object", "format"} + + var result []spec.Ref + for _, u := range urls { + result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("known", u)))) + } + return result +} + +func complexRefs(base string) []spec.Ref { + urls := []string{"object", "array", "map"} + + var result []spec.Ref + for _, u := range urls { + result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("complex", u)))) + } + return result +} + +func refServer() *httptest.Server { + mux := http.NewServeMux() + mux.Handle("/known/bool", schemaHandler(knownSchemas[0])) + mux.Handle("/known/string", schemaHandler(knownSchemas[1])) + mux.Handle("/known/integer", schemaHandler(knownSchemas[5])) + mux.Handle("/known/float", schemaHandler(knownSchemas[6])) + mux.Handle("/known/date", schemaHandler(knownSchemas[8])) + mux.Handle("/known/object", schemaHandler(knownSchemas[11])) + mux.Handle("/known/format", schemaHandler(knownSchemas[13])) + + mux.Handle("/complex/object", schemaHandler(complexSchemas[0])) + mux.Handle("/complex/array", schemaHandler(complexSchemas[1])) + mux.Handle("/complex/map", schemaHandler(complexSchemas[2])) + + return httptest.NewServer(mux) +} + +func refSchema(ref spec.Ref) *spec.Schema { + return &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}} +} + +func schemaHandler(schema *spec.Schema) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, schema) + }) +} + +func writeJSON(w http.ResponseWriter, data interface{}) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + enc := json.NewEncoder(w) + if err := enc.Encode(data); err != nil { + panic(err) + } +} + +func TestSchemaAnalysis_KnownTypes(t *testing.T) { + for i, v := range knownSchemas { + sch, err := Schema(SchemaOpts{Schema: v}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsKnownType, "item at %d should be a known type", i) + } + } + for i, v := range complexSchemas { + sch, err := Schema(SchemaOpts{Schema: v}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.False(t, sch.IsKnownType, "item at %d should not be a known type", i) + } + } + + serv := refServer() + defer serv.Close() + + for i, ref := range knownRefs(serv.URL) { + sch, err := Schema(SchemaOpts{Schema: refSchema(ref)}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsKnownType, "item at %d should be a known type", i) + } + } + for i, ref := range complexRefs(serv.URL) { + sch, err := Schema(SchemaOpts{Schema: refSchema(ref)}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.False(t, sch.IsKnownType, "item at %d should not be a known type", i) + } + } +} + +func TestSchemaAnalysis_Array(t *testing.T) { + for i, v := range append(knownSchemas, (&spec.Schema{}).Typed("array", "")) { + sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsArray, "item at %d should be an array type", i) + assert.True(t, sch.IsSimpleArray, "item at %d should be a simple array type", i) + } + } + + for i, v := range complexSchemas { + sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsArray, "item at %d should be an array type", i) + assert.False(t, sch.IsSimpleArray, "item at %d should not be a simple array type", i) + } + } + + serv := refServer() + defer serv.Close() + + for i, ref := range knownRefs(serv.URL) { + sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(refSchema(ref))}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsArray, "item at %d should be an array type", i) + assert.True(t, sch.IsSimpleArray, "item at %d should be a simple array type", i) + } + } + for i, ref := range complexRefs(serv.URL) { + sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(refSchema(ref))}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.False(t, sch.IsKnownType, "item at %d should not be a known type", i) + assert.True(t, sch.IsArray, "item at %d should be an array type", i) + assert.False(t, sch.IsSimpleArray, "item at %d should not be a simple array type", i) + } + } + +} + +func TestSchemaAnalysis_Map(t *testing.T) { + for i, v := range append(knownSchemas, spec.MapProperty(nil)) { + sch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsMap, "item at %d should be a map type", i) + assert.True(t, sch.IsSimpleMap, "item at %d should be a simple map type", i) + } + } + + for i, v := range complexSchemas { + sch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsMap, "item at %d should be a map type", i) + assert.False(t, sch.IsSimpleMap, "item at %d should not be a simple map type", i) + } + } +} + +func TestSchemaAnalysis_ExtendedObject(t *testing.T) { + for i, v := range knownSchemas { + wex := spec.MapProperty(v).SetProperty("name", *spec.StringProperty()) + sch, err := Schema(SchemaOpts{Schema: wex}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsExtendedObject, "item at %d should be an extended map object type", i) + assert.False(t, sch.IsMap, "item at %d should not be a map type", i) + assert.False(t, sch.IsSimpleMap, "item at %d should not be a simple map type", i) + } + } +} + +func TestSchemaAnalysis_Tuple(t *testing.T) { + at := spec.ArrayProperty(nil) + at.Items = &spec.SchemaOrArray{} + at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty(), *spec.Int64Property()) + + sch, err := Schema(SchemaOpts{Schema: at}) + if assert.NoError(t, err) { + assert.True(t, sch.IsTuple) + assert.False(t, sch.IsTupleWithExtra) + assert.False(t, sch.IsKnownType) + assert.False(t, sch.IsSimpleSchema) + } +} + +func TestSchemaAnalysis_TupleWithExtra(t *testing.T) { + at := spec.ArrayProperty(nil) + at.Items = &spec.SchemaOrArray{} + at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty(), *spec.Int64Property()) + at.AdditionalItems = &spec.SchemaOrBool{Allows: true} + at.AdditionalItems.Schema = spec.Int32Property() + + sch, err := Schema(SchemaOpts{Schema: at}) + if assert.NoError(t, err) { + assert.False(t, sch.IsTuple) + assert.True(t, sch.IsTupleWithExtra) + assert.False(t, sch.IsKnownType) + assert.False(t, sch.IsSimpleSchema) + } +} + +func TestSchemaAnalysis_BaseType(t *testing.T) { + cl := (&spec.Schema{}).Typed("object", "").SetProperty("type", *spec.StringProperty()).WithDiscriminator("type") + + sch, err := Schema(SchemaOpts{Schema: cl}) + if assert.NoError(t, err) { + assert.True(t, sch.IsBaseType) + assert.False(t, sch.IsKnownType) + assert.False(t, sch.IsSimpleSchema) + } +} + +func TestSchemaAnalysis_SimpleSchema(t *testing.T) { + for i, v := range append(knownSchemas, spec.ArrayProperty(nil), spec.MapProperty(nil)) { + sch, err := Schema(SchemaOpts{Schema: v}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.True(t, sch.IsSimpleSchema, "item at %d should be a simple schema", i) + } + + asch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)}) + if assert.NoError(t, err, "failed to analyze array schema at %d: %v", i, err) { + assert.True(t, asch.IsSimpleSchema, "array item at %d should be a simple schema", i) + } + + msch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)}) + if assert.NoError(t, err, "failed to analyze map schema at %d: %v", i, err) { + assert.True(t, msch.IsSimpleSchema, "map item at %d should be a simple schema", i) + } + } + + for i, v := range complexSchemas { + sch, err := Schema(SchemaOpts{Schema: v}) + if assert.NoError(t, err, "failed to analyze schema at %d: %v", i, err) { + assert.False(t, sch.IsSimpleSchema, "item at %d should not be a simple schema", i) + } + } + +} diff --git a/vendor/github.com/go-openapi/runtime/.travis.yml b/vendor/github.com/go-openapi/runtime/.travis.yml index a555674e5f..192d6d27b9 100644 --- a/vendor/github.com/go-openapi/runtime/.travis.yml +++ b/vendor/github.com/go-openapi/runtime/.travis.yml @@ -1,5 +1,7 @@ language: go +dist: trusty go: +- 1.7.x - 1.8.x install: - go get -u github.com/axw/gocov/gocov diff --git a/vendor/github.com/go-openapi/runtime/authinfo_test.go b/vendor/github.com/go-openapi/runtime/authinfo_test.go index 6a7d94b1ad..0768df3b7c 100644 --- a/vendor/github.com/go-openapi/runtime/authinfo_test.go +++ b/vendor/github.com/go-openapi/runtime/authinfo_test.go @@ -23,11 +23,11 @@ import ( func TestAuthInfoWriter(t *testing.T) { hand := ClientAuthInfoWriterFunc(func(r ClientRequest, _ strfmt.Registry) error { - r.SetHeaderParam("authorization", "Bearer the-token-goes-here") - return nil + return r.SetHeaderParam("authorization", "Bearer the-token-goes-here") }) tr := new(trw) - hand.AuthenticateRequest(tr, nil) + err := hand.AuthenticateRequest(tr, nil) + assert.NoError(t, err) assert.Equal(t, "Bearer the-token-goes-here", tr.Headers.Get("Authorization")) } diff --git a/vendor/github.com/go-openapi/runtime/client/auth_info.go b/vendor/github.com/go-openapi/runtime/client/auth_info.go index 290a3eb623..9e18222b5e 100644 --- a/vendor/github.com/go-openapi/runtime/client/auth_info.go +++ b/vendor/github.com/go-openapi/runtime/client/auth_info.go @@ -32,8 +32,7 @@ func init() { func BasicAuth(username, password string) runtime.ClientAuthInfoWriter { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { encoded := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) - r.SetHeaderParam("Authorization", "Basic "+encoded) - return nil + return r.SetHeaderParam("Authorization", "Basic "+encoded) }) } @@ -41,15 +40,13 @@ func BasicAuth(username, password string) runtime.ClientAuthInfoWriter { func APIKeyAuth(name, in, value string) runtime.ClientAuthInfoWriter { if in == "query" { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { - r.SetQueryParam(name, value) - return nil + return r.SetQueryParam(name, value) }) } if in == "header" { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { - r.SetHeaderParam(name, value) - return nil + return r.SetHeaderParam(name, value) }) } return nil @@ -58,7 +55,6 @@ func APIKeyAuth(name, in, value string) runtime.ClientAuthInfoWriter { // BearerToken provides a header based oauth2 bearer access token auth info writer func BearerToken(token string) runtime.ClientAuthInfoWriter { return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error { - r.SetHeaderParam("Authorization", "Bearer "+token) - return nil + return r.SetHeaderParam("Authorization", "Bearer "+token) }) } diff --git a/vendor/github.com/go-openapi/runtime/client/auth_info_test.go b/vendor/github.com/go-openapi/runtime/client/auth_info_test.go index 70cf8466f5..ef88594437 100644 --- a/vendor/github.com/go-openapi/runtime/client/auth_info_test.go +++ b/vendor/github.com/go-openapi/runtime/client/auth_info_test.go @@ -25,7 +25,8 @@ func TestBasicAuth(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := BasicAuth("someone", "with a password") - writer.AuthenticateRequest(r, nil) + err := writer.AuthenticateRequest(r, nil) + assert.NoError(t, err) req := new(http.Request) req.Header = make(http.Header) @@ -41,7 +42,8 @@ func TestAPIKeyAuth_Query(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := APIKeyAuth("api_key", "query", "the-shared-key") - writer.AuthenticateRequest(r, nil) + err := writer.AuthenticateRequest(r, nil) + assert.NoError(t, err) assert.Equal(t, "the-shared-key", r.query.Get("api_key")) } @@ -50,7 +52,8 @@ func TestAPIKeyAuth_Header(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := APIKeyAuth("x-api-token", "header", "the-shared-key") - writer.AuthenticateRequest(r, nil) + err := writer.AuthenticateRequest(r, nil) + assert.NoError(t, err) assert.Equal(t, "the-shared-key", r.header.Get("x-api-token")) } @@ -59,7 +62,8 @@ func TestBearerTokenAuth(t *testing.T) { r, _ := newRequest("GET", "/", nil) writer := BearerToken("the-shared-token") - writer.AuthenticateRequest(r, nil) + err := writer.AuthenticateRequest(r, nil) + assert.NoError(t, err) assert.Equal(t, "Bearer the-shared-token", r.header.Get("Authorization")) } diff --git a/vendor/github.com/go-openapi/runtime/client/request_test.go b/vendor/github.com/go-openapi/runtime/client/request_test.go index 7f44095e86..0f1d602671 100644 --- a/vendor/github.com/go-openapi/runtime/client/request_test.go +++ b/vendor/github.com/go-openapi/runtime/client/request_test.go @@ -38,20 +38,20 @@ var testProducers = map[string]runtime.Producer{ func TestBuildRequest_SetHeaders(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/", nil) // single value - r.SetHeaderParam("X-Rate-Limit", "500") + _ = r.SetHeaderParam("X-Rate-Limit", "500") assert.Equal(t, "500", r.header.Get("X-Rate-Limit")) - r.SetHeaderParam("X-Rate-Limit", "400") + _ = r.SetHeaderParam("X-Rate-Limit", "400") assert.Equal(t, "400", r.header.Get("X-Rate-Limit")) // multi value - r.SetHeaderParam("X-Accepts", "json", "xml", "yaml") + _ = r.SetHeaderParam("X-Accepts", "json", "xml", "yaml") assert.EqualValues(t, []string{"json", "xml", "yaml"}, r.header["X-Accepts"]) } func TestBuildRequest_SetPath(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/?hello=world", nil) - r.SetPathParam("id", "1345") + _ = r.SetPathParam("id", "1345") assert.Equal(t, "1345", r.pathParams["id"]) } @@ -59,20 +59,20 @@ func TestBuildRequest_SetQuery(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/", nil) // single value - r.SetQueryParam("hello", "there") + _ = r.SetQueryParam("hello", "there") assert.Equal(t, "there", r.query.Get("hello")) // multi value - r.SetQueryParam("goodbye", "cruel", "world") + _ = r.SetQueryParam("goodbye", "cruel", "world") assert.Equal(t, []string{"cruel", "world"}, r.query["goodbye"]) } func TestBuildRequest_SetForm(t *testing.T) { // non-multipart r, _ := newRequest("POST", "/flats", nil) - r.SetFormParam("hello", "world") + _ = r.SetFormParam("hello", "world") assert.Equal(t, "world", r.formFields.Get("hello")) - r.SetFormParam("goodbye", "cruel", "world") + _ = r.SetFormParam("goodbye", "cruel", "world") assert.Equal(t, []string{"cruel", "world"}, r.formFields["goodbye"]) } @@ -107,16 +107,16 @@ func TestBuildRequest_SetBody(t *testing.T) { r, _ := newRequest("GET", "/flats/{id}/?hello=world", nil) bd := []struct{ Name, Hobby string }{{"Tom", "Organ trail"}, {"John", "Bird watching"}} - r.SetBodyParam(bd) + _ = r.SetBodyParam(bd) assert.Equal(t, bd, r.payload) } func TestBuildRequest_BuildHTTP_NoPayload(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetBodyParam(nil) - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetBodyParam(nil) + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("POST", "/flats/{id}/", reqWrtr) @@ -133,14 +133,14 @@ func TestBuildRequest_BuildHTTP_NoPayload(t *testing.T) { func TestBuildRequest_BuildHTTP_Payload(t *testing.T) { bd := []struct{ Name, Hobby string }{{"Tom", "Organ trail"}, {"John", "Bird watching"}} reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetBodyParam(bd) - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetBodyParam(bd) + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) - r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) + _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.BuildHTTP(runtime.JSONMime, testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { @@ -160,14 +160,14 @@ func TestBuildRequest_BuildHTTP_XMLPayload(t *testing.T) { Hobby string `xml:"hobby"` }{{xml.Name{}, "Tom", "Organ trail"}, {xml.Name{}, "John", "Bird watching"}} reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetBodyParam(bd) - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetBodyParam(bd) + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) - r.SetHeaderParam(runtime.HeaderContentType, runtime.XMLMime) + _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.XMLMime) req, err := r.BuildHTTP(runtime.XMLMime, testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { @@ -183,14 +183,14 @@ func TestBuildRequest_BuildHTTP_XMLPayload(t *testing.T) { func TestBuildRequest_BuildHTTP_TextPayload(t *testing.T) { bd := "Tom: Organ trail; John: Bird watching" reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetBodyParam(bd) - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetBodyParam(bd) + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) - r.SetHeaderParam(runtime.HeaderContentType, runtime.TextMime) + _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.TextMime) req, err := r.BuildHTTP(runtime.TextMime, testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { @@ -205,14 +205,14 @@ func TestBuildRequest_BuildHTTP_TextPayload(t *testing.T) { func TestBuildRequest_BuildHTTP_Form(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetFormParam("something", "some value") - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetFormParam("something", "some value") + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) - r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) + _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.BuildHTTP(runtime.JSONMime, testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { @@ -227,14 +227,14 @@ func TestBuildRequest_BuildHTTP_Form(t *testing.T) { func TestBuildRequest_BuildHTTP_Form_Content_Length(t *testing.T) { reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetFormParam("something", "some value") - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetFormParam("something", "some value") + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) - r.SetHeaderParam(runtime.HeaderContentType, runtime.MultipartFormMime) + _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.MultipartFormMime) req, err := r.BuildHTTP(runtime.JSONMime, testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { @@ -252,15 +252,15 @@ func TestBuildRequest_BuildHTTP_Form_Content_Length(t *testing.T) { func TestBuildRequest_BuildHTTP_Files(t *testing.T) { cont, _ := ioutil.ReadFile("./runtime.go") reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - req.SetFormParam("something", "some value") - req.SetFileParam("file", mustGetFile("./runtime.go")) - req.SetQueryParam("hello", "world") - req.SetPathParam("id", "1234") - req.SetHeaderParam("X-Rate-Limit", "200") + _ = req.SetFormParam("something", "some value") + _ = req.SetFileParam("file", mustGetFile("./runtime.go")) + _ = req.SetQueryParam("hello", "world") + _ = req.SetPathParam("id", "1234") + _ = req.SetHeaderParam("X-Rate-Limit", "200") return nil }) r, _ := newRequest("GET", "/flats/{id}/", reqWrtr) - r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) + _ = r.SetHeaderParam(runtime.HeaderContentType, runtime.JSONMime) req, err := r.BuildHTTP(runtime.JSONMime, testProducers, nil) if assert.NoError(t, err) && assert.NotNil(t, req) { assert.Equal(t, "200", req.Header.Get("x-rate-limit")) diff --git a/vendor/github.com/go-openapi/runtime/client/runtime.go b/vendor/github.com/go-openapi/runtime/client/runtime.go index 3fea025c92..6ec9cb6dc4 100644 --- a/vendor/github.com/go-openapi/runtime/client/runtime.go +++ b/vendor/github.com/go-openapi/runtime/client/runtime.go @@ -214,10 +214,10 @@ func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error } var accept []string - for _, mimeType := range operation.ProducesMediaTypes { - accept = append(accept, mimeType) + accept = append(accept, operation.ProducesMediaTypes...) + if err = request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil { + return nil, err } - request.SetHeaderParam(runtime.HeaderAccept, accept...) if auth == nil && r.DefaultAuthentication != nil { auth = r.DefaultAuthentication diff --git a/vendor/github.com/go-openapi/runtime/client/runtime_test.go b/vendor/github.com/go-openapi/runtime/client/runtime_test.go index 6572ad78f6..1d878f22f1 100644 --- a/vendor/github.com/go-openapi/runtime/client/runtime_test.go +++ b/vendor/github.com/go-openapi/runtime/client/runtime_test.go @@ -77,7 +77,7 @@ func TestRuntime_Concurrent(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode(result) + _ = jsongen.Encode(result) })) defer server.Close() @@ -153,7 +153,7 @@ func TestRuntime_Canary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode(result) + _ = jsongen.Encode(result) })) defer server.Close() @@ -204,7 +204,7 @@ func TestRuntime_XMLCanary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.XMLMime) rw.WriteHeader(http.StatusOK) xmlgen := xml.NewEncoder(rw) - xmlgen.Encode(result) + _ = xmlgen.Encode(result) })) defer server.Close() @@ -245,7 +245,7 @@ func TestRuntime_TextCanary(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Add(runtime.HeaderContentType, runtime.TextMime) rw.WriteHeader(http.StatusOK) - rw.Write([]byte(result)) + _, _ = rw.Write([]byte(result)) })) defer server.Close() @@ -305,7 +305,7 @@ func TestRuntime_CustomTransport(t *testing.T) { resp.Header.Set("content-type", "application/json") buf := bytes.NewBuffer(nil) enc := json.NewEncoder(buf) - enc.Encode(result) + _ = enc.Encode(result) resp.Body = ioutil.NopCloser(buf) return &resp, nil }) @@ -354,7 +354,7 @@ func TestRuntime_CustomCookieJar(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode([]task{}) + _ = jsongen.Encode([]task{}) } else { rw.WriteHeader(http.StatusUnauthorized) } @@ -407,7 +407,7 @@ func TestRuntime_AuthCanary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode(result) + _ = jsongen.Encode(result) })) defer server.Close() @@ -455,13 +455,12 @@ func TestRuntime_PickConsumer(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode(result) + _ = jsongen.Encode(result) })) defer server.Close() rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { - req.SetBodyParam(bytes.NewBufferString("hello")) - return nil + return req.SetBodyParam(bytes.NewBufferString("hello")) }) hu, _ := url.Parse(server.URL) @@ -509,7 +508,7 @@ func TestRuntime_ContentTypeCanary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode(result) + _ = jsongen.Encode(result) })) defer server.Close() @@ -563,7 +562,7 @@ func TestRuntime_ChunkedResponse(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+";charset=utf-8") rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - jsongen.Encode(result) + _ = jsongen.Encode(result) })) defer server.Close() @@ -605,26 +604,26 @@ func TestRuntime_DebugValue(t *testing.T) { original := os.Getenv("DEBUG") // Emtpy DEBUG means Debug is False - os.Setenv("DEBUG", "") + _ = os.Setenv("DEBUG", "") runtime := New("", "/", []string{"https"}) assert.False(t, runtime.Debug) // Non-Empty Debug means Debug is True - os.Setenv("DEBUG", "1") + _ = os.Setenv("DEBUG", "1") runtime = New("", "/", []string{"https"}) assert.True(t, runtime.Debug) - os.Setenv("DEBUG", "true") + _ = os.Setenv("DEBUG", "true") runtime = New("", "/", []string{"https"}) assert.True(t, runtime.Debug) - os.Setenv("DEBUG", "foo") + _ = os.Setenv("DEBUG", "foo") runtime = New("", "/", []string{"https"}) assert.True(t, runtime.Debug) // Make sure DEBUG is initial value once again - os.Setenv("DEBUG", original) + _ = os.Setenv("DEBUG", original) } func TestRuntime_OverrideScheme(t *testing.T) { diff --git a/vendor/github.com/go-openapi/runtime/client_request_test.go b/vendor/github.com/go-openapi/runtime/client_request_test.go index 5fc3a44605..ea93401d30 100644 --- a/vendor/github.com/go-openapi/runtime/client_request_test.go +++ b/vendor/github.com/go-openapi/runtime/client_request_test.go @@ -56,13 +56,13 @@ func (t *trw) SetTimeout(timeout time.Duration) error { func TestRequestWriterFunc(t *testing.T) { hand := ClientRequestWriterFunc(func(r ClientRequest, reg strfmt.Registry) error { - r.SetHeaderParam("blah", "blah blah") - r.SetBodyParam(struct{ Name string }{"Adriana"}) + _ = r.SetHeaderParam("blah", "blah blah") + _ = r.SetBodyParam(struct{ Name string }{"Adriana"}) return nil }) tr := new(trw) - hand.WriteToRequest(tr, nil) + _ = hand.WriteToRequest(tr, nil) assert.Equal(t, "blah blah", tr.Headers.Get("blah")) assert.Equal(t, "Adriana", tr.Body.(struct{ Name string }).Name) } diff --git a/vendor/github.com/go-openapi/runtime/client_response_test.go b/vendor/github.com/go-openapi/runtime/client_response_test.go index d3f5db4d1c..7422d349d5 100644 --- a/vendor/github.com/go-openapi/runtime/client_response_test.go +++ b/vendor/github.com/go-openapi/runtime/client_response_test.go @@ -52,7 +52,7 @@ func TestResponseReaderFunc(t *testing.T) { actual.Header = r.GetHeader("blah") return actual, nil }) - reader.ReadResponse(response{}, nil) + _, _ = reader.ReadResponse(response{}, nil) assert.Equal(t, "the content", actual.Body) assert.Equal(t, "the message", actual.Message) assert.Equal(t, "the header", actual.Header) diff --git a/vendor/github.com/go-openapi/runtime/interfaces.go b/vendor/github.com/go-openapi/runtime/interfaces.go index 85330aa3fc..5f2a472a83 100644 --- a/vendor/github.com/go-openapi/runtime/interfaces.go +++ b/vendor/github.com/go-openapi/runtime/interfaces.go @@ -15,8 +15,10 @@ package runtime import ( - "github.com/go-openapi/strfmt" "io" + "net/http" + + "github.com/go-openapi/strfmt" ) // OperationHandlerFunc an adapter for a function to the OperationHandler interface @@ -77,6 +79,21 @@ type Authenticator interface { Authenticate(interface{}) (bool, interface{}, error) } +// AuthorizerFunc turns a function into an authorizer +type AuthorizerFunc func(*http.Request, interface{}) error + +// Authorize authorizes the processing of the request for the principal +func (f AuthorizerFunc) Authorize(r *http.Request, principal interface{}) error { + return f(r, principal) +} + +// Authorizer represents an authorization strategy +// implementations of Authorizer know how to authorize the principal object +// using the request data and returns error if unauthorized +type Authorizer interface { + Authorize(*http.Request, interface{}) error +} + // Validatable types implementing this interface allow customizing their validation // this will be used instead of the reflective valditation based on the spec document. // the implementations are assumed to have been generated by the swagger tool so they should diff --git a/vendor/github.com/go-openapi/runtime/internal/testing/petstore/api.go b/vendor/github.com/go-openapi/runtime/internal/testing/petstore/api.go index e5bf19385c..2a066cee1b 100644 --- a/vendor/github.com/go-openapi/runtime/internal/testing/petstore/api.go +++ b/vendor/github.com/go-openapi/runtime/internal/testing/petstore/api.go @@ -15,7 +15,10 @@ package petstore import ( + goerrors "errors" "io" + "net/http" + "strings" gotest "testing" "github.com/go-openapi/errors" @@ -46,6 +49,8 @@ func NewAPI(t gotest.TB) (*loads.Document, *untyped.API) { api.RegisterAuth("basic", security.BasicAuth(func(username, password string) (interface{}, error) { if username == "admin" && password == "admin" { return "admin", nil + } else if username == "topuser" && password == "topuser" { + return "topuser", nil } return nil, errors.Unauthenticated("basic") })) @@ -55,6 +60,12 @@ func NewAPI(t gotest.TB) (*loads.Document, *untyped.API) { } return nil, errors.Unauthenticated("token") })) + api.RegisterAuthorizer(runtime.AuthorizerFunc(func(r *http.Request, user interface{}) error { + if r.Method == http.MethodPost && strings.HasPrefix(r.URL.Path, "/api/pets") && user.(string) != "admin" { + return goerrors.New("unauthorized") + } + return nil + })) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) api.RegisterOperation("post", "/pets", new(stubOperationHandler)) api.RegisterOperation("delete", "/pets/{id}", new(stubOperationHandler)) @@ -94,6 +105,7 @@ func NewRootAPI(t gotest.TB) (*loads.Document, *untyped.API) { } return nil, errors.Unauthenticated("token") })) + api.RegisterAuthorizer(security.Authorized()) api.RegisterOperation("get", "/pets", new(stubOperationHandler)) api.RegisterOperation("post", "/pets", new(stubOperationHandler)) api.RegisterOperation("delete", "/pets/{id}", new(stubOperationHandler)) diff --git a/vendor/github.com/go-openapi/runtime/middleware/body_test.go b/vendor/github.com/go-openapi/runtime/middleware/body_test.go index f78783a658..7e8ad0f932 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/body_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/body_test.go @@ -68,12 +68,12 @@ func TestBindRequest_DeleteNoBody(t *testing.T) { ri, rCtx, ok := ctx.RouteInfo(req) if assert.True(t, ok) { req = rCtx - err := ctx.BindValidRequest(req, ri, rbn(func(r *http.Request, rr *MatchedRoute) error { + bverr := ctx.BindValidRequest(req, ri, rbn(func(r *http.Request, rr *MatchedRoute) error { return nil })) - assert.NoError(t, err) - //assert.Equal(t, io.EOF, err) + assert.NoError(t, bverr) + //assert.Equal(t, io.EOF, bverr) } } diff --git a/vendor/github.com/go-openapi/runtime/middleware/context.go b/vendor/github.com/go-openapi/runtime/middleware/context.go index 8b472d9c02..c5c6c6efc8 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/context.go +++ b/vendor/github.com/go-openapi/runtime/middleware/context.go @@ -75,7 +75,6 @@ type Context struct { analyzer *analysis.Spec api RoutableAPI router Router - formats strfmt.Registry } type routableUntypedAPI struct { @@ -173,6 +172,9 @@ func (r *routableUntypedAPI) ProducersFor(mediaTypes []string) map[string]runtim func (r *routableUntypedAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { return r.api.AuthenticatorsFor(schemes) } +func (r *routableUntypedAPI) Authorizer() runtime.Authorizer { + return r.api.Authorizer() +} func (r *routableUntypedAPI) Formats() strfmt.Registry { return r.api.Formats() } @@ -225,12 +227,9 @@ const ( ctxContentType ctxResponseFormat ctxMatchedRoute - ctxAllowedMethods ctxBoundParams ctxSecurityPrincipal ctxSecurityScopes - - ctxConsumer ) type contentTypeValue struct { @@ -396,6 +395,11 @@ func (c *Context) Authorize(request *http.Request, route *MatchedRoute) (interfa } continue } + if route.Authorizer != nil { + if err := route.Authorizer.Authorize(request, usr); err != nil { + return nil, nil, errors.New(http.StatusForbidden, err.Error()) + } + } rCtx = stdContext.WithValue(rCtx, ctxSecurityPrincipal, usr) rCtx = stdContext.WithValue(rCtx, ctxSecurityScopes, route.Scopes[scheme]) return usr, request.WithContext(rCtx), nil @@ -542,7 +546,7 @@ func (c *Context) APIHandler(builder Builder) http.Handler { Title: title, } - return Spec("", c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(builder))) + return Spec("", c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b))) } // RoutesHandler returns a handler to serve the API, just the routes and the contract defined in the swagger spec diff --git a/vendor/github.com/go-openapi/runtime/middleware/context_test.go b/vendor/github.com/go-openapi/runtime/middleware/context_test.go index d02d3c4c38..e236ece893 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/context_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/context_test.go @@ -39,13 +39,6 @@ func (s *stubOperationHandler) Handle(params interface{}) (interface{}, error) { return nil, nil } -type testBinder struct { -} - -func (t *testBinder) BindRequest(r *http.Request, m *MatchedRoute) error { - return nil -} - func init() { loads.AddLoader(fmts.YAMLMatcher, fmts.YAMLDoc) } @@ -138,6 +131,32 @@ func TestContextAuthorize(t *testing.T) { assert.Equal(t, request, reqCtx) } +func TestContextAuthorize_WithAuthorizer(t *testing.T) { + spec, api := petstore.NewAPI(t) + ctx := NewContext(spec, api, nil) + ctx.router = DefaultRouter(spec, ctx.api) + + request, _ := runtime.JSONRequest("POST", "/api/pets", nil) + + ri, reqWithCtx, ok := ctx.RouteInfo(request) + assert.True(t, ok) + assert.NotNil(t, reqWithCtx) + + request = reqWithCtx + + request.SetBasicAuth("topuser", "topuser") + p, reqWithCtx, err := ctx.Authorize(request, ri) + assert.Error(t, err) + assert.Nil(t, p) + assert.Nil(t, reqWithCtx) + + request.SetBasicAuth("admin", "admin") + p, reqWithCtx, err = ctx.Authorize(request, ri) + assert.NoError(t, err) + assert.Equal(t, "admin", p) + assert.NotNil(t, reqWithCtx) +} + func TestContextNegotiateContentType(t *testing.T) { spec, api := petstore.NewAPI(t) ctx := NewContext(spec, api, nil) @@ -240,6 +259,7 @@ func TestContextRender(t *testing.T) { ri, request, _ = ctx.RouteInfo(request) ctx.Respond(recorder, request, ri.Produces, ri, nil) assert.Equal(t, 204, recorder.Code) + } func TestContextValidResponseFormat(t *testing.T) { @@ -266,7 +286,7 @@ func TestContextValidResponseFormat(t *testing.T) { assert.Equal(t, ct, cached) // check if the cast works and fetch from cache too - mt, request = ctx.ResponseFormat(request, []string{ct}) + mt, _ = ctx.ResponseFormat(request, []string{ct}) assert.Equal(t, ct, mt) } diff --git a/vendor/github.com/go-openapi/runtime/middleware/go18.go b/vendor/github.com/go-openapi/runtime/middleware/go18.go new file mode 100644 index 0000000000..75c762c094 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/go18.go @@ -0,0 +1,9 @@ +// +build go1.8 + +package middleware + +import "net/url" + +func pathUnescape(path string) (string, error) { + return url.PathUnescape(path) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/header/header.go b/vendor/github.com/go-openapi/runtime/middleware/header/header.go index 4bfde8b86f..3e342258bc 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/header/header.go +++ b/vendor/github.com/go-openapi/runtime/middleware/header/header.go @@ -46,8 +46,8 @@ func init() { var t octetType isCtl := c <= 31 || c == 127 isChar := 0 <= c && c <= 127 - isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 - if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { + isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) + if strings.ContainsRune(" \t\r\n", rune(c)) { t |= isSpace } if isChar && !isCtl && !isSeparator { @@ -167,11 +167,13 @@ func parseValueAndParams(s string) (value string, params map[string]string) { return } +// AcceptSpec ... type AcceptSpec struct { Value string Q float64 } +// ParseAccept2 ... func ParseAccept2(header http.Header, key string) (specs []AcceptSpec) { for _, en := range ParseList(header, key) { v, p := parseValueAndParams(en) diff --git a/vendor/github.com/go-openapi/runtime/middleware/parameter.go b/vendor/github.com/go-openapi/runtime/middleware/parameter.go index 9eb39c82ea..8975b6e1c8 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/parameter.go +++ b/vendor/github.com/go-openapi/runtime/middleware/parameter.go @@ -193,28 +193,28 @@ func (p *untypedParamBinder) Bind(request *http.Request, routeParams RouteParams } if mt == "multipart/form-data" { - if err := request.ParseMultipartForm(defaultMaxMemory); err != nil { + if err = request.ParseMultipartForm(defaultMaxMemory); err != nil { return errors.NewParseError(p.Name, p.parameter.In, "", err) } } - if err := request.ParseForm(); err != nil { + if err = request.ParseForm(); err != nil { return errors.NewParseError(p.Name, p.parameter.In, "", err) } if p.parameter.Type == "file" { - file, header, err := request.FormFile(p.parameter.Name) - if err != nil { - return errors.NewParseError(p.Name, p.parameter.In, "", err) + file, header, ffErr := request.FormFile(p.parameter.Name) + if ffErr != nil { + return errors.NewParseError(p.Name, p.parameter.In, "", ffErr) } target.Set(reflect.ValueOf(runtime.File{Data: file, Header: header})) return nil } if request.MultipartForm != nil { - data, custom, hasKey, err := p.readValue(runtime.Values(request.MultipartForm.Value), target) - if err != nil { - return err + data, custom, hasKey, rvErr := p.readValue(runtime.Values(request.MultipartForm.Value), target) + if rvErr != nil { + return rvErr } if custom { return nil diff --git a/vendor/github.com/go-openapi/runtime/middleware/parameter_test.go b/vendor/github.com/go-openapi/runtime/middleware/parameter_test.go index 8cba121451..d0524ab9ad 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/parameter_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/parameter_test.go @@ -28,9 +28,9 @@ import ( "github.com/stretchr/testify/assert" ) -type email struct { - Address string -} +// type email struct { +// Address string +// } type paramFactory func(string) *spec.Parameter diff --git a/vendor/github.com/go-openapi/runtime/middleware/pre_go18.go b/vendor/github.com/go-openapi/runtime/middleware/pre_go18.go new file mode 100644 index 0000000000..03385251e1 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/pre_go18.go @@ -0,0 +1,9 @@ +// +build !go1.8 + +package middleware + +import "net/url" + +func pathUnescape(path string) (string, error) { + return url.QueryUnescape(path) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/redoc.go b/vendor/github.com/go-openapi/runtime/middleware/redoc.go index 23ce367e65..21277948c0 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/redoc.go +++ b/vendor/github.com/go-openapi/runtime/middleware/redoc.go @@ -51,7 +51,7 @@ func Redoc(opts RedocOpts, next http.Handler) http.Handler { tmpl := template.Must(template.New("redoc").Parse(redocTemplate)) buf := bytes.NewBuffer(nil) - tmpl.Execute(buf, opts) + _ = tmpl.Execute(buf, opts) b := buf.Bytes() return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { @@ -59,14 +59,14 @@ func Redoc(opts RedocOpts, next http.Handler) http.Handler { rw.Header().Set("Content-Type", "text/html; charset=utf-8") rw.WriteHeader(http.StatusOK) - rw.Write(b) + _, _ = rw.Write(b) return } if next == nil { rw.Header().Set("Content-Type", "text/plain") rw.WriteHeader(http.StatusNotFound) - rw.Write([]byte(fmt.Sprintf("%q not found", pth))) + _, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth))) return } next.ServeHTTP(rw, r) diff --git a/vendor/github.com/go-openapi/runtime/middleware/request.go b/vendor/github.com/go-openapi/runtime/middleware/request.go index 6171ca71b9..ee725f587a 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/request.go +++ b/vendor/github.com/go-openapi/runtime/middleware/request.go @@ -54,7 +54,7 @@ func (o *untypedRequestBinder) Bind(request *http.Request, routeParams RoutePara debugLog("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath()) for fieldName, param := range o.Parameters { binder := o.paramBinders[fieldName] - debugLog("binding paramter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath()) + debugLog("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath()) var target reflect.Value if !isMap { binder.Name = fieldName diff --git a/vendor/github.com/go-openapi/runtime/middleware/request_test.go b/vendor/github.com/go-openapi/runtime/middleware/request_test.go index 091bad07d5..395065b808 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/request_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/request_test.go @@ -67,21 +67,6 @@ type jsonRequestSlice struct { Friend []friend } -type jsonRequestAllTypes struct { - Confirmed bool - Planned strfmt.Date - Delivered strfmt.DateTime - Age int32 - ID int64 - Score float32 - Factor float64 - Friend friend - Name string - Tags []string - Picture []byte - RequestID int64 -} - func parametersForAllTypes(fmt string) map[string]spec.Parameter { if fmt == "" { fmt = "csv" @@ -299,7 +284,7 @@ func TestRequestBindingForValid(t *testing.T) { binder := newUntypedRequestBinder(op1, new(spec.Swagger), strfmt.Default) lval := []string{"one", "two", "three"} - queryString := "" + var queryString string switch fmt { case "multi": queryString = strings.Join(lval, "&tags=") @@ -428,8 +413,8 @@ func TestBindingFileUpload(t *testing.T) { part, err := writer.CreateFormFile("file", "plain-jane.txt") assert.NoError(t, err) - part.Write([]byte("the file contents")) - writer.WriteField("name", "the-name") + _, _ = part.Write([]byte("the file contents")) + _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) urlStr := "http://localhost:8002/hello" @@ -462,8 +447,8 @@ func TestBindingFileUpload(t *testing.T) { part, err = writer.CreateFormFile("bad-name", "plain-jane.txt") assert.NoError(t, err) - part.Write([]byte("the file contents")) - writer.WriteField("name", "the-name") + _, _ = part.Write([]byte("the file contents")) + _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -473,7 +458,7 @@ func TestBindingFileUpload(t *testing.T) { req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) - req.MultipartReader() + _, _ = req.MultipartReader() data = fileRequest{} assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) diff --git a/vendor/github.com/go-openapi/runtime/middleware/router.go b/vendor/github.com/go-openapi/runtime/middleware/router.go index da447bbd67..6ae4905086 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/router.go +++ b/vendor/github.com/go-openapi/runtime/middleware/router.go @@ -16,7 +16,6 @@ package middleware import ( "net/http" - "net/url" fpath "path" "regexp" "strings" @@ -93,6 +92,7 @@ type RoutableAPI interface { ConsumersFor([]string) map[string]runtime.Consumer ProducersFor([]string) map[string]runtime.Producer AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator + Authorizer() runtime.Authorizer Formats() strfmt.Registry DefaultProduces() string DefaultConsumes() string @@ -113,7 +113,6 @@ type defaultRouteBuilder struct { type defaultRouter struct { spec *loads.Document - api RoutableAPI routers map[string]*denco.Router } @@ -154,6 +153,7 @@ type routeEntry struct { Formats strfmt.Registry Binder *untypedRequestBinder Authenticators map[string]runtime.Authenticator + Authorizer runtime.Authorizer Scopes map[string][]string } @@ -182,7 +182,7 @@ func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) { debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters)) var params RouteParams for _, p := range rp { - v, err := url.PathUnescape(p.Value) + v, err := pathUnescape(p.Value) if err != nil { debugLog("failed to escape %q: %v", p.Value, err) v = p.Value @@ -249,6 +249,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper Formats: d.api.Formats(), Binder: newUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()), Authenticators: d.api.AuthenticatorsFor(definitions), + Authorizer: d.api.Authorizer(), Scopes: scopes, }) d.records[mn] = append(d.records[mn], record) @@ -259,7 +260,7 @@ func (d *defaultRouteBuilder) Build() *defaultRouter { routers := make(map[string]*denco.Router) for method, records := range d.records { router := denco.New() - router.Build(records) + _ = router.Build(records) routers[method] = router } return &defaultRouter{ diff --git a/vendor/github.com/go-openapi/runtime/middleware/security.go b/vendor/github.com/go-openapi/runtime/middleware/security.go index 1e21a81af2..399058310a 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/security.go +++ b/vendor/github.com/go-openapi/runtime/middleware/security.go @@ -27,12 +27,12 @@ func newSecureAPI(ctx *Context, next http.Handler) http.Handler { return } - if _, rCtx, err := ctx.Authorize(r, route); err != nil { + _, rCtx, err := ctx.Authorize(r, route) + if err != nil { ctx.Respond(rw, r, route.Produces, route, err) return - } else { - r = rCtx } + r = rCtx next.ServeHTTP(rw, r) }) diff --git a/vendor/github.com/go-openapi/runtime/middleware/spec.go b/vendor/github.com/go-openapi/runtime/middleware/spec.go index 8a09fb75f7..72bc5643ad 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/spec.go +++ b/vendor/github.com/go-openapi/runtime/middleware/spec.go @@ -33,7 +33,7 @@ func Spec(basePath string, b []byte, next http.Handler) http.Handler { if r.URL.Path == pth { rw.Header().Set("Content-Type", "application/json") rw.WriteHeader(http.StatusOK) - rw.Write(b) + _, _ = rw.Write(b) return } diff --git a/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go b/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go index 3e7b37db8f..3b0cd4e285 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go +++ b/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go @@ -57,6 +57,7 @@ type API struct { consumers map[string]runtime.Consumer producers map[string]runtime.Producer authenticators map[string]runtime.Authenticator + authorizer runtime.Authorizer operations map[string]map[string]runtime.OperationHandler ServeError func(http.ResponseWriter, *http.Request, error) Models map[string]func() interface{} @@ -105,6 +106,11 @@ func (d *API) RegisterAuth(scheme string, handler runtime.Authenticator) { d.authenticators[scheme] = handler } +// RegisterAuthorizer registers an authorizer handler in this api +func (d *API) RegisterAuthorizer(handler runtime.Authorizer) { + d.authorizer = handler +} + // RegisterConsumer registers a consumer for a media type. func (d *API) RegisterConsumer(mediaType string, handler runtime.Consumer) { if d.consumers == nil { @@ -178,6 +184,11 @@ func (d *API) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[stri return result } +// AuthorizersFor returns the registered authorizer +func (d *API) Authorizer() runtime.Authorizer { + return d.authorizer +} + // Validate validates this API for any missing items func (d *API) Validate() error { return d.validate() diff --git a/vendor/github.com/go-openapi/runtime/middleware/untyped/api_test.go b/vendor/github.com/go-openapi/runtime/middleware/untyped/api_test.go index 580fb6215e..551bec71f8 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/untyped/api_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/untyped/api_test.go @@ -16,6 +16,7 @@ package untyped import ( "io" + "net/http" "sort" "testing" @@ -31,6 +32,10 @@ func stubAutenticator() runtime.Authenticator { return runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) { return false, nil, nil }) } +func stubAuthorizer() runtime.Authorizer { + return runtime.AuthorizerFunc(func(_ *http.Request, _ interface{}) error { return nil }) +} + type stubConsumer struct { } @@ -63,7 +68,9 @@ func TestUntypedAPIRegistrations(t *testing.T) { api.RegisterProducer("application/yada-2", new(stubProducer)) api.RegisterOperation("get", "/{someId}", new(stubOperationHandler)) api.RegisterAuth("basic", stubAutenticator()) + api.RegisterAuthorizer(stubAuthorizer()) + assert.NotNil(t, api.authorizer) assert.NotEmpty(t, api.authenticators) _, ok := api.authenticators["basic"] @@ -79,6 +86,9 @@ func TestUntypedAPIRegistrations(t *testing.T) { _, ok = api.operations["GET"]["/{someId}"] assert.True(t, ok) + authorizer := api.Authorizer() + assert.NotNil(t, authorizer) + h, ok := api.OperationHandlerFor("get", "/{someId}") assert.True(t, ok) assert.NotNil(t, h) diff --git a/vendor/github.com/go-openapi/runtime/middleware/untyped_request_test.go b/vendor/github.com/go-openapi/runtime/middleware/untyped_request_test.go index bc2af1f95a..5c226f62ea 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/untyped_request_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/untyped_request_test.go @@ -59,8 +59,8 @@ func TestUntypedFileUpload(t *testing.T) { part, err := writer.CreateFormFile("file", "plain-jane.txt") assert.NoError(t, err) - part.Write([]byte("the file contents")) - writer.WriteField("name", "the-name") + _, _ = part.Write([]byte("the file contents")) + _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) urlStr := "http://localhost:8002/hello" @@ -95,8 +95,8 @@ func TestUntypedFileUpload(t *testing.T) { part, err = writer.CreateFormFile("bad-name", "plain-jane.txt") assert.NoError(t, err) - part.Write([]byte("the file contents")) - writer.WriteField("name", "the-name") + _, _ = part.Write([]byte("the file contents")) + _ = writer.WriteField("name", "the-name") assert.NoError(t, writer.Close()) req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -106,7 +106,7 @@ func TestUntypedFileUpload(t *testing.T) { req, _ = http.NewRequest("POST", urlStr, body) req.Header.Set("Content-Type", writer.FormDataContentType()) - req.MultipartReader() + _, _ = req.MultipartReader() data = make(map[string]interface{}) assert.Error(t, binder.Bind(req, nil, runtime.JSONConsumer(), &data)) diff --git a/vendor/github.com/go-openapi/runtime/middleware/validation.go b/vendor/github.com/go-openapi/runtime/middleware/validation.go index 8d6686f5d0..bb8df3cb3d 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/validation.go +++ b/vendor/github.com/go-openapi/runtime/middleware/validation.go @@ -24,30 +24,6 @@ import ( "github.com/go-openapi/swag" ) -// NewValidation starts a new validation middleware -func newValidation(ctx *Context, next http.Handler) http.Handler { - - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - matched, rCtx, _ := ctx.RouteInfo(r) - if rCtx != nil { - r = rCtx - } - if matched == nil { - ctx.NotFound(rw, r) - return - } - _, r, result := ctx.BindAndValidate(r, matched) - - if result != nil { - ctx.Respond(rw, r, matched.Produces, matched, result) - return - } - - debugLog("no result for %s %s", r.Method, r.URL.EscapedPath()) - next.ServeHTTP(rw, r) - }) -} - type validation struct { context *Context result []error @@ -56,15 +32,6 @@ type validation struct { bound map[string]interface{} } -type untypedBinder map[string]interface{} - -func (ub untypedBinder) BindRequest(r *http.Request, route *MatchedRoute, consumer runtime.Consumer) error { - if err := route.Binder.Bind(r, route.Params, consumer, ub); err != nil { - return err - } - return nil -} - // ContentType validates the content type of a request func validateContentType(allowed []string, actual string) error { debugLog("validating content type for %q against [%s]", actual, strings.Join(allowed, ", ")) diff --git a/vendor/github.com/go-openapi/runtime/middleware/validation_test.go b/vendor/github.com/go-openapi/runtime/middleware/validation_test.go index 2175e77318..10ec57879d 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/validation_test.go +++ b/vendor/github.com/go-openapi/runtime/middleware/validation_test.go @@ -26,11 +26,33 @@ import ( "github.com/stretchr/testify/assert" ) +func newTestValidation(ctx *Context, next http.Handler) http.Handler { + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + matched, rCtx, _ := ctx.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + if matched == nil { + ctx.NotFound(rw, r) + return + } + _, r, result := ctx.BindAndValidate(r, matched) + + if result != nil { + ctx.Respond(rw, r, matched.Produces, matched, result) + return + } + + next.ServeHTTP(rw, r) + }) +} + func TestContentTypeValidation(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) - mw := newValidation(context, http.HandlerFunc(terminator)) + mw := newTestValidation(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/api/pets", nil) @@ -41,6 +63,7 @@ func TestContentTypeValidation(t *testing.T) { recorder = httptest.NewRecorder() request, _ = http.NewRequest("POST", "/api/pets", nil) request.Header.Add("content-type", "application(") + request.Header.Add("Accept", "application/json") request.ContentLength = 1 mw.ServeHTTP(recorder, request) @@ -81,7 +104,7 @@ func TestResponseFormatValidation(t *testing.T) { spec, api := petstore.NewAPI(t) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) - mw := newValidation(context, http.HandlerFunc(terminator)) + mw := newTestValidation(context, http.HandlerFunc(terminator)) recorder := httptest.NewRecorder() request, _ := http.NewRequest("POST", "/api/pets", bytes.NewBuffer([]byte(`name: Dog`))) diff --git a/vendor/github.com/go-openapi/runtime/security/authorizer.go b/vendor/github.com/go-openapi/runtime/security/authorizer.go new file mode 100644 index 0000000000..00c1a4d6a4 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/security/authorizer.go @@ -0,0 +1,27 @@ +// Copyright 2015 go-swagger maintainers +// +// 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. + +package security + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// Authorized provides a default implementation of the Authorizer interface where all +// requests are authorized (successful) +func Authorized() runtime.Authorizer { + return runtime.AuthorizerFunc(func(_ *http.Request, _ interface{}) error { return nil }) +} diff --git a/vendor/github.com/go-openapi/runtime/security/authorizer_test.go b/vendor/github.com/go-openapi/runtime/security/authorizer_test.go new file mode 100644 index 0000000000..7f1f9b053d --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/security/authorizer_test.go @@ -0,0 +1,28 @@ +// Copyright 2015 go-swagger maintainers +// +// 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. + +package security + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAuthorized(t *testing.T) { + authorizer := Authorized() + + err := authorizer.Authorize(nil, nil) + assert.NoError(t, err) +} diff --git a/vendor/github.com/go-openapi/runtime/security/bearer_auth_test.go b/vendor/github.com/go-openapi/runtime/security/bearer_auth_test.go index 0e577175ac..899dc73d9d 100644 --- a/vendor/github.com/go-openapi/runtime/security/bearer_auth_test.go +++ b/vendor/github.com/go-openapi/runtime/security/bearer_auth_test.go @@ -49,7 +49,7 @@ func TestValidBearerAuth(t *testing.T) { mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) - writer.WriteField("access_token", "token123") + _ = writer.WriteField("access_token", "token123") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4.Header.Set("Content-Type", writer.FormDataContentType()) @@ -90,7 +90,7 @@ func TestInvalidBearerAuth(t *testing.T) { mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) - writer.WriteField("access_token", "token124") + _ = writer.WriteField("access_token", "token124") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4.Header.Set("Content-Type", writer.FormDataContentType()) @@ -131,7 +131,7 @@ func TestMissingBearerAuth(t *testing.T) { mpbody := bytes.NewBuffer(nil) writer := multipart.NewWriter(mpbody) - writer.WriteField("access_toke", "token123") + _ = writer.WriteField("access_toke", "token123") writer.Close() req4, _ := http.NewRequest("POST", "/blah", mpbody) req4.Header.Set("Content-Type", writer.FormDataContentType()) diff --git a/vendor/github.com/go-openapi/spec/expander.go b/vendor/github.com/go-openapi/spec/expander.go index c694bbcb30..b4429a21c8 100644 --- a/vendor/github.com/go-openapi/spec/expander.go +++ b/vendor/github.com/go-openapi/spec/expander.go @@ -36,8 +36,9 @@ var ( // ExpandOptions provides options for expand. type ExpandOptions struct { - RelativeBase string - SkipSchemas bool + RelativeBase string + SkipSchemas bool + ContinueOnError bool } // ResolutionCache a cache for resolving urls @@ -510,7 +511,8 @@ func (r *schemaLoader) reset() { // ExpandSpec expands the references in a swagger spec func ExpandSpec(spec *Swagger, options *ExpandOptions) error { resolver, err := defaultSchemaLoader(spec, nil, options, nil) - if err != nil { + // Just in case this ever returns an error. + if shouldStopOnError(err, resolver.options) { return err } @@ -518,7 +520,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error { for key, definition := range spec.Definitions { var def *Schema var err error - if def, err = expandSchema(definition, []string{"#/definitions/" + key}, resolver); err != nil { + if def, err = expandSchema(definition, []string{"#/definitions/" + key}, resolver); shouldStopOnError(err, resolver.options) { return err } resolver.reset() @@ -527,14 +529,14 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error { } for key, parameter := range spec.Parameters { - if err := expandParameter(¶meter, resolver); err != nil { + if err := expandParameter(¶meter, resolver); shouldStopOnError(err, resolver.options) { return err } spec.Parameters[key] = parameter } for key, response := range spec.Responses { - if err := expandResponse(&response, resolver); err != nil { + if err := expandResponse(&response, resolver); shouldStopOnError(err, resolver.options) { return err } spec.Responses[key] = response @@ -542,7 +544,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error { if spec.Paths != nil { for key, path := range spec.Paths.Paths { - if err := expandPathItem(&path, resolver); err != nil { + if err := expandPathItem(&path, resolver); shouldStopOnError(err, resolver.options) { return err } spec.Paths.Paths[key] = path @@ -552,6 +554,18 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error { return nil } +func shouldStopOnError(err error, opts *ExpandOptions) bool { + if err != nil && !opts.ContinueOnError { + return true + } + + if err != nil { + log.Println(err) + } + + return false +} + // ExpandSchema expands the refs in the schema object func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error { return ExpandSchemaWithBasePath(schema, root, cache, nil) @@ -639,7 +653,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader) (* return &target, nil } - if err := resolver.Resolve(&target.Ref, &t); err != nil { + if err := resolver.Resolve(&target.Ref, &t); shouldStopOnError(err, resolver.options) { return &target, err } @@ -648,86 +662,108 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader) (* return &target, nil } parentRefs = append(parentRefs, target.Ref.String()) - target = *t + if t != nil { + target = *t + } } t, err := expandItems(target, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - target = *t + if t != nil { + target = *t + } for i := range target.AllOf { t, err := expandSchema(target.AllOf[i], parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - target.AllOf[i] = *t + if t != nil { + target.AllOf[i] = *t + } } for i := range target.AnyOf { t, err := expandSchema(target.AnyOf[i], parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } target.AnyOf[i] = *t } for i := range target.OneOf { t, err := expandSchema(target.OneOf[i], parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - target.OneOf[i] = *t + if t != nil { + target.OneOf[i] = *t + } } if target.Not != nil { t, err := expandSchema(*target.Not, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - *target.Not = *t + if t != nil { + *target.Not = *t + } } for k := range target.Properties { t, err := expandSchema(target.Properties[k], parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - target.Properties[k] = *t + if t != nil { + target.Properties[k] = *t + } } if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil { t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - *target.AdditionalProperties.Schema = *t + if t != nil { + *target.AdditionalProperties.Schema = *t + } } for k := range target.PatternProperties { t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - target.PatternProperties[k] = *t + if t != nil { + target.PatternProperties[k] = *t + } } for k := range target.Dependencies { if target.Dependencies[k].Schema != nil { t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - *target.Dependencies[k].Schema = *t + if t != nil { + *target.Dependencies[k].Schema = *t + } } } if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil { t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - *target.AdditionalItems.Schema = *t + if t != nil { + *target.AdditionalItems.Schema = *t + } } for k := range target.Definitions { t, err := expandSchema(target.Definitions[k], parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return &target, err } - target.Definitions[k] = *t + if t != nil { + target.Definitions[k] = *t + } } return &target, nil } @@ -736,6 +772,7 @@ func expandPathItem(pathItem *PathItem, resolver *schemaLoader) error { if pathItem == nil { return nil } + if pathItem.Ref.String() != "" { if err := resolver.Resolve(&pathItem.Ref, &pathItem); err != nil { return err @@ -745,29 +782,29 @@ func expandPathItem(pathItem *PathItem, resolver *schemaLoader) error { } for idx := range pathItem.Parameters { - if err := expandParameter(&(pathItem.Parameters[idx]), resolver); err != nil { + if err := expandParameter(&(pathItem.Parameters[idx]), resolver); shouldStopOnError(err, resolver.options) { return err } } - if err := expandOperation(pathItem.Get, resolver); err != nil { + if err := expandOperation(pathItem.Get, resolver); shouldStopOnError(err, resolver.options) { return err } - if err := expandOperation(pathItem.Head, resolver); err != nil { + if err := expandOperation(pathItem.Head, resolver); shouldStopOnError(err, resolver.options) { return err } - if err := expandOperation(pathItem.Options, resolver); err != nil { + if err := expandOperation(pathItem.Options, resolver); shouldStopOnError(err, resolver.options) { return err } - if err := expandOperation(pathItem.Put, resolver); err != nil { + if err := expandOperation(pathItem.Put, resolver); shouldStopOnError(err, resolver.options) { return err } - if err := expandOperation(pathItem.Post, resolver); err != nil { + if err := expandOperation(pathItem.Post, resolver); shouldStopOnError(err, resolver.options) { return err } - if err := expandOperation(pathItem.Patch, resolver); err != nil { + if err := expandOperation(pathItem.Patch, resolver); shouldStopOnError(err, resolver.options) { return err } - if err := expandOperation(pathItem.Delete, resolver); err != nil { + if err := expandOperation(pathItem.Delete, resolver); shouldStopOnError(err, resolver.options) { return err } return nil @@ -777,8 +814,9 @@ func expandOperation(op *Operation, resolver *schemaLoader) error { if op == nil { return nil } + for i, param := range op.Parameters { - if err := expandParameter(¶m, resolver); err != nil { + if err := expandParameter(¶m, resolver); shouldStopOnError(err, resolver.options) { return err } op.Parameters[i] = param @@ -786,11 +824,11 @@ func expandOperation(op *Operation, resolver *schemaLoader) error { if op.Responses != nil { responses := op.Responses - if err := expandResponse(responses.Default, resolver); err != nil { + if err := expandResponse(responses.Default, resolver); shouldStopOnError(err, resolver.options) { return err } for code, response := range responses.StatusCodeResponses { - if err := expandResponse(&response, resolver); err != nil { + if err := expandResponse(&response, resolver); shouldStopOnError(err, resolver.options) { return err } responses.StatusCodeResponses[code] = response @@ -805,9 +843,10 @@ func expandResponse(response *Response, resolver *schemaLoader) error { } var parentRefs []string + if response.Ref.String() != "" { parentRefs = append(parentRefs, response.Ref.String()) - if err := resolver.Resolve(&response.Ref, response); err != nil { + if err := resolver.Resolve(&response.Ref, response); shouldStopOnError(err, resolver.options) { return err } resolver.reset() @@ -817,11 +856,11 @@ func expandResponse(response *Response, resolver *schemaLoader) error { if !resolver.options.SkipSchemas && response.Schema != nil { parentRefs = append(parentRefs, response.Schema.Ref.String()) debugLog("response ref: %s", response.Schema.Ref) - if err := resolver.Resolve(&response.Schema.Ref, &response.Schema); err != nil { + if err := resolver.Resolve(&response.Schema.Ref, &response.Schema); shouldStopOnError(err, resolver.options) { return err } s, err := expandSchema(*response.Schema, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return err } resolver.reset() @@ -836,9 +875,10 @@ func expandParameter(parameter *Parameter, resolver *schemaLoader) error { } var parentRefs []string + if parameter.Ref.String() != "" { parentRefs = append(parentRefs, parameter.Ref.String()) - if err := resolver.Resolve(¶meter.Ref, parameter); err != nil { + if err := resolver.Resolve(¶meter.Ref, parameter); shouldStopOnError(err, resolver.options) { return err } resolver.reset() @@ -846,11 +886,11 @@ func expandParameter(parameter *Parameter, resolver *schemaLoader) error { } if !resolver.options.SkipSchemas && parameter.Schema != nil { parentRefs = append(parentRefs, parameter.Schema.Ref.String()) - if err := resolver.Resolve(¶meter.Schema.Ref, ¶meter.Schema); err != nil { + if err := resolver.Resolve(¶meter.Schema.Ref, ¶meter.Schema); shouldStopOnError(err, resolver.options) { return err } s, err := expandSchema(*parameter.Schema, parentRefs, resolver) - if err != nil { + if shouldStopOnError(err, resolver.options) { return err } resolver.reset() diff --git a/vendor/github.com/go-openapi/spec/expander_test.go b/vendor/github.com/go-openapi/spec/expander_test.go index 2cbffcab67..09c8573cfa 100644 --- a/vendor/github.com/go-openapi/spec/expander_test.go +++ b/vendor/github.com/go-openapi/spec/expander_test.go @@ -197,6 +197,35 @@ func TestCircularRefsExpansion(t *testing.T) { }, "Calling expand schema with circular refs, should not panic!") } +func TestContinueOnErrorExpansion(t *testing.T) { + missingRefDoc, err := jsonDoc("fixtures/expansion/missingRef.json") + assert.NoError(t, err) + + testCase := struct { + Input *Swagger `json:"input"` + Expected *Swagger `json:"expected"` + }{} + err = json.Unmarshal(missingRefDoc, &testCase) + assert.NoError(t, err) + + opts := &ExpandOptions{ + ContinueOnError: true, + } + err = ExpandSpec(testCase.Input, opts) + assert.NoError(t, err) + assert.Equal(t, testCase.Input, testCase.Expected, "Should continue expanding spec when a definition can't be found.") + + doc, err := jsonDoc("fixtures/expansion/missingItemRef.json") + spec := new(Swagger) + err = json.Unmarshal(doc, spec) + assert.NoError(t, err) + + assert.NotPanics(t, func() { + err = ExpandSpec(spec, opts) + assert.NoError(t, err) + }, "Array of missing refs should not cause a panic, and continue to expand spec.") +} + func TestIssue415(t *testing.T) { doc, err := jsonDoc("fixtures/expansion/clickmeter.json") assert.NoError(t, err) diff --git a/vendor/github.com/go-openapi/spec/fixtures/expansion/missingItemRef.json b/vendor/github.com/go-openapi/spec/fixtures/expansion/missingItemRef.json new file mode 100644 index 0000000000..f3908d52f0 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/fixtures/expansion/missingItemRef.json @@ -0,0 +1,31 @@ +{ + "swagger": "2.0", + "info": { + "version": "2.1.0", + "title": "Missing Item API" + }, + "host": "item.com", + "basePath": "/missing/ref", + "schemes": [ + "http" + ], + "paths": { + "/employees": { + "get": { + "operationId": "LIST-Employees", + "summary": "List Employee Types", + "responses": { + "200": { + "description": "", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/employees-output" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/vendor/github.com/go-openapi/spec/fixtures/expansion/missingRef.json b/vendor/github.com/go-openapi/spec/fixtures/expansion/missingRef.json new file mode 100644 index 0000000000..d59794696a --- /dev/null +++ b/vendor/github.com/go-openapi/spec/fixtures/expansion/missingRef.json @@ -0,0 +1,165 @@ +{ + "input": { + "swagger": "2.0", + "info": { + "version": "1.0", + "title": "Continue On Error" + }, + "paths": { + "/todos": { + "get": { + "responses": { + "200": { + "description": "List Todos", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/todo-full" + } + } + }, + "404": { + "$ref": "#/responses/404" + } + } + } + } + }, + "definitions": { + "todo-partial": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "completed": { + "type": "boolean" + } + } + }, + "todo-full": { + "allOf": [ + { + "$ref": "#/definitions/todo-partial" + }, + { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "completed_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + } + ] + } + } + }, + "expected": { + "swagger": "2.0", + "info": { + "title": "Continue On Error", + "version": "1.0" + }, + "paths": { + "/todos": { + "get": { + "responses": { + "200": { + "description": "List Todos", + "schema": { + "type": "array", + "items": { + "allOf": [ + { + "type": "object", + "properties": { + "completed": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "properties": { + "completed_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + } + ] + } + } + }, + "404": {} + } + } + } + }, + "definitions": { + "todo-full": { + "allOf": [ + { + "type": "object", + "properties": { + "completed": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "properties": { + "completed_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + } + ] + }, + "todo-partial": { + "type": "object", + "properties": { + "completed": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/vendor/github.com/go-openapi/spec/ref.go b/vendor/github.com/go-openapi/spec/ref.go index f6bfbfb560..4833b87e2f 100644 --- a/vendor/github.com/go-openapi/spec/ref.go +++ b/vendor/github.com/go-openapi/spec/ref.go @@ -120,25 +120,18 @@ func NewRef(refURI string) (Ref, error) { return Ref{Ref: ref}, nil } -// MustCreateRef creates a ref object but +// MustCreateRef creates a ref object but panics when refURI is invalid. +// Use the NewRef method for a version that returns an error. func MustCreateRef(refURI string) Ref { return Ref{Ref: jsonreference.MustCreateRef(refURI)} } -// // NewResolvedRef creates a resolved ref -// func NewResolvedRef(refURI string, data interface{}) Ref { -// return Ref{ -// Ref: jsonreference.MustCreateRef(refURI), -// Resolved: data, -// } -// } - // MarshalJSON marshals this ref into a JSON object func (r Ref) MarshalJSON() ([]byte, error) { str := r.String() if str == "" { if r.IsRoot() { - return []byte(`{"$ref":"#"}`), nil + return []byte(`{"$ref":""}`), nil } return []byte("{}"), nil } diff --git a/vendor/github.com/go-openapi/spec/schema.go b/vendor/github.com/go-openapi/spec/schema.go index 04aafcc74b..1cdcc163f1 100644 --- a/vendor/github.com/go-openapi/spec/schema.go +++ b/vendor/github.com/go-openapi/spec/schema.go @@ -201,8 +201,8 @@ func (r *SchemaURL) UnmarshalJSON(data []byte) error { type SchemaProps struct { ID string `json:"id,omitempty"` - Ref Ref `json:"-,omitempty"` - Schema SchemaURL `json:"-,omitempty"` + Ref Ref `json:"-"` + Schema SchemaURL `json:"-"` Description string `json:"description,omitempty"` Type StringOrArray `json:"type,omitempty"` Format string `json:"format,omitempty"` diff --git a/vendor/github.com/go-openapi/strfmt/time.go b/vendor/github.com/go-openapi/strfmt/time.go index ebf97c7bbf..c1e3745b93 100644 --- a/vendor/github.com/go-openapi/strfmt/time.go +++ b/vendor/github.com/go-openapi/strfmt/time.go @@ -55,13 +55,16 @@ func IsDateTime(str string) bool { const ( // RFC3339Millis represents a ISO8601 format to millis instead of to nanos RFC3339Millis = "2006-01-02T15:04:05.000Z07:00" + // RFC3339Micro represents a ISO8601 format to micro instead of to nano + 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 = `^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$` ) var ( - dateTimeFormats = []string{RFC3339Millis, time.RFC3339, time.RFC3339Nano} + dateTimeFormats = []string{RFC3339Micro, RFC3339Millis, time.RFC3339, time.RFC3339Nano} rxDateTime = regexp.MustCompile(DateTimePattern) + MarshalFormat = RFC3339Millis ) // ParseDateTime parses a string that represents an ISO8601 time or a unix epoch @@ -96,7 +99,7 @@ func NewDateTime() DateTime { } func (t DateTime) String() string { - return time.Time(t).Format(RFC3339Millis) + return time.Time(t).Format(MarshalFormat) } // MarshalText implements the text marshaller interface @@ -145,7 +148,7 @@ func (t DateTime) MarshalJSON() ([]byte, error) { } func (t DateTime) MarshalEasyJSON(w *jwriter.Writer) { - w.String(time.Time(t).Format(RFC3339Millis)) + w.String(time.Time(t).Format(MarshalFormat)) } func (t *DateTime) UnmarshalJSON(data []byte) error { diff --git a/vendor/github.com/go-openapi/swag/json.go b/vendor/github.com/go-openapi/swag/json.go index 0eb3744675..cb20a6a0f7 100644 --- a/vendor/github.com/go-openapi/swag/json.go +++ b/vendor/github.com/go-openapi/swag/json.go @@ -237,6 +237,8 @@ func newNameIndex(tpe reflect.Type) nameIndex { // GetJSONNames gets all the json property names for a type func (n *NameProvider) GetJSONNames(subject interface{}) []string { + n.lock.Lock() + defer n.lock.Unlock() tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() names, ok := n.index[tpe] if !ok { @@ -258,6 +260,8 @@ func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bo // GetJSONNameForType gets the json name for a go property name on a given type func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() names, ok := n.index[tpe] if !ok { names = n.makeNameIndex(tpe) @@ -267,8 +271,6 @@ func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string } func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex { - n.lock.Lock() - defer n.lock.Unlock() names := newNameIndex(tpe) n.index[tpe] = names return names @@ -282,6 +284,8 @@ func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool // GetGoNameForType gets the go name for a given type for a json property name func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() names, ok := n.index[tpe] if !ok { names = n.makeNameIndex(tpe) diff --git a/vendor/github.com/go-openapi/validate/defaulter_test.go b/vendor/github.com/go-openapi/validate/defaulter_test.go new file mode 100644 index 0000000000..e7f37d14c6 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/defaulter_test.go @@ -0,0 +1,53 @@ +package validate + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +var defaulterFixturesPath = filepath.Join("fixtures", "defaulting") + +func TestDefaulter(t *testing.T) { + fname := filepath.Join(defaulterFixturesPath, "schema.json") + b, err := ioutil.ReadFile(fname) + assert.NoError(t, err) + var schema spec.Schema + assert.NoError(t, json.Unmarshal(b, &schema)) + + err = spec.ExpandSchema(&schema, nil, nil /*new(noopResCache)*/) + assert.NoError(t, err, fname+" should expand cleanly") + + validator := NewSchemaValidator(&schema, nil, "", strfmt.Default) + x := map[string]interface{}{ + "nested": map[string]interface{}{}, + "all": map[string]interface{}{}, + "any": map[string]interface{}{}, + "one": map[string]interface{}{}, + } + t.Logf("Before: %v", x) + r := validator.Validate(x) + assert.False(t, r.HasErrors(), fmt.Sprintf("unexpected validation error: %v", r.AsError())) + + r.ApplyDefaults() + t.Logf("After: %v", x) + var expected interface{} + err = json.Unmarshal([]byte(`{ + "int": 42, + "str": "Hello", + "obj": {"foo": "bar"}, + "nested": {"inner": 7}, + "all": {"foo": 42, "bar": 42}, + "any": {"foo": 42}, + "one": {"bar": 42} + }`), &expected) + assert.NoError(t, err) + assert.Equal(t, expected, x) +} diff --git a/vendor/github.com/go-openapi/validate/fixtures/defaulting/schema.json b/vendor/github.com/go-openapi/validate/fixtures/defaulting/schema.json new file mode 100644 index 0000000000..024dd811a2 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/fixtures/defaulting/schema.json @@ -0,0 +1,101 @@ +{ + "properties": { + "int": { + "type": "integer", + "default": 42 + }, + "str": { + "type": "string", + "minLength": 4, + "default": "Hello" + }, + "obj": { + "type": "object", + "default": {"foo": "bar"} + }, + "nested": { + "type": "object", + "properties": { + "inner": { + "type": "integer", + "default": 7 + } + } + }, + "all": { + "allOf": [ + { + "type": "object", + "properties": { + "foo": { + "type": "integer", + "default": 42 + } + } + }, + { + "type": "object", + "properties": { + "bar": { + "type": "integer", + "default": 42 + } + } + } + ] + }, + "any": { + "anyOf": [ + { + "type": "object", + "properties": { + "foo": { + "type": "integer", + "default": 42 + } + } + }, + { + "type": "object", + "properties": { + "bar": { + "type": "integer", + "default": 42 + } + } + } + ] + }, + "one": { + "oneOf": [ + { + "type": "object", + "properties": { + "foo": { + "type": "integer" + } + }, + "required": ["foo"] + }, + { + "type": "object", + "properties": { + "bar": { + "type": "integer", + "default": 42 + } + } + } + ] + }, + "not": { + "not": { + "type": "object", + "default": { + "foo": 1 + } + } + } + }, + "required": ["int", "str", "nested", "all", "any", "one"] +} diff --git a/vendor/github.com/go-openapi/validate/object_validator.go b/vendor/github.com/go-openapi/validate/object_validator.go index eb3edce833..8169d231c5 100644 --- a/vendor/github.com/go-openapi/validate/object_validator.go +++ b/vendor/github.com/go-openapi/validate/object_validator.go @@ -64,14 +64,6 @@ func (o *objectValidator) Validate(data interface{}) *Result { } res := new(Result) - if len(o.Required) > 0 { - for _, k := range o.Required { - if _, ok := val[k]; !ok { - res.AddErrors(errors.Required(o.Path+"."+k, o.In)) - continue - } - } - } if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows { for k := range val { @@ -102,6 +94,8 @@ func (o *objectValidator) Validate(data interface{}) *Result { } } + createdFromDefaults := map[string]bool{} + for pName, pSchema := range o.Properties { rName := pName if o.Path != "" { @@ -109,7 +103,24 @@ func (o *objectValidator) Validate(data interface{}) *Result { } if v, ok := val[pName]; ok { - res.Merge(NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats).Validate(v)) + r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats).Validate(v) + res.Merge(r) + } else if pSchema.Default != nil { + createdFromDefaults[pName] = true + pName := pName // shaddow + def := pSchema.Default + res.Defaulters = append(res.Defaulters, DefaulterFunc(func() { + val[pName] = def + })) + } + } + + if len(o.Required) > 0 { + for _, k := range o.Required { + if _, ok := val[k]; !ok && !createdFromDefaults[k] { + res.AddErrors(errors.Required(o.Path+"."+k, o.In)) + continue + } } } @@ -140,9 +151,6 @@ func (o *objectValidator) validatePatternProperty(key string, value interface{}, res := validator.Validate(value) result.Merge(res) - if res.IsValid() { - succeededOnce = true - } } } diff --git a/vendor/github.com/go-openapi/validate/result.go b/vendor/github.com/go-openapi/validate/result.go index f5b68969fd..7b58e50df7 100644 --- a/vendor/github.com/go-openapi/validate/result.go +++ b/vendor/github.com/go-openapi/validate/result.go @@ -25,10 +25,21 @@ var ( Debug = os.Getenv("SWAGGER_DEBUG") != "" ) +type Defaulter interface { + Apply() +} + +type DefaulterFunc func() + +func (f DefaulterFunc) Apply() { + f() +} + // Result represents a validation result type Result struct { Errors []error MatchCount int + Defaulters []Defaulter } // Merge merges this result with the other one, preserving match counts etc @@ -38,6 +49,7 @@ func (r *Result) Merge(other *Result) *Result { } r.AddErrors(other.Errors...) r.MatchCount += other.MatchCount + r.Defaulters = append(r.Defaulters, other.Defaulters...) return r } @@ -69,3 +81,9 @@ func (r *Result) AsError() error { } return errors.CompositeValidationError(r.Errors...) } + +func (r *Result) ApplyDefaults() { + for _, d := range r.Defaulters { + d.Apply() + } +} diff --git a/vendor/github.com/go-openapi/validate/schema.go b/vendor/github.com/go-openapi/validate/schema.go index cf87286c4b..1073d8cda2 100644 --- a/vendor/github.com/go-openapi/validate/schema.go +++ b/vendor/github.com/go-openapi/validate/schema.go @@ -148,7 +148,6 @@ func (s *SchemaValidator) commonValidator() valueValidator { return &basicCommonValidator{ Path: s.Path, In: s.in, - Default: s.Schema.Default, Enum: s.Schema.Enum, } } @@ -184,7 +183,6 @@ func (s *SchemaValidator) stringValidator() valueValidator { return &stringValidator{ Path: s.Path, In: s.in, - Default: s.Schema.Default, MaxLength: s.Schema.MaxLength, MinLength: s.Schema.MinLength, Pattern: s.Schema.Pattern, @@ -195,7 +193,6 @@ func (s *SchemaValidator) formatValidator() valueValidator { return &formatValidator{ Path: s.Path, In: s.in, - //Default: s.Schema.Default, Format: s.Schema.Format, KnownFormats: s.KnownFormats, } diff --git a/vendor/github.com/go-openapi/validate/schema_props.go b/vendor/github.com/go-openapi/validate/schema_props.go index a32de3ecd1..5d8ed40452 100644 --- a/vendor/github.com/go-openapi/validate/schema_props.go +++ b/vendor/github.com/go-openapi/validate/schema_props.go @@ -89,6 +89,7 @@ func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bo func (s *schemaPropsValidator) Validate(data interface{}) *Result { mainResult := new(Result) + var firstSuccess *Result if len(s.anyOfValidators) > 0 { var bestFailures *Result succeededOnce := false @@ -97,6 +98,9 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result { if result.IsValid() { bestFailures = nil succeededOnce = true + if firstSuccess == nil { + firstSuccess = result + } break } if bestFailures == nil || result.MatchCount > bestFailures.MatchCount { @@ -109,11 +113,14 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result { } if bestFailures != nil { mainResult.Merge(bestFailures) + } else if firstSuccess != nil { + mainResult.Merge(firstSuccess) } } if len(s.oneOfValidators) > 0 { var bestFailures *Result + var firstSuccess *Result validated := 0 for _, oneOfSchema := range s.oneOfValidators { @@ -121,6 +128,9 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result { if result.IsValid() { validated++ bestFailures = nil + if firstSuccess == nil { + firstSuccess = result + } continue } if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) { @@ -133,6 +143,8 @@ func (s *schemaPropsValidator) Validate(data interface{}) *Result { if bestFailures != nil { mainResult.Merge(bestFailures) } + } else if firstSuccess != nil { + mainResult.Merge(firstSuccess) } } diff --git a/vendor/github.com/mailru/easyjson/.travis.yml b/vendor/github.com/mailru/easyjson/.travis.yml index 3e5ac1320b..884f8bbdf4 100644 --- a/vendor/github.com/mailru/easyjson/.travis.yml +++ b/vendor/github.com/mailru/easyjson/.travis.yml @@ -1,8 +1,9 @@ language: go go: - - tip + - tip install: - - go get github.com/ugorji/go/codec - - go get github.com/pquerna/ffjson/fflib/v1 - - go get github.com/golang/lint/golint + - go get github.com/ugorji/go/codec + - go get github.com/pquerna/ffjson/fflib/v1 + - go get github.com/json-iterator/go + - go get github.com/golang/lint/golint diff --git a/vendor/github.com/mailru/easyjson/Makefile b/vendor/github.com/mailru/easyjson/Makefile index 8e720a084a..ea591b0c05 100644 --- a/vendor/github.com/mailru/easyjson/Makefile +++ b/vendor/github.com/mailru/easyjson/Makefile @@ -4,7 +4,7 @@ export GOPATH all: test -.root/src/$(PKG): +.root/src/$(PKG): mkdir -p $@ for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done @@ -45,6 +45,7 @@ test: generate root bench-other: generate root @go test -benchmem -bench . $(PKG)/benchmark @go test -benchmem -tags use_ffjson -bench . $(PKG)/benchmark + @go test -benchmem -tags use_jsoniter -bench . $(PKG)/benchmark @go test -benchmem -tags use_codec -bench . $(PKG)/benchmark bench-python: diff --git a/vendor/github.com/mailru/easyjson/README.md b/vendor/github.com/mailru/easyjson/README.md index 9cb88455f6..9366e3f712 100644 --- a/vendor/github.com/mailru/easyjson/README.md +++ b/vendor/github.com/mailru/easyjson/README.md @@ -49,18 +49,20 @@ Usage of easyjson: process the whole package instead of just the given file -snake_case use snake_case names instead of CamelCase by default + -lower_camel_case + use lowerCamelCase instead of CamelCase by default -stubs only generate stubs for marshaler/unmarshaler funcs ``` Using `-all` will generate marshalers/unmarshalers for all Go structs in the -file. If `-all` is not provided, then only those structs whose preceeding +file. If `-all` is not provided, then only those structs whose preceding comment starts with `easyjson:json` will have marshalers/unmarshalers generated. For example: ```go //easyjson:json -struct A{} +type A struct {} ``` Additional option notes: diff --git a/vendor/github.com/mailru/easyjson/benchmark/default_test.go b/vendor/github.com/mailru/easyjson/benchmark/default_test.go index b647bef239..68b37910de 100644 --- a/vendor/github.com/mailru/easyjson/benchmark/default_test.go +++ b/vendor/github.com/mailru/easyjson/benchmark/default_test.go @@ -1,4 +1,4 @@ -// +build !use_easyjson,!use_ffjson,!use_codec +// +build !use_easyjson,!use_ffjson,!use_codec,!use_jsoniter package benchmark diff --git a/vendor/github.com/mailru/easyjson/benchmark/jsoniter_test.go b/vendor/github.com/mailru/easyjson/benchmark/jsoniter_test.go new file mode 100644 index 0000000000..004f891da5 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/benchmark/jsoniter_test.go @@ -0,0 +1,119 @@ +// +build use_jsoniter + +package benchmark + +import ( + "testing" + + jsoniter "github.com/json-iterator/go" +) + +func BenchmarkJI_Unmarshal_M(b *testing.B) { + b.SetBytes(int64(len(largeStructText))) + for i := 0; i < b.N; i++ { + var s LargeStruct + err := jsoniter.Unmarshal(largeStructText, &s) + if err != nil { + b.Error(err) + } + } +} + +func BenchmarkJI_Unmarshal_S(b *testing.B) { + for i := 0; i < b.N; i++ { + var s Entities + err := jsoniter.Unmarshal(smallStructText, &s) + if err != nil { + b.Error(err) + } + } + b.SetBytes(int64(len(smallStructText))) +} + +func BenchmarkJI_Marshal_M(b *testing.B) { + var l int64 + for i := 0; i < b.N; i++ { + data, err := jsoniter.Marshal(&largeStructData) + if err != nil { + b.Error(err) + } + l = int64(len(data)) + } + b.SetBytes(l) +} + +func BenchmarkJI_Marshal_L(b *testing.B) { + var l int64 + for i := 0; i < b.N; i++ { + data, err := jsoniter.Marshal(&xlStructData) + if err != nil { + b.Error(err) + } + l = int64(len(data)) + } + b.SetBytes(l) +} + +func BenchmarkJI_Marshal_M_Parallel(b *testing.B) { + var l int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + data, err := jsoniter.Marshal(&largeStructData) + if err != nil { + b.Error(err) + } + l = int64(len(data)) + } + }) + b.SetBytes(l) +} + +func BenchmarkJI_Marshal_L_Parallel(b *testing.B) { + var l int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + data, err := jsoniter.Marshal(&xlStructData) + if err != nil { + b.Error(err) + } + l = int64(len(data)) + } + }) + b.SetBytes(l) +} + +func BenchmarkJI_Marshal_S(b *testing.B) { + var l int64 + for i := 0; i < b.N; i++ { + data, err := jsoniter.Marshal(&smallStructData) + if err != nil { + b.Error(err) + } + l = int64(len(data)) + } + b.SetBytes(l) +} + +func BenchmarkJI_Marshal_S_Parallel(b *testing.B) { + var l int64 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + data, err := jsoniter.Marshal(&smallStructData) + if err != nil { + b.Error(err) + } + l = int64(len(data)) + } + }) + b.SetBytes(l) +} + +func BenchmarkJI_Marshal_M_ToWriter(b *testing.B) { + enc := jsoniter.NewEncoder(&DummyWriter{}) + for i := 0; i < b.N; i++ { + err := enc.Encode(&largeStructData) + if err != nil { + b.Error(err) + } + } +} diff --git a/vendor/github.com/mailru/easyjson/bootstrap/bootstrap.go b/vendor/github.com/mailru/easyjson/bootstrap/bootstrap.go index 0322511721..3c20e09ca6 100644 --- a/vendor/github.com/mailru/easyjson/bootstrap/bootstrap.go +++ b/vendor/github.com/mailru/easyjson/bootstrap/bootstrap.go @@ -24,6 +24,7 @@ type Generator struct { NoStdMarshalers bool SnakeCase bool + LowerCamelCase bool OmitEmpty bool OutName string @@ -110,6 +111,9 @@ func (g *Generator) writeMain() (path string, err error) { if g.SnakeCase { fmt.Fprintln(f, " g.UseSnakeCase()") } + if g.LowerCamelCase { + fmt.Fprintln(f, " g.UseLowerCamelCase()") + } if g.OmitEmpty { fmt.Fprintln(f, " g.OmitEmpty()") } diff --git a/vendor/github.com/mailru/easyjson/easyjson/main.go b/vendor/github.com/mailru/easyjson/easyjson/main.go index 6287926750..1cd30bb363 100644 --- a/vendor/github.com/mailru/easyjson/easyjson/main.go +++ b/vendor/github.com/mailru/easyjson/easyjson/main.go @@ -18,6 +18,7 @@ import ( var buildTags = flag.String("build_tags", "", "build tags to add to generated file") var snakeCase = flag.Bool("snake_case", false, "use snake_case names instead of CamelCase by default") +var lowerCamelCase = flag.Bool("lower_camel_case", false, "use lowerCamelCase names instead of CamelCase by default") var noStdMarshalers = flag.Bool("no_std_marshalers", false, "don't generate MarshalJSON/UnmarshalJSON funcs") var omitEmpty = flag.Bool("omit_empty", false, "omit empty fields by default") var allStructs = flag.Bool("all", false, "generate marshaler/unmarshalers for all structs in a file") @@ -53,12 +54,18 @@ func generate(fname string) (err error) { outName = *specifiedName } + var trimmedBuildTags string + if *buildTags != "" { + trimmedBuildTags = strings.TrimSpace(*buildTags) + } + g := bootstrap.Generator{ - BuildTags: *buildTags, + BuildTags: trimmedBuildTags, PkgPath: p.PkgPath, PkgName: p.PkgName, Types: p.StructNames, SnakeCase: *snakeCase, + LowerCamelCase: *lowerCamelCase, NoStdMarshalers: *noStdMarshalers, OmitEmpty: *omitEmpty, LeaveTemps: *leaveTemps, diff --git a/vendor/github.com/mailru/easyjson/gen/decoder.go b/vendor/github.com/mailru/easyjson/gen/decoder.go index 80f8d2cc6c..2fece5dd0c 100644 --- a/vendor/github.com/mailru/easyjson/gen/decoder.go +++ b/vendor/github.com/mailru/easyjson/gen/decoder.go @@ -228,7 +228,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field fmt.Fprintln(g.out, ws+"if m, ok := "+out+".(easyjson.Unmarshaler); ok {") fmt.Fprintln(g.out, ws+"m.UnmarshalEasyJSON(in)") fmt.Fprintln(g.out, ws+"} else if m, ok := "+out+".(json.Unmarshaler); ok {") - fmt.Fprintln(g.out, ws+"m.UnmarshalJSON(in.Raw())") + fmt.Fprintln(g.out, ws+"_ = m.UnmarshalJSON(in.Raw())") fmt.Fprintln(g.out, ws+"} else {") fmt.Fprintln(g.out, ws+" "+out+" = in.Interface()") fmt.Fprintln(g.out, ws+"}") diff --git a/vendor/github.com/mailru/easyjson/gen/generator.go b/vendor/github.com/mailru/easyjson/gen/generator.go index 988a3a5f8f..eb0d70ba27 100644 --- a/vendor/github.com/mailru/easyjson/gen/generator.go +++ b/vendor/github.com/mailru/easyjson/gen/generator.go @@ -99,6 +99,11 @@ func (g *Generator) UseSnakeCase() { g.fieldNamer = SnakeCaseFieldNamer{} } +// UseLowerCamelCase sets lowerCamelCase field naming strategy. +func (g *Generator) UseLowerCamelCase() { + g.fieldNamer = LowerCamelCaseFieldNamer{} +} + // NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON // methods (only the custom interface). func (g *Generator) NoStdMarshalers() { @@ -219,6 +224,10 @@ func fixAliasName(alias string) string { "_", -1, ) + + if alias[0] == 'v' { // to void conflicting with var names, say v1 + alias = "_" + alias + } return alias } @@ -374,6 +383,71 @@ func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) } } +// LowerCamelCaseFieldNamer +type LowerCamelCaseFieldNamer struct{} + +func isLower(b byte) bool { + return b <= 122 && b >= 97 +} + +func isUpper(b byte) bool { + return b >= 65 && b <= 90 +} + +// convert HTTPRestClient to httpRestClient +func lowerFirst(s string) string { + if s == "" { + return "" + } + + str := "" + strlen := len(s) + + /** + Loop each char + If is uppercase: + If is first char, LOWER it + If the following char is lower, LEAVE it + If the following char is upper OR numeric, LOWER it + If is the end of string, LEAVE it + Else lowercase + */ + + foundLower := false + for i := range s { + ch := s[i] + if isUpper(ch) { + if i == 0 { + str += string(ch + 32) + } else if !foundLower { // Currently just a stream of capitals, eg JSONRESTS[erver] + if strlen > (i+1) && isLower(s[i+1]) { + // Next char is lower, keep this a capital + str += string(ch) + } else { + // Either at end of string or next char is capital + str += string(ch + 32) + } + } else { + str += string(ch) + } + } else { + foundLower = true + str += string(ch) + } + } + + return str +} + +func (LowerCamelCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string { + jsonName := strings.Split(f.Tag.Get("json"), ",")[0] + if jsonName != "" { + return jsonName + } else { + return lowerFirst(f.Name) + } +} + // SnakeCaseFieldNamer implements CamelCase to snake_case conversion for fields names. type SnakeCaseFieldNamer struct{} diff --git a/vendor/github.com/mailru/easyjson/gen/generator_test.go b/vendor/github.com/mailru/easyjson/gen/generator_test.go index 62c03f084f..0c9d278458 100644 --- a/vendor/github.com/mailru/easyjson/gen/generator_test.go +++ b/vendor/github.com/mailru/easyjson/gen/generator_test.go @@ -28,6 +28,28 @@ func TestCamelToSnake(t *testing.T) { } } +func TestCamelToLowerCamel(t *testing.T) { + for i, test := range []struct { + In, Out string + }{ + {"", ""}, + {"A", "a"}, + {"SimpleExample", "simpleExample"}, + {"internalField", "internalField"}, + + {"SomeHTTPStuff", "someHTTPStuff"}, + {"WriteJSON", "writeJSON"}, + {"HTTP2Server", "http2Server"}, + + {"JSONHTTPRPCServer", "jsonhttprpcServer"}, // nothing can be done here without a dictionary + } { + got := lowerFirst(test.In) + if got != test.Out { + t.Errorf("[%d] lowerFirst(%s) = %s; want %s", i, test.In, got, test.Out) + } + } +} + func TestJoinFunctionNameParts(t *testing.T) { for i, test := range []struct { keepFirst bool diff --git a/vendor/github.com/mailru/easyjson/parser/parser_windows.go b/vendor/github.com/mailru/easyjson/parser/parser_windows.go index 64974aa972..a0b2611957 100644 --- a/vendor/github.com/mailru/easyjson/parser/parser_windows.go +++ b/vendor/github.com/mailru/easyjson/parser/parser_windows.go @@ -4,15 +4,18 @@ import ( "fmt" "os" "path" + "path/filepath" "strings" ) func normalizePath(path string) string { - return strings.Replace(path, "\\", "/", -1) + // use lower case, as Windows file systems will almost always be case insensitive + return strings.ToLower(strings.Replace(path, "\\", "/", -1)) } func getPkgPath(fname string, isDir bool) (string, error) { - if !path.IsAbs(fname) { + // path.IsAbs doesn't work properly on Windows; use filepath.IsAbs instead + if !filepath.IsAbs(fname) { pwd, err := os.Getwd() if err != nil { return "", err diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml new file mode 100644 index 0000000000..004172a2e3 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - tip + +go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE index a68e67f01b..8dada3edaf 100644 --- a/vendor/gopkg.in/yaml.v2/LICENSE +++ b/vendor/gopkg.in/yaml.v2/LICENSE @@ -1,188 +1,201 @@ - -Copyright (c) 2011-2014 - Canonical Inc. - -This software is licensed under the LGPLv3, included below. - -As a special exception to the GNU Lesser General Public License version 3 -("LGPL3"), the copyright holders of this Library give you permission to -convey to a third party a Combined Work that links statically or dynamically -to this Library without providing any Minimal Corresponding Source or -Minimal Application Code as set out in 4d or providing the installation -information set out in section 4e, provided that you comply with the other -provisions of LGPL3 and provided that you meet, for the Application the -terms and conditions of the license(s) which apply to the Application. - -Except as stated in this special exception, the provisions of LGPL3 will -continue to comply in full to this Library. If you modify this Library, you -may apply this exception to your version of this Library, but you are not -obliged to do so. If you do not wish to do so, delete this exception -statement from your version. This exception does not (and cannot) modify any -license terms which apply to the Application, with which you must still -comply. - - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. + 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. diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/gopkg.in/yaml.v2/README.md index 7b8bd86701..7a512d67c2 100644 --- a/vendor/gopkg.in/yaml.v2/README.md +++ b/vendor/gopkg.in/yaml.v2/README.md @@ -42,12 +42,14 @@ The package API for yaml v2 will remain stable as described in [gopkg.in](https: License ------- -The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details. +The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. Example ------- +Some more examples can be found in the "examples" folder. + ```Go package main diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go index 085cddc44b..db1f5f2068 100644 --- a/vendor/gopkg.in/yaml.v2/decode.go +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -120,7 +120,6 @@ func (p *parser) parse() *node { default: panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) } - panic("unreachable") } func (p *parser) node(kind int) *node { @@ -191,6 +190,7 @@ type decoder struct { aliases map[string]bool mapType reflect.Type terrors []string + strict bool } var ( @@ -200,8 +200,8 @@ var ( ifaceType = defaultMapType.Elem() ) -func newDecoder() *decoder { - d := &decoder{mapType: defaultMapType} +func newDecoder(strict bool) *decoder { + d := &decoder{mapType: defaultMapType, strict: strict} d.aliases = make(map[string]bool) return d } @@ -251,7 +251,7 @@ func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { // // If n holds a null value, prepare returns before doing anything. func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { - if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "") { + if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "" && n.implicit) { return out, false, false } again := true @@ -640,6 +640,8 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { value := reflect.New(elemType).Elem() d.unmarshal(n.children[i+1], value) inlineMap.SetMapIndex(name, value) + } else if d.strict { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in struct %s", n.line+1, name.String(), out.Type())) } } return true diff --git a/vendor/gopkg.in/yaml.v2/decode_test.go b/vendor/gopkg.in/yaml.v2/decode_test.go index 04fdd9e72c..713b1ee9c4 100644 --- a/vendor/gopkg.in/yaml.v2/decode_test.go +++ b/vendor/gopkg.in/yaml.v2/decode_test.go @@ -405,6 +405,12 @@ var unmarshalTests = []struct { map[string]interface{}{"v": 1}, }, + // Non-specific tag (Issue #75) + { + "v: ! test", + map[string]interface{}{"v": "test"}, + }, + // Anchors and aliases. { "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", @@ -551,7 +557,7 @@ var unmarshalTests = []struct { }, { "a: 2015-02-24T18:19:39Z\n", - map[string]time.Time{"a": time.Unix(1424801979, 0)}, + map[string]time.Time{"a": time.Unix(1424801979, 0).In(time.UTC)}, }, // Encode empty lists as zero-length slices. @@ -559,6 +565,37 @@ var unmarshalTests = []struct { "a: []", &struct{ A []int }{[]int{}}, }, + + // UTF-16-LE + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", + M{"ñoño": "very yes"}, + }, + // UTF-16-LE with surrogate. + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", + M{"ñoño": "very yes 🟔"}, + }, + + // UTF-16-BE + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", + M{"ñoño": "very yes"}, + }, + // UTF-16-BE with surrogate. + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", + M{"ñoño": "very yes 🟔"}, + }, + + // YAML Float regex shouldn't match this + { + "a: 123456e1\n", + M{"a": "123456e1"}, + }, { + "a: 123456E1\n", + M{"a": "123456E1"}, + }, } type M map[interface{}]interface{} @@ -573,7 +610,8 @@ type inlineC struct { } func (s *S) TestUnmarshal(c *C) { - for _, item := range unmarshalTests { + for i, item := range unmarshalTests { + c.Logf("test %d: %q", i, item.data) t := reflect.ValueOf(item.value).Type() var value interface{} switch t.Kind() { @@ -617,6 +655,7 @@ var unmarshalErrorTests = []struct { {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, + {"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "yaml: did not find expected whitespace"}, } func (s *S) TestUnmarshalErrors(c *C) { @@ -638,6 +677,7 @@ var unmarshalerTests = []struct { {`_: BAR!`, "!!str", "BAR!"}, {`_: "BAR!"`, "!!str", "BAR!"}, {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, + {`_: ""`, "!!str", ""}, } var unmarshalerResult = map[int]error{} @@ -936,6 +976,17 @@ func (s *S) TestUnmarshalSliceOnPreset(c *C) { c.Assert(v.A, DeepEquals, []int{2}) } +func (s *S) TestUnmarshalStrict(c *C) { + v := struct{ A, B int }{} + + err := yaml.UnmarshalStrict([]byte("a: 1\nb: 2"), &v) + c.Check(err, IsNil) + err = yaml.Unmarshal([]byte("a: 1\nb: 2\nc: 3"), &v) + c.Check(err, IsNil) + err = yaml.UnmarshalStrict([]byte("a: 1\nb: 2\nc: 3"), &v) + c.Check(err, ErrorMatches, "yaml: unmarshal errors:\n line 1: field c not found in struct struct { A int; B int }") +} + //var data []byte //func init() { // var err error diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/gopkg.in/yaml.v2/emitterc.go index 2befd553ed..41de8b856c 100644 --- a/vendor/gopkg.in/yaml.v2/emitterc.go +++ b/vendor/gopkg.in/yaml.v2/emitterc.go @@ -666,7 +666,6 @@ func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, return yaml_emitter_set_emitter_error(emitter, "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS") } - return false } // Expect ALIAS. @@ -995,7 +994,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { break_space = false space_break = false - preceeded_by_whitespace = false + preceded_by_whitespace = false followed_by_whitespace = false previous_space = false previous_break = false @@ -1017,7 +1016,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { flow_indicators = true } - preceeded_by_whitespace = true + preceded_by_whitespace = true for i, w := 0, 0; i < len(value); i += w { w = width(value[i]) followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) @@ -1048,7 +1047,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { block_indicators = true } case '#': - if preceeded_by_whitespace { + if preceded_by_whitespace { flow_indicators = true block_indicators = true } @@ -1089,7 +1088,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { } // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. - preceeded_by_whitespace = is_blankz(value, i) + preceded_by_whitespace = is_blankz(value, i) } emitter.scalar_data.multiline = line_breaks diff --git a/vendor/gopkg.in/yaml.v2/example_embedded_test.go b/vendor/gopkg.in/yaml.v2/example_embedded_test.go new file mode 100644 index 0000000000..c8b241d549 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/example_embedded_test.go @@ -0,0 +1,41 @@ +package yaml_test + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +// An example showing how to unmarshal embedded +// structs from YAML. + +type StructA struct { + A string `yaml:"a"` +} + +type StructB struct { + // Embedded structs are not treated as embedded in YAML by default. To do that, + // add the ",inline" annotation below + StructA `yaml:",inline"` + B string `yaml:"b"` +} + +var data = ` +a: a string from struct A +b: a string from struct B +` + +func ExampleUnmarshal_embedded() { + var b StructB + + err := yaml.Unmarshal([]byte(data), &b) + if err != nil { + log.Fatal("cannot unmarshal data: %v", err) + } + fmt.Println(b.A) + fmt.Println(b.B) + // Output: + // a string from struct A + // a string from struct B +} diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/gopkg.in/yaml.v2/parserc.go index 0a7037ad1b..81d05dfe57 100644 --- a/vendor/gopkg.in/yaml.v2/parserc.go +++ b/vendor/gopkg.in/yaml.v2/parserc.go @@ -166,7 +166,6 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool default: panic("invalid parser state") } - return false } // Parse the production: diff --git a/vendor/gopkg.in/yaml.v2/readerc.go b/vendor/gopkg.in/yaml.v2/readerc.go index d5fb097277..f450791717 100644 --- a/vendor/gopkg.in/yaml.v2/readerc.go +++ b/vendor/gopkg.in/yaml.v2/readerc.go @@ -247,7 +247,7 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if parser.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { - high, low = 1, 0 + low, high = 1, 0 } // The UTF-16 encoding is not as simple as one might @@ -357,23 +357,26 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if value <= 0x7F { // 0000 0000-0000 007F . 0xxxxxxx parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 } else if value <= 0x7FF { // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 } else if value <= 0xFFFF { // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 } else { // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 } - buffer_len += width parser.unread++ } diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go index 93a8632743..232313cc08 100644 --- a/vendor/gopkg.in/yaml.v2/resolve.go +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -3,6 +3,7 @@ package yaml import ( "encoding/base64" "math" + "regexp" "strconv" "strings" "unicode/utf8" @@ -80,6 +81,8 @@ func resolvableTag(tag string) bool { return false } +var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) + func resolve(tag string, in string) (rtag string, out interface{}) { if !resolvableTag(tag) { return tag, in @@ -135,9 +138,11 @@ func resolve(tag string, in string) (rtag string, out interface{}) { if err == nil { return yaml_INT_TAG, uintv } - floatv, err := strconv.ParseFloat(plain, 64) - if err == nil { - return yaml_FLOAT_TAG, floatv + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } } if strings.HasPrefix(plain, "0b") { intv, err := strconv.ParseInt(plain[2:], 2, 64) diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go index d97d76fa55..0744844558 100644 --- a/vendor/gopkg.in/yaml.v2/scannerc.go +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -9,7 +9,7 @@ import ( // ************ // // The following notes assume that you are familiar with the YAML specification -// (http://yaml.org/spec/cvs/current.html). We mostly follow it, although in +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in // some cases we are less restrictive that it requires. // // The process of transforming a YAML stream into a sequence of events is @@ -611,7 +611,7 @@ func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, co if directive { context = "while parsing a %TAG directive" } - return yaml_parser_set_scanner_error(parser, context, context_mark, "did not find URI escaped octet") + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) } func trace(args ...interface{}) func() { @@ -1546,7 +1546,7 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool // Unknown directive. } else { yaml_parser_set_scanner_error(parser, "while scanning a directive", - start_mark, "found uknown directive name") + start_mark, "found unknown directive name") return false } @@ -1944,7 +1944,7 @@ func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_ma } else { // It's either the '!' tag or not really a tag handle. If it's a %TAG // directive, it's an error. If it's a tag token, it must be a part of URI. - if directive && !(s[0] == '!' && s[1] == 0) { + if directive && string(s) != "!" { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false @@ -1959,6 +1959,7 @@ func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_ma func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { //size_t length = head ? strlen((char *)head) : 0 var s []byte + hasTag := len(head) > 0 // Copy the head if needed. // @@ -2000,10 +2001,10 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } + hasTag = true } - // Check if the tag is non-empty. - if len(s) == 0 { + if !hasTag { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected tag URI") return false diff --git a/vendor/gopkg.in/yaml.v2/yaml.go b/vendor/gopkg.in/yaml.v2/yaml.go index d133edf9d3..bf18884e0e 100644 --- a/vendor/gopkg.in/yaml.v2/yaml.go +++ b/vendor/gopkg.in/yaml.v2/yaml.go @@ -77,8 +77,19 @@ type Marshaler interface { // supported tag options. // func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// UnmarshalStrict is like Unmarshal except that any fields that are found +// in the data that do not have corresponding struct members will result in +// an error. +func UnmarshalStrict(in []byte, out interface{}) (err error) { + return unmarshal(in, out, true) +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { defer handleErr(&err) - d := newDecoder() + d := newDecoder(strict) p := newParser(in) defer p.destroy() node := p.parse() @@ -222,7 +233,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { inlineMap := -1 for i := 0; i != n; i++ { field := st.Field(i) - if field.PkgPath != "" { + if field.PkgPath != "" && !field.Anonymous { continue // Private field } diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go index d60a6b6b00..3caeca0491 100644 --- a/vendor/gopkg.in/yaml.v2/yamlh.go +++ b/vendor/gopkg.in/yaml.v2/yamlh.go @@ -508,7 +508,7 @@ type yaml_parser_t struct { problem string // Error description. - // The byte about which the problem occured. + // The byte about which the problem occurred. problem_offset int problem_value int problem_mark yaml_mark_t