diff -Nru golang-github-getkin-kin-openapi-0.80.0/debian/changelog golang-github-getkin-kin-openapi-0.85.0/debian/changelog --- golang-github-getkin-kin-openapi-0.80.0/debian/changelog 2021-11-04 11:32:52.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/debian/changelog 2021-12-16 07:02:52.000000000 +0000 @@ -1,3 +1,13 @@ +golang-github-getkin-kin-openapi (0.85.0-1) unstable; urgency=medium + + * New upstream version 0.85.0 + * Use dh-sequence-golang instead of dh-golang and --with=golang + * Reorder fields in debian/control and debian/copyright as would be + generated in the next dh-make-golang release after 0.6.0-1 + * Add debian/upstream/metadata + + -- Anthony Fok Thu, 16 Dec 2021 00:02:52 -0700 + golang-github-getkin-kin-openapi (0.80.0-1) unstable; urgency=medium * New upstream version 0.80.0 diff -Nru golang-github-getkin-kin-openapi-0.80.0/debian/control golang-github-getkin-kin-openapi-0.85.0/debian/control --- golang-github-getkin-kin-openapi-0.80.0/debian/control 2021-10-15 10:57:01.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/debian/control 2021-12-16 06:57:46.000000000 +0000 @@ -1,22 +1,22 @@ Source: golang-github-getkin-kin-openapi +Section: golang +Priority: optional Maintainer: Debian Go Packaging Team Uploaders: Alexandre Viau , Anthony Fok -Section: golang -Testsuite: autopkgtest-pkg-go -Priority: optional +Rules-Requires-Root: no Build-Depends: debhelper-compat (= 13), - dh-golang, + dh-sequence-golang, golang-any, golang-github-ghodss-yaml-dev (>= 1.0.0), golang-github-go-openapi-jsonpointer-dev (>= 1:0.19.5), golang-github-gorilla-mux-dev (>= 1.8.0), golang-github-stretchr-testify-dev (>= 1.5.1) +Testsuite: autopkgtest-pkg-go Standards-Version: 4.6.0 Vcs-Browser: https://salsa.debian.org/go-team/packages/golang-github-getkin-kin-openapi Vcs-Git: https://salsa.debian.org/go-team/packages/golang-github-getkin-kin-openapi.git Homepage: https://github.com/getkin/kin-openapi -Rules-Requires-Root: no XS-Go-Import-Path: github.com/getkin/kin-openapi Package: golang-github-getkin-kin-openapi-dev diff -Nru golang-github-getkin-kin-openapi-0.80.0/debian/copyright golang-github-getkin-kin-openapi-0.85.0/debian/copyright --- golang-github-getkin-kin-openapi-0.80.0/debian/copyright 2021-02-28 12:52:14.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/debian/copyright 2021-12-16 07:00:35.000000000 +0000 @@ -1,6 +1,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: kin-openapi Source: https://github.com/getkin/kin-openapi +Upstream-Name: kin-openapi +Upstream-Contact: https://github.com/getkin/kin-openapi/issues/new Files: * Copyright: 2017-2018 the project authors diff -Nru golang-github-getkin-kin-openapi-0.80.0/debian/gitlab-ci.yml golang-github-getkin-kin-openapi-0.85.0/debian/gitlab-ci.yml --- golang-github-getkin-kin-openapi-0.80.0/debian/gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/debian/gitlab-ci.yml 2021-12-16 06:56:37.000000000 +0000 @@ -0,0 +1,6 @@ +# auto-generated, DO NOT MODIFY. +# The authoritative copy of this file lives at: +# https://salsa.debian.org/go-team/infra/pkg-go-tools/blob/master/config/gitlabciyml.go +--- +include: + - https://salsa.debian.org/go-team/infra/pkg-go-tools/-/raw/master/pipeline/test-archive.yml diff -Nru golang-github-getkin-kin-openapi-0.80.0/debian/rules golang-github-getkin-kin-openapi-0.85.0/debian/rules --- golang-github-getkin-kin-openapi-0.80.0/debian/rules 2021-02-28 12:52:14.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/debian/rules 2021-12-16 06:57:12.000000000 +0000 @@ -3,4 +3,4 @@ export DH_GOLANG_INSTALL_EXTRA := openapi3filter/fixtures %: - dh $@ --buildsystem=golang --with=golang + dh $@ --buildsystem=golang diff -Nru golang-github-getkin-kin-openapi-0.80.0/debian/upstream/metadata golang-github-getkin-kin-openapi-0.85.0/debian/upstream/metadata --- golang-github-getkin-kin-openapi-0.80.0/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/debian/upstream/metadata 2021-12-16 07:02:52.000000000 +0000 @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/getkin/kin-openapi/issues +Bug-Submit: https://github.com/getkin/kin-openapi/issues/new +Repository: https://github.com/getkin/kin-openapi.git +Repository-Browse: https://github.com/getkin/kin-openapi diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi2conv/issue187_test.go golang-github-getkin-kin-openapi-0.85.0/openapi2conv/issue187_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi2conv/issue187_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi2conv/issue187_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -167,3 +167,27 @@ err = doc3.Validate(context.Background()) require.NoError(t, err) } + +func TestPR449(t *testing.T) { + spec := ` +swagger: '2.0' +info: + version: 1.0.0 + title: title + +securityDefinitions: + OAuth2Application: + type: "oauth2" + flow: "application" + tokenUrl: "example.com/oauth2/token" +` + doc3, err := v2v3YAML([]byte(spec)) + require.NoError(t, err) + require.NotNil(t, doc3.Components.SecuritySchemes["OAuth2Application"].Value.Flows.ClientCredentials) + _, err = yaml.Marshal(doc3) + require.NoError(t, err) + + doc2, err := FromV3(doc3) + require.NoError(t, err) + require.Equal(t, doc2.SecurityDefinitions["OAuth2Application"].Flow, "application") +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi2conv/openapi2_conv.go golang-github-getkin-kin-openapi-0.85.0/openapi2conv/openapi2_conv.go --- golang-github-getkin-kin-openapi-0.80.0/openapi2conv/openapi2_conv.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi2conv/openapi2_conv.go 2021-12-03 09:49:46.000000000 +0000 @@ -298,6 +298,10 @@ required = true } + var schemaRefRef string + if schemaRef := parameter.Schema; schemaRef != nil && schemaRef.Ref != "" { + schemaRefRef = schemaRef.Ref + } result := &openapi3.Parameter{ In: parameter.In, Name: parameter.Name, @@ -322,7 +326,9 @@ AllowEmptyValue: parameter.AllowEmptyValue, UniqueItems: parameter.UniqueItems, MultipleOf: parameter.MultipleOf, - }}), + }, + Ref: schemaRefRef, + }), } return &openapi3.ParameterRef{Value: result}, nil, nil, nil } @@ -525,6 +531,8 @@ flows.AuthorizationCode = flow case "password": flows.Password = flow + case "application": + flows.ClientCredentials = flow default: return nil, fmt.Errorf("unsupported flow %q", securityScheme.Flow) } @@ -978,6 +986,10 @@ } if schemaRef := parameter.Schema; schemaRef != nil { schemaRef, _ = FromV3SchemaRef(schemaRef, components) + if ref := schemaRef.Ref; ref != "" { + result.Schema = &openapi3.SchemaRef{Ref: FromV3Ref(ref)} + return result, nil + } schema := schemaRef.Value result.Type = schema.Type result.Format = schema.Format @@ -1076,6 +1088,8 @@ result.Flow = "accessCode" } else if flow = flows.Password; flow != nil { result.Flow = "password" + } else if flow = flows.ClientCredentials; flow != nil { + result.Flow = "application" } else { return nil, nil } diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi2conv/openapi2_conv_test.go golang-github-getkin-kin-openapi-0.85.0/openapi2conv/openapi2_conv_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi2conv/openapi2_conv_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi2conv/openapi2_conv_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -79,6 +79,14 @@ "ItemExtension": { "description": "It could be anything.", "type": "boolean" + }, + "foo": { + "description": "foo description", + "enum": [ + "bar", + "baz" + ], + "type": "string" } }, "externalDocs": { @@ -305,6 +313,34 @@ }, "x-path": "path extension 1", "x-path2": "path extension 2" + }, + "/foo": { + "get": { + "operationId": "getFoo", + "consumes": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "x-originalParamName": "foo", + "in": "body", + "name": "foo", + "schema": { + "$ref": "#/definitions/foo" + } + } + ], + "responses": { + "default": { + "description": "OK", + "schema": { + "$ref": "#/definitions/foo" + } + } + }, + "summary": "get foo" + } } }, "responses": { @@ -420,6 +456,14 @@ "type": "string", "x-formData-name": "fileUpload2", "x-mimetype": "text/plain" + }, + "foo": { + "description": "foo description", + "enum": [ + "bar", + "baz" + ], + "type": "string" } } }, @@ -646,6 +690,39 @@ }, "x-path": "path extension 1", "x-path2": "path extension 2" + }, + "/foo": { + "get": { + "operationId": "getFoo", + "requestBody": { + "x-originalParamName": "foo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/foo" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/foo" + } + } + } + }, + "responses": { + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/foo" + } + } + }, + "description": "OK" + } + }, + "summary": "get foo" + } } }, "security": [ diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/internalize_refs.go golang-github-getkin-kin-openapi-0.85.0/openapi3/internalize_refs.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/internalize_refs.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/internalize_refs.go 2021-12-03 09:49:46.000000000 +0000 @@ -0,0 +1,369 @@ +package openapi3 + +import ( + "context" + "path/filepath" + "strings" +) + +type RefNameResolver func(string) string + +// DefaultRefResolver is a default implementation of refNameResolver for the +// InternalizeRefs function. +// +// If a reference points to an element inside a document, it returns the last +// element in the reference using filepath.Base. Otherwise if the reference points +// to a file, it returns the file name trimmed of all extensions. +func DefaultRefNameResolver(ref string) string { + if ref == "" { + return "" + } + split := strings.SplitN(ref, "#", 2) + if len(split) == 2 { + return filepath.Base(split[1]) + } + ref = split[0] + for ext := filepath.Ext(ref); len(ext) > 0; ext = filepath.Ext(ref) { + ref = strings.TrimSuffix(ref, ext) + } + return filepath.Base(ref) +} + +func schemaNames(s Schemas) []string { + out := make([]string, 0, len(s)) + for i := range s { + out = append(out, i) + } + return out +} + +func parametersMapNames(s ParametersMap) []string { + out := make([]string, 0, len(s)) + for i := range s { + out = append(out, i) + } + return out +} + +func isExternalRef(ref string) bool { + return ref != "" && !strings.HasPrefix(ref, "#/components/") +} + +func (doc *T) addSchemaToSpec(s *SchemaRef, refNameResolver RefNameResolver) { + if s == nil || !isExternalRef(s.Ref) { + return + } + + name := refNameResolver(s.Ref) + if _, ok := doc.Components.Schemas[name]; ok { + s.Ref = "#/components/schemas/" + name + return + } + + if doc.Components.Schemas == nil { + doc.Components.Schemas = make(Schemas) + } + doc.Components.Schemas[name] = s.Value.NewRef() + s.Ref = "#/components/schemas/" + name +} + +func (doc *T) addParameterToSpec(p *ParameterRef, refNameResolver RefNameResolver) { + if p == nil || !isExternalRef(p.Ref) { + return + } + name := refNameResolver(p.Ref) + if _, ok := doc.Components.Parameters[name]; ok { + p.Ref = "#/components/parameters/" + name + return + } + + if doc.Components.Parameters == nil { + doc.Components.Parameters = make(ParametersMap) + } + doc.Components.Parameters[name] = &ParameterRef{Value: p.Value} + p.Ref = "#/components/parameters/" + name +} + +func (doc *T) addHeaderToSpec(h *HeaderRef, refNameResolver RefNameResolver) { + if h == nil || !isExternalRef(h.Ref) { + return + } + name := refNameResolver(h.Ref) + if _, ok := doc.Components.Headers[name]; ok { + h.Ref = "#/components/headers/" + name + return + } + if doc.Components.Headers == nil { + doc.Components.Headers = make(Headers) + } + doc.Components.Headers[name] = &HeaderRef{Value: h.Value} + h.Ref = "#/components/headers/" + name +} + +func (doc *T) addRequestBodyToSpec(r *RequestBodyRef, refNameResolver RefNameResolver) { + if r == nil || !isExternalRef(r.Ref) { + return + } + name := refNameResolver(r.Ref) + if _, ok := doc.Components.RequestBodies[name]; ok { + r.Ref = "#/components/requestBodies/" + name + return + } + if doc.Components.RequestBodies == nil { + doc.Components.RequestBodies = make(RequestBodies) + } + doc.Components.RequestBodies[name] = &RequestBodyRef{Value: r.Value} + r.Ref = "#/components/requestBodies/" + name +} + +func (doc *T) addResponseToSpec(r *ResponseRef, refNameResolver RefNameResolver) { + if r == nil || !isExternalRef(r.Ref) { + return + } + name := refNameResolver(r.Ref) + if _, ok := doc.Components.Responses[name]; ok { + r.Ref = "#/components/responses/" + name + return + } + if doc.Components.Responses == nil { + doc.Components.Responses = make(Responses) + } + doc.Components.Responses[name] = &ResponseRef{Value: r.Value} + r.Ref = "#/components/responses/" + name + +} + +func (doc *T) addSecuritySchemeToSpec(ss *SecuritySchemeRef, refNameResolver RefNameResolver) { + if ss == nil || !isExternalRef(ss.Ref) { + return + } + name := refNameResolver(ss.Ref) + if _, ok := doc.Components.SecuritySchemes[name]; ok { + ss.Ref = "#/components/securitySchemes/" + name + return + } + if doc.Components.SecuritySchemes == nil { + doc.Components.SecuritySchemes = make(SecuritySchemes) + } + doc.Components.SecuritySchemes[name] = &SecuritySchemeRef{Value: ss.Value} + ss.Ref = "#/components/securitySchemes/" + name + +} + +func (doc *T) addExampleToSpec(e *ExampleRef, refNameResolver RefNameResolver) { + if e == nil || !isExternalRef(e.Ref) { + return + } + name := refNameResolver(e.Ref) + if _, ok := doc.Components.Examples[name]; ok { + e.Ref = "#/components/examples/" + name + return + } + if doc.Components.Examples == nil { + doc.Components.Examples = make(Examples) + } + doc.Components.Examples[name] = &ExampleRef{Value: e.Value} + e.Ref = "#/components/examples/" + name + +} + +func (doc *T) addLinkToSpec(l *LinkRef, refNameResolver RefNameResolver) { + if l == nil || !isExternalRef(l.Ref) { + return + } + name := refNameResolver(l.Ref) + if _, ok := doc.Components.Links[name]; ok { + l.Ref = "#/components/links/" + name + return + } + if doc.Components.Links == nil { + doc.Components.Links = make(Links) + } + doc.Components.Links[name] = &LinkRef{Value: l.Value} + l.Ref = "#/components/links/" + name + +} + +func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver) { + if c == nil || !isExternalRef(c.Ref) { + return + } + name := refNameResolver(c.Ref) + if _, ok := doc.Components.Callbacks[name]; ok { + c.Ref = "#/components/callbacks/" + name + } + if doc.Components.Callbacks == nil { + doc.Components.Callbacks = make(Callbacks) + } + doc.Components.Callbacks[name] = &CallbackRef{Value: c.Value} + c.Ref = "#/components/callbacks/" + name +} + +func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver) { + if s == nil { + return + } + + for _, list := range []SchemaRefs{s.AllOf, s.AnyOf, s.OneOf} { + for _, s2 := range list { + doc.addSchemaToSpec(s2, refNameResolver) + if s2 != nil { + doc.derefSchema(s2.Value, refNameResolver) + } + } + } + for _, s2 := range s.Properties { + doc.addSchemaToSpec(s2, refNameResolver) + if s2 != nil { + doc.derefSchema(s2.Value, refNameResolver) + } + } + for _, ref := range []*SchemaRef{s.Not, s.AdditionalProperties, s.Items} { + doc.addSchemaToSpec(ref, refNameResolver) + if ref != nil { + doc.derefSchema(ref.Value, refNameResolver) + } + } +} + +func (doc *T) derefHeaders(hs Headers, refNameResolver RefNameResolver) { + for _, h := range hs { + doc.addHeaderToSpec(h, refNameResolver) + doc.derefParameter(h.Value.Parameter, refNameResolver) + } +} + +func (doc *T) derefExamples(es Examples, refNameResolver RefNameResolver) { + for _, e := range es { + doc.addExampleToSpec(e, refNameResolver) + } +} + +func (doc *T) derefContent(c Content, refNameResolver RefNameResolver) { + for _, mediatype := range c { + doc.addSchemaToSpec(mediatype.Schema, refNameResolver) + if mediatype.Schema != nil { + doc.derefSchema(mediatype.Schema.Value, refNameResolver) + } + doc.derefExamples(mediatype.Examples, refNameResolver) + for _, e := range mediatype.Encoding { + doc.derefHeaders(e.Headers, refNameResolver) + } + } +} + +func (doc *T) derefLinks(ls Links, refNameResolver RefNameResolver) { + for _, l := range ls { + doc.addLinkToSpec(l, refNameResolver) + } +} + +func (doc *T) derefResponses(es Responses, refNameResolver RefNameResolver) { + for _, e := range es { + doc.addResponseToSpec(e, refNameResolver) + if e.Value != nil { + doc.derefHeaders(e.Value.Headers, refNameResolver) + doc.derefContent(e.Value.Content, refNameResolver) + doc.derefLinks(e.Value.Links, refNameResolver) + } + } +} + +func (doc *T) derefParameter(p Parameter, refNameResolver RefNameResolver) { + doc.addSchemaToSpec(p.Schema, refNameResolver) + doc.derefContent(p.Content, refNameResolver) + if p.Schema != nil { + doc.derefSchema(p.Schema.Value, refNameResolver) + } +} + +func (doc *T) derefRequestBody(r RequestBody, refNameResolver RefNameResolver) { + doc.derefContent(r.Content, refNameResolver) +} + +func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameResolver) { + for _, ops := range paths { + // inline full operations + ops.Ref = "" + + for _, op := range ops.Operations() { + doc.addRequestBodyToSpec(op.RequestBody, refNameResolver) + if op.RequestBody != nil && op.RequestBody.Value != nil { + doc.derefRequestBody(*op.RequestBody.Value, refNameResolver) + } + for _, cb := range op.Callbacks { + doc.addCallbackToSpec(cb, refNameResolver) + if cb.Value != nil { + doc.derefPaths(*cb.Value, refNameResolver) + } + } + doc.derefResponses(op.Responses, refNameResolver) + for _, param := range op.Parameters { + doc.addParameterToSpec(param, refNameResolver) + if param.Value != nil { + doc.derefParameter(*param.Value, refNameResolver) + } + } + } + } +} + +// InternalizeRefs removes all references to external files from the spec and moves them +// to the components section. +// +// refNameResolver takes in references to returns a name to store the reference under locally. +// It MUST return a unique name for each reference type. +// A default implementation is provided that will suffice for most use cases. See the function +// documention for more details. +// +// Example: +// +// doc.InternalizeRefs(context.Background(), nil) +func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref string) string) { + if refNameResolver == nil { + refNameResolver = DefaultRefNameResolver + } + + // Handle components section + names := schemaNames(doc.Components.Schemas) + for _, name := range names { + schema := doc.Components.Schemas[name] + doc.addSchemaToSpec(schema, refNameResolver) + if schema != nil { + schema.Ref = "" // always dereference the top level + doc.derefSchema(schema.Value, refNameResolver) + } + } + names = parametersMapNames(doc.Components.Parameters) + for _, name := range names { + p := doc.Components.Parameters[name] + doc.addParameterToSpec(p, refNameResolver) + if p != nil && p.Value != nil { + p.Ref = "" // always dereference the top level + doc.derefParameter(*p.Value, refNameResolver) + } + } + doc.derefHeaders(doc.Components.Headers, refNameResolver) + for _, req := range doc.Components.RequestBodies { + doc.addRequestBodyToSpec(req, refNameResolver) + if req != nil && req.Value != nil { + req.Ref = "" // always dereference the top level + doc.derefRequestBody(*req.Value, refNameResolver) + } + } + doc.derefResponses(doc.Components.Responses, refNameResolver) + for _, ss := range doc.Components.SecuritySchemes { + doc.addSecuritySchemeToSpec(ss, refNameResolver) + } + doc.derefExamples(doc.Components.Examples, refNameResolver) + doc.derefLinks(doc.Components.Links, refNameResolver) + for _, cb := range doc.Components.Callbacks { + doc.addCallbackToSpec(cb, refNameResolver) + if cb != nil && cb.Value != nil { + cb.Ref = "" // always dereference the top level + doc.derefPaths(*cb.Value, refNameResolver) + } + } + + doc.derefPaths(doc.Paths, refNameResolver) +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/internalize_refs_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3/internalize_refs_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/internalize_refs_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/internalize_refs_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -0,0 +1,55 @@ +package openapi3 + +import ( + "context" + "regexp" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestInternalizeRefs(t *testing.T) { + var regexpRef = regexp.MustCompile(`"\$ref":`) + var regexpRefInternal = regexp.MustCompile(`"\$ref":"\#`) + + tests := []struct { + filename string + }{ + {"testdata/testref.openapi.yml"}, + {"testdata/recursiveRef/openapi.yml"}, + {"testdata/spec.yaml"}, + {"testdata/callbacks.yml"}, + } + + for _, test := range tests { + t.Run(test.filename, func(t *testing.T) { + // Load in the reference spec from the testdata + sl := NewLoader() + sl.IsExternalRefsAllowed = true + doc, err := sl.LoadFromFile(test.filename) + require.NoError(t, err, "loading test file") + + // Internalize the references + doc.InternalizeRefs(context.Background(), DefaultRefNameResolver) + + // Validate the internalized spec + err = doc.Validate(context.Background()) + require.Nil(t, err, "validating internalized spec") + + data, err := doc.MarshalJSON() + require.NoError(t, err, "marshalling internalized spec") + + // run a static check over the file, making sure each occurence of a + // reference is followed by a # + numRefs := len(regexpRef.FindAll(data, -1)) + numInternalRefs := len(regexpRefInternal.FindAll(data, -1)) + require.Equal(t, numRefs, numInternalRefs, "checking all references are internal") + + // load from data, but with the path set to the current directory + doc2, err := sl.LoadFromData(data) + require.NoError(t, err, "reloading spec") + err = doc2.Validate(context.Background()) + require.Nil(t, err, "validating reloaded spec") + }) + } +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/loader_issue212_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3/loader_issue212_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/loader_issue212_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/loader_issue212_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -81,7 +81,7 @@ expected, err := json.Marshal(&Schema{ Type: "object", Required: []string{"id", "uri"}, - Properties: map[string]*SchemaRef{ + Properties: Schemas{ "id": {Value: &Schema{Type: "string"}}, "uri": {Value: &Schema{Type: "string"}}, }, diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/loader_recursive_ref_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3/loader_recursive_ref_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/loader_recursive_ref_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/loader_recursive_ref_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -11,7 +11,39 @@ loader.IsExternalRefsAllowed = true doc, err := loader.LoadFromFile("testdata/recursiveRef/openapi.yml") require.NoError(t, err) - require.NotNil(t, doc) - require.NoError(t, doc.Validate(loader.Context)) + err = doc.Validate(loader.Context) + require.NoError(t, err) require.Equal(t, "bar", doc.Paths["/foo"].Get.Responses.Get(200).Value.Content.Get("application/json").Schema.Value.Properties["foo2"].Value.Properties["foo"].Value.Properties["bar"].Value.Example) } + +func TestIssue447(t *testing.T) { + loader := NewLoader() + doc, err := loader.LoadFromData([]byte(` +openapi: 3.0.1 +info: + title: Recursive refs example + version: "1.0" +paths: {} +components: + schemas: + Complex: + type: object + properties: + parent: + $ref: '#/components/schemas/Complex' +`)) + require.NoError(t, err) + err = doc.Validate(loader.Context) + require.NoError(t, err) + require.Equal(t, "object", doc.Components. + // Complex + Schemas["Complex"]. + // parent + Value.Properties["parent"]. + // parent + Value.Properties["parent"]. + // parent + Value.Properties["parent"]. + // type + Value.Type) +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/loader_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3/loader_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/loader_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/loader_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -227,18 +227,18 @@ require.NotNil(t, doc.Paths["/"].Post.RequestBody.Value.Content.Get("application/json").Examples["test"]) } -func createTestServer(handler http.Handler) *httptest.Server { +func createTestServer(t *testing.T, handler http.Handler) *httptest.Server { ts := httptest.NewUnstartedServer(handler) - l, _ := net.Listen("tcp", addr) + l, err := net.Listen("tcp", addr) + require.NoError(t, err) ts.Listener.Close() ts.Listener = l return ts } func TestLoadFromRemoteURL(t *testing.T) { - fs := http.FileServer(http.Dir("testdata")) - ts := createTestServer(fs) + ts := createTestServer(t, fs) ts.Start() defer ts.Close() @@ -351,7 +351,7 @@ }`) fs := http.FileServer(http.Dir("testdata")) - ts := createTestServer(fs) + ts := createTestServer(t, fs) ts.Start() defer ts.Close() diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/schema.go golang-github-getkin-kin-openapi-0.85.0/openapi3/schema.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/schema.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/schema.go 2021-12-03 09:49:46.000000000 +0000 @@ -372,7 +372,7 @@ func NewObjectSchema() *Schema { return &Schema{ Type: TypeObject, - Properties: make(map[string]*SchemaRef), + Properties: make(Schemas), } } @@ -493,7 +493,7 @@ func (schema *Schema) WithPropertyRef(name string, ref *SchemaRef) *Schema { properties := schema.Properties if properties == nil { - properties = make(map[string]*SchemaRef) + properties = make(Schemas) schema.Properties = properties } properties[name] = ref @@ -501,7 +501,7 @@ } func (schema *Schema) WithProperties(properties map[string]*Schema) *Schema { - result := make(map[string]*SchemaRef, len(properties)) + result := make(Schemas, len(properties)) for k, v := range properties { result[k] = &SchemaRef{ Value: v, @@ -800,13 +800,22 @@ return schema.visitJSONArray(settings, value) case map[string]interface{}: return schema.visitJSONObject(settings, value) - default: - return &SchemaError{ - Value: value, - Schema: schema, - SchemaField: "type", - Reason: fmt.Sprintf("unhandled value of type %T", value), + case map[interface{}]interface{}: // for YAML cf. issue #444 + values := make(map[string]interface{}, len(value)) + for key, v := range value { + if k, ok := key.(string); ok { + values[k] = v + } } + if len(value) == len(values) { + return schema.visitJSONObject(settings, values) + } + } + return &SchemaError{ + Value: value, + Schema: schema, + SchemaField: "type", + Reason: fmt.Sprintf("unhandled value of type %T", value), } } diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3/schema_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3/schema_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3/schema_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3/schema_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -389,7 +389,7 @@ UniqueItems: true, Items: (&Schema{ Type: "object", - Properties: map[string]*SchemaRef{ + Properties: Schemas{ "key1": NewFloat64Schema().NewRef(), }, }).NewRef(), @@ -446,7 +446,7 @@ UniqueItems: true, Items: (&Schema{ Type: "object", - Properties: map[string]*SchemaRef{ + Properties: Schemas{ "key1": (&Schema{ Type: "array", UniqueItems: true, @@ -579,7 +579,7 @@ UniqueItems: true, Items: (&Schema{ Type: "object", - Properties: map[string]*SchemaRef{ + Properties: Schemas{ "key1": NewFloat64Schema().NewRef(), }, }).NewRef(), @@ -678,7 +678,7 @@ Schema: &Schema{ Type: "object", MaxProps: Uint64Ptr(2), - Properties: map[string]*SchemaRef{ + Properties: Schemas{ "numberProperty": NewFloat64Schema().NewRef(), }, }, diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3filter/req_resp_decoder.go golang-github-getkin-kin-openapi-0.85.0/openapi3filter/req_resp_decoder.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3filter/req_resp_decoder.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3filter/req_resp_decoder.go 2021-12-03 09:49:46.000000000 +0000 @@ -244,8 +244,6 @@ } func decodeValue(dec valueDecoder, param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef, required bool) (interface{}, error) { - var decodeFn func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, error) - if len(schema.Value.AllOf) > 0 { var value interface{} var err error @@ -298,6 +296,7 @@ } if schema.Value.Type != "" { + var decodeFn func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, error) switch schema.Value.Type { case "array": decodeFn = func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, error) { diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3filter/validate_request.go golang-github-getkin-kin-openapi-0.85.0/openapi3filter/validate_request.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3filter/validate_request.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3filter/validate_request.go 2021-12-03 09:49:46.000000000 +0000 @@ -140,7 +140,7 @@ // Validate a parameter's value. if value == nil { if parameter.Required { - return &RequestError{Input: input, Parameter: parameter, Reason: ErrInvalidRequired.Error(), Err: ErrInvalidRequired} + return &RequestError{Input: input, Parameter: parameter, Err: ErrInvalidRequired} } return nil } diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3filter/validation_error_encoder.go golang-github-getkin-kin-openapi-0.85.0/openapi3filter/validation_error_encoder.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3filter/validation_error_encoder.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3filter/validation_error_encoder.go 2021-12-03 09:49:46.000000000 +0000 @@ -75,7 +75,7 @@ } func convertErrInvalidRequired(e *RequestError) *ValidationError { - if e.Reason == ErrInvalidRequired.Error() && e.Parameter != nil { + if e.Err == ErrInvalidRequired && e.Parameter != nil { return &ValidationError{ Status: http.StatusBadRequest, Title: fmt.Sprintf("parameter %q in %s is required", e.Parameter.Name, e.Parameter.In), diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3filter/validation_error_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3filter/validation_error_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3filter/validation_error_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3filter/validation_error_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -56,7 +56,8 @@ } func getValidationTests(t *testing.T) []*validationTest { - badHost, _ := http.NewRequest(http.MethodGet, "http://unknown-host.com/v2/pet", nil) + badHost, err := http.NewRequest(http.MethodGet, "http://unknown-host.com/v2/pet", nil) + require.NoError(t, err) badPath := newPetstoreRequest(t, http.MethodGet, "/watdis", nil) badMethod := newPetstoreRequest(t, http.MethodTrace, "/pet", nil) @@ -180,7 +181,7 @@ }, wantErrParam: "status", wantErrParamIn: "query", - wantErrReason: ErrInvalidRequired.Error(), + wantErrBody: `parameter "status" in query has an error: value is required but missing`, wantErrResponse: &ValidationError{Status: http.StatusBadRequest, Title: `parameter "status" in query is required`}, }, @@ -424,7 +425,7 @@ }, wantErrParam: "petId", wantErrParamIn: "path", - wantErrReason: ErrInvalidRequired.Error(), + wantErrBody: `parameter "petId" in path has an error: value is required but missing`, wantErrResponse: &ValidationError{Status: http.StatusBadRequest, Title: `parameter "petId" in path is required`}, }, diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3gen/openapi3gen.go golang-github-getkin-kin-openapi-0.85.0/openapi3gen/openapi3gen.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3gen/openapi3gen.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3gen/openapi3gen.go 2021-12-03 09:49:46.000000000 +0000 @@ -52,14 +52,10 @@ return func(x *generatorOpt) { x.schemaCustomizer = sc } } -// NewSchemaRefForValue uses reflection on the given value to produce a SchemaRef. -func NewSchemaRefForValue(value interface{}, opts ...Option) (*openapi3.SchemaRef, map[*openapi3.SchemaRef]int, error) { +// NewSchemaRefForValue is a shortcut for NewGenerator(...).NewSchemaRefForValue(...) +func NewSchemaRefForValue(value interface{}, schemas openapi3.Schemas, opts ...Option) (*openapi3.SchemaRef, error) { g := NewGenerator(opts...) - ref, err := g.GenerateSchemaRef(reflect.TypeOf(value)) - for ref := range g.SchemaRefs { - ref.Ref = "" - } - return ref, g.SchemaRefs, err + return g.NewSchemaRefForValue(value, schemas) } type Generator struct { @@ -71,6 +67,9 @@ // If count is 1, it's not ne // An OpenAPI identifier has been assigned to each. SchemaRefs map[*openapi3.SchemaRef]int + + // componentSchemaRefs is a set of schemas that must be defined in the components to avoid cycles + componentSchemaRefs map[string]struct{} } func NewGenerator(opts ...Option) *Generator { @@ -79,9 +78,10 @@ f(gOpt) } return &Generator{ - Types: make(map[reflect.Type]*openapi3.SchemaRef), - SchemaRefs: make(map[*openapi3.SchemaRef]int), - opts: *gOpt, + Types: make(map[reflect.Type]*openapi3.SchemaRef), + SchemaRefs: make(map[*openapi3.SchemaRef]int), + componentSchemaRefs: make(map[string]struct{}), + opts: *gOpt, } } @@ -90,17 +90,41 @@ return g.generateSchemaRefFor(nil, t, "_root", "") } +// NewSchemaRefForValue uses reflection on the given value to produce a SchemaRef, and updates a supplied map with any dependent component schemas if they lead to cycles +func (g *Generator) NewSchemaRefForValue(value interface{}, schemas openapi3.Schemas) (*openapi3.SchemaRef, error) { + ref, err := g.GenerateSchemaRef(reflect.TypeOf(value)) + if err != nil { + return nil, err + } + for ref := range g.SchemaRefs { + if _, ok := g.componentSchemaRefs[ref.Ref]; ok && schemas != nil { + schemas[ref.Ref] = &openapi3.SchemaRef{ + Value: ref.Value, + } + } + if strings.HasPrefix(ref.Ref, "#/components/schemas/") { + ref.Value = nil + } else { + ref.Ref = "" + } + } + return ref, nil +} + func (g *Generator) generateSchemaRefFor(parents []*jsoninfo.TypeInfo, t reflect.Type, name string, tag reflect.StructTag) (*openapi3.SchemaRef, error) { if ref := g.Types[t]; ref != nil && g.opts.schemaCustomizer == nil { g.SchemaRefs[ref]++ return ref, nil } ref, err := g.generateWithoutSaving(parents, t, name, tag) + if err != nil { + return nil, err + } if ref != nil { g.Types[t] = ref g.SchemaRefs[ref]++ } - return ref, err + return ref, nil } func getStructField(t reflect.Type, fieldInfo jsoninfo.FieldInfo) reflect.StructField { @@ -341,6 +365,7 @@ typeName = t.Name() } + g.componentSchemaRefs[typeName] = struct{}{} return openapi3.NewSchemaRef(fmt.Sprintf("#/components/schemas/%s", typeName), schema) } diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3gen/openapi3gen_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3gen/openapi3gen_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3gen/openapi3gen_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3gen/openapi3gen_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -1,29 +1,177 @@ -package openapi3gen +package openapi3gen_test import ( "encoding/json" "errors" + "fmt" "reflect" "strconv" "strings" "testing" + "time" "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/openapi3gen" "github.com/stretchr/testify/require" ) -type CyclicType0 struct { - CyclicField *CyclicType1 `json:"a"` -} -type CyclicType1 struct { - CyclicField *CyclicType0 `json:"b"` +func ExampleGenerator_SchemaRefs() { + type SomeOtherType string + type SomeStruct struct { + Bool bool `json:"bool"` + Int int `json:"int"` + Int64 int64 `json:"int64"` + Float64 float64 `json:"float64"` + String string `json:"string"` + Bytes []byte `json:"bytes"` + JSON json.RawMessage `json:"json"` + Time time.Time `json:"time"` + Slice []SomeOtherType `json:"slice"` + Map map[string]*SomeOtherType `json:"map"` + + Struct struct { + X string `json:"x"` + } `json:"struct"` + + EmptyStruct struct { + Y string + } `json:"structWithoutFields"` + + Ptr *SomeOtherType `json:"ptr"` + } + + g := openapi3gen.NewGenerator() + schemaRef, err := g.NewSchemaRefForValue(&SomeStruct{}, nil) + if err != nil { + panic(err) + } + + fmt.Printf("g.SchemaRefs: %d\n", len(g.SchemaRefs)) + var data []byte + if data, err = json.MarshalIndent(&schemaRef, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemaRef: %s\n", data) + // Output: + // g.SchemaRefs: 15 + // schemaRef: { + // "properties": { + // "bool": { + // "type": "boolean" + // }, + // "bytes": { + // "format": "byte", + // "type": "string" + // }, + // "float64": { + // "format": "double", + // "type": "number" + // }, + // "int": { + // "type": "integer" + // }, + // "int64": { + // "format": "int64", + // "type": "integer" + // }, + // "json": {}, + // "map": { + // "additionalProperties": { + // "type": "string" + // }, + // "type": "object" + // }, + // "ptr": { + // "type": "string" + // }, + // "slice": { + // "items": { + // "type": "string" + // }, + // "type": "array" + // }, + // "string": { + // "type": "string" + // }, + // "struct": { + // "properties": { + // "x": { + // "type": "string" + // } + // }, + // "type": "object" + // }, + // "structWithoutFields": {}, + // "time": { + // "format": "date-time", + // "type": "string" + // } + // }, + // "type": "object" + // } } -func TestCyclic(t *testing.T) { - schemaRef, refsMap, err := NewSchemaRefForValue(&CyclicType0{}, ThrowErrorOnCycle()) - require.IsType(t, &CycleError{}, err) - require.Nil(t, schemaRef) - require.Empty(t, refsMap) +func ExampleThrowErrorOnCycle() { + type CyclicType0 struct { + CyclicField *struct { + CyclicField *CyclicType0 `json:"b"` + } `json:"a"` + } + + schemas := make(openapi3.Schemas) + schemaRef, err := openapi3gen.NewSchemaRefForValue(&CyclicType0{}, schemas, openapi3gen.ThrowErrorOnCycle()) + if schemaRef != nil || err == nil { + panic(`With option ThrowErrorOnCycle, an error is returned when a schema reference cycle is found`) + } + if _, ok := err.(*openapi3gen.CycleError); !ok { + panic(`With option ThrowErrorOnCycle, an error of type CycleError is returned`) + } + if len(schemas) != 0 { + panic(`No references should have been collected at this point`) + } + + if schemaRef, err = openapi3gen.NewSchemaRefForValue(&CyclicType0{}, schemas); err != nil { + panic(err) + } + + var data []byte + if data, err = json.MarshalIndent(schemaRef, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemaRef: %s\n", data) + if data, err = json.MarshalIndent(schemas, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemas: %s\n", data) + // Output: + // schemaRef: { + // "properties": { + // "a": { + // "properties": { + // "b": { + // "$ref": "#/components/schemas/CyclicType0" + // } + // }, + // "type": "object" + // } + // }, + // "type": "object" + // } + // schemas: { + // "CyclicType0": { + // "properties": { + // "a": { + // "properties": { + // "b": { + // "$ref": "#/components/schemas/CyclicType0" + // } + // }, + // "type": "object" + // } + // }, + // "type": "object" + // } + // } } func TestExportedNonTagged(t *testing.T) { @@ -34,7 +182,7 @@ EvenAYaml string `yaml:"even_a_yaml"` } - schemaRef, _, err := NewSchemaRefForValue(&Bla{}, UseAllExportedFields()) + schemaRef, err := openapi3gen.NewSchemaRefForValue(&Bla{}, nil, openapi3gen.UseAllExportedFields()) require.NoError(t, err) require.Equal(t, &openapi3.SchemaRef{Value: &openapi3.Schema{ Type: "object", @@ -45,21 +193,34 @@ }}}, schemaRef) } -func TestExportUint(t *testing.T) { +func ExampleUseAllExportedFields() { type UnsignedIntStruct struct { UnsignedInt uint `json:"uint"` } - schemaRef, _, err := NewSchemaRefForValue(&UnsignedIntStruct{}, UseAllExportedFields()) - require.NoError(t, err) - require.Equal(t, &openapi3.SchemaRef{Value: &openapi3.Schema{ - Type: "object", - Properties: map[string]*openapi3.SchemaRef{ - "uint": {Value: &openapi3.Schema{Type: "integer", Min: &zeroInt}}, - }}}, schemaRef) + schemaRef, err := openapi3gen.NewSchemaRefForValue(&UnsignedIntStruct{}, nil, openapi3gen.UseAllExportedFields()) + if err != nil { + panic(err) + } + + var data []byte + if data, err = json.MarshalIndent(schemaRef, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemaRef: %s\n", data) + // Output: + // schemaRef: { + // "properties": { + // "uint": { + // "minimum": 0, + // "type": "integer" + // } + // }, + // "type": "object" + // } } -func TestEmbeddedStructs(t *testing.T) { +func ExampleGenerator_GenerateSchemaRef() { type EmbeddedStruct struct { ID string } @@ -76,17 +237,31 @@ }, } - generator := NewGenerator(UseAllExportedFields()) + generator := openapi3gen.NewGenerator(openapi3gen.UseAllExportedFields()) schemaRef, err := generator.GenerateSchemaRef(reflect.TypeOf(instance)) - require.NoError(t, err) - - var ok bool - _, ok = schemaRef.Value.Properties["Name"] - require.Equal(t, true, ok) + if err != nil { + panic(err) + } - _, ok = schemaRef.Value.Properties["ID"] - require.Equal(t, true, ok) + var data []byte + if data, err = json.MarshalIndent(schemaRef.Value.Properties["Name"].Value, "", " "); err != nil { + panic(err) + } + fmt.Printf(`schemaRef.Value.Properties["Name"].Value: %s`, data) + fmt.Println() + if data, err = json.MarshalIndent(schemaRef.Value.Properties["ID"].Value, "", " "); err != nil { + panic(err) + } + fmt.Printf(`schemaRef.Value.Properties["ID"].Value: %s`, data) + fmt.Println() + // Output: + // schemaRef.Value.Properties["Name"].Value: { + // "type": "string" + // } + // schemaRef.Value.Properties["ID"].Value: { + // "type": "string" + // } } func TestEmbeddedPointerStructs(t *testing.T) { @@ -106,7 +281,7 @@ }, } - generator := NewGenerator(UseAllExportedFields()) + generator := openapi3gen.NewGenerator(openapi3gen.UseAllExportedFields()) schemaRef, err := generator.GenerateSchemaRef(reflect.TypeOf(instance)) require.NoError(t, err) @@ -132,7 +307,7 @@ MapCycle: nil, } - generator := NewGenerator(UseAllExportedFields()) + generator := openapi3gen.NewGenerator(openapi3gen.UseAllExportedFields()) schemaRef, err := generator.GenerateSchemaRef(reflect.TypeOf(instance)) require.NoError(t, err) @@ -149,7 +324,7 @@ require.Equal(t, "#/components/schemas/ObjectDiff", schemaRef.Value.Properties["MapCycle"].Value.AdditionalProperties.Ref) } -func TestSchemaCustomizer(t *testing.T) { +func ExampleSchemaCustomizer() { type NestedInnerBla struct { Enum1Field string `json:"enum1" myenumtag:"a,b"` } @@ -169,8 +344,7 @@ EnumField3 string `json:"enum3" myenumtag:"e,f"` } - schemaRef, _, err := NewSchemaRefForValue(&Bla{}, UseAllExportedFields(), SchemaCustomizer(func(name string, ft reflect.Type, tag reflect.StructTag, schema *openapi3.Schema) error { - t.Logf("Field=%s,Tag=%s", name, tag) + customizer := openapi3gen.SchemaCustomizer(func(name string, ft reflect.Type, tag reflect.StructTag, schema *openapi3.Schema) error { if tag.Get("mymintag") != "" { minVal, err := strconv.ParseFloat(tag.Get("mymintag"), 64) if err != nil { @@ -191,58 +365,137 @@ } } return nil - })) - require.NoError(t, err) - jsonSchema, err := json.MarshalIndent(schemaRef, "", " ") - require.NoError(t, err) - require.JSONEq(t, `{ - "properties": { - "AnonStruct": { - "properties": { - "InnerFieldWithTag": { - "maximum": 50, - "minimum": -1, - "type": "integer" - }, - "InnerFieldWithoutTag": { - "type": "integer" - }, - "enum1": { - "enum": [ - "a", - "b" - ], - "type": "string" - } - }, - "type": "object" - }, - "UntaggedStringField": { - "type": "string" - }, - "enum2": { - "enum": [ - "c", - "d" - ], - "type": "string" - }, - "enum3": { - "enum": [ - "e", - "f" - ], - "type": "string" - } - }, - "type": "object" -}`, string(jsonSchema)) + }) + + schemaRef, err := openapi3gen.NewSchemaRefForValue(&Bla{}, nil, openapi3gen.UseAllExportedFields(), customizer) + if err != nil { + panic(err) + } + + var data []byte + if data, err = json.MarshalIndent(schemaRef, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemaRef: %s\n", data) + // Output: + // schemaRef: { + // "properties": { + // "AnonStruct": { + // "properties": { + // "InnerFieldWithTag": { + // "maximum": 50, + // "minimum": -1, + // "type": "integer" + // }, + // "InnerFieldWithoutTag": { + // "type": "integer" + // }, + // "enum1": { + // "enum": [ + // "a", + // "b" + // ], + // "type": "string" + // } + // }, + // "type": "object" + // }, + // "UntaggedStringField": { + // "type": "string" + // }, + // "enum2": { + // "enum": [ + // "c", + // "d" + // ], + // "type": "string" + // }, + // "enum3": { + // "enum": [ + // "e", + // "f" + // ], + // "type": "string" + // } + // }, + // "type": "object" + // } } func TestSchemaCustomizerError(t *testing.T) { - type Bla struct{} - _, _, err := NewSchemaRefForValue(&Bla{}, UseAllExportedFields(), SchemaCustomizer(func(name string, ft reflect.Type, tag reflect.StructTag, schema *openapi3.Schema) error { + customizer := openapi3gen.SchemaCustomizer(func(name string, ft reflect.Type, tag reflect.StructTag, schema *openapi3.Schema) error { return errors.New("test error") - })) + }) + + type Bla struct{} + _, err := openapi3gen.NewSchemaRefForValue(&Bla{}, nil, openapi3gen.UseAllExportedFields(), customizer) require.EqualError(t, err, "test error") } + +func ExampleNewSchemaRefForValue_recursive() { + type RecursiveType struct { + Field1 string `json:"field1"` + Field2 string `json:"field2"` + Field3 string `json:"field3"` + Components []*RecursiveType `json:"children,omitempty"` + } + + schemas := make(openapi3.Schemas) + schemaRef, err := openapi3gen.NewSchemaRefForValue(&RecursiveType{}, schemas) + if err != nil { + panic(err) + } + + var data []byte + if data, err = json.MarshalIndent(&schemas, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemas: %s\n", data) + if data, err = json.MarshalIndent(&schemaRef, "", " "); err != nil { + panic(err) + } + fmt.Printf("schemaRef: %s\n", data) + // Output: + // schemas: { + // "RecursiveType": { + // "properties": { + // "children": { + // "items": { + // "$ref": "#/components/schemas/RecursiveType" + // }, + // "type": "array" + // }, + // "field1": { + // "type": "string" + // }, + // "field2": { + // "type": "string" + // }, + // "field3": { + // "type": "string" + // } + // }, + // "type": "object" + // } + // } + // schemaRef: { + // "properties": { + // "children": { + // "items": { + // "$ref": "#/components/schemas/RecursiveType" + // }, + // "type": "array" + // }, + // "field1": { + // "type": "string" + // }, + // "field2": { + // "type": "string" + // }, + // "field3": { + // "type": "string" + // } + // }, + // "type": "object" + // } +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/openapi3gen/simple_test.go golang-github-getkin-kin-openapi-0.85.0/openapi3gen/simple_test.go --- golang-github-getkin-kin-openapi-0.80.0/openapi3gen/simple_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/openapi3gen/simple_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -36,15 +36,11 @@ ) func Example() { - schemaRef, refsMap, err := openapi3gen.NewSchemaRefForValue(&SomeStruct{}) + schemaRef, err := openapi3gen.NewSchemaRefForValue(&SomeStruct{}, nil) if err != nil { panic(err) } - if len(refsMap) != 15 { - panic(fmt.Sprintf("unintended len(refsMap) = %d", len(refsMap))) - } - data, err := json.MarshalIndent(schemaRef, "", " ") if err != nil { panic(err) diff -Nru golang-github-getkin-kin-openapi-0.80.0/README.md golang-github-getkin-kin-openapi-0.85.0/README.md --- golang-github-getkin-kin-openapi-0.80.0/README.md 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/README.md 2021-12-03 09:49:46.000000000 +0000 @@ -193,6 +193,11 @@ ## Sub-v0 breaking API changes +### v0.84.0 +* The prototype of `openapi3gen.NewSchemaRefForValue` changed: + * It no longer returns a map but that is still accessible under the field `(*Generator).SchemaRefs`. + * It now takes in an additional argument (basically `doc.Components.Schemas`) which gets written to so `$ref` cycles can be properly handled. + ### v0.61.0 * Renamed `openapi2.Swagger` to `openapi2.T`. * Renamed `openapi2conv.FromV3Swagger` to `openapi2conv.FromV3`. diff -Nru golang-github-getkin-kin-openapi-0.80.0/routers/gorillamux/router.go golang-github-getkin-kin-openapi-0.85.0/routers/gorillamux/router.go --- golang-github-getkin-kin-openapi-0.80.0/routers/gorillamux/router.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/routers/gorillamux/router.go 2021-12-03 09:49:46.000000000 +0000 @@ -35,10 +35,16 @@ servers := make([]srv, 0, len(doc.Servers)) for _, server := range doc.Servers { serverURL := server.URL - scheme0 := strings.Split(serverURL, "://")[0] - schemes := permutePart(scheme0, server) - - u, err := url.Parse(bEncode(strings.Replace(serverURL, scheme0+"://", schemes[0]+"://", 1))) + var schemes []string + var u *url.URL + var err error + if strings.Contains(serverURL, "://") { + scheme0 := strings.Split(serverURL, "://")[0] + schemes = permutePart(scheme0, server) + u, err = url.Parse(bEncode(strings.Replace(serverURL, scheme0+"://", schemes[0]+"://", 1))) + } else { + u, err = url.Parse(bEncode(serverURL)) + } if err != nil { return nil, err } diff -Nru golang-github-getkin-kin-openapi-0.80.0/routers/gorillamux/router_test.go golang-github-getkin-kin-openapi-0.85.0/routers/gorillamux/router_test.go --- golang-github-getkin-kin-openapi-0.80.0/routers/gorillamux/router_test.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/routers/gorillamux/router_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -219,3 +219,26 @@ }) require.NoError(t, err) } + +func TestRelativeURL(t *testing.T) { + helloGET := &openapi3.Operation{Responses: openapi3.NewResponses()} + doc := &openapi3.T{ + Servers: openapi3.Servers{ + &openapi3.Server{ + URL: "/api/v1", + }, + }, + Paths: openapi3.Paths{ + "/hello": &openapi3.PathItem{ + Get: helloGET, + }, + }, + } + router, err := NewRouter(doc) + require.NoError(t, err) + req, err := http.NewRequest(http.MethodGet, "https://example.com/api/v1/hello", nil) + require.NoError(t, err) + route, _, err := router.FindRoute(req) + require.NoError(t, err) + require.Equal(t, "/hello", route.Path) +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/routers/legacy/issue444_test.go golang-github-getkin-kin-openapi-0.85.0/routers/legacy/issue444_test.go --- golang-github-getkin-kin-openapi-0.80.0/routers/legacy/issue444_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/routers/legacy/issue444_test.go 2021-12-03 09:49:46.000000000 +0000 @@ -0,0 +1,58 @@ +package legacy_test + +import ( + "bytes" + "context" + "net/http/httptest" + "testing" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/openapi3filter" + legacyrouter "github.com/getkin/kin-openapi/routers/legacy" + "github.com/stretchr/testify/require" +) + +func TestIssue444(t *testing.T) { + loader := openapi3.NewLoader() + oas, err := loader.LoadFromData([]byte(` +openapi: '3.0.0' +info: + title: API + version: 1.0.0 +paths: + '/path': + post: + requestBody: + required: true + content: + application/x-yaml: + schema: + type: object + responses: + '200': + description: x + content: + application/json: + schema: + type: string +`)) + require.NoError(t, err) + router, err := legacyrouter.NewRouter(oas) + require.NoError(t, err) + + r := httptest.NewRequest("POST", "/path", bytes.NewReader([]byte(` +foo: bar +`))) + r.Header.Set("Content-Type", "application/x-yaml") + + openapi3.SchemaErrorDetailsDisabled = true + route, pathParams, err := router.FindRoute(r) + require.NoError(t, err) + reqValidationInput := &openapi3filter.RequestValidationInput{ + Request: r, + PathParams: pathParams, + Route: route, + } + err = openapi3filter.ValidateRequest(context.Background(), reqValidationInput) + require.NoError(t, err) +} diff -Nru golang-github-getkin-kin-openapi-0.80.0/routers/legacy/router.go golang-github-getkin-kin-openapi-0.85.0/routers/legacy/router.go --- golang-github-getkin-kin-openapi-0.80.0/routers/legacy/router.go 2021-10-21 15:42:22.000000000 +0000 +++ golang-github-getkin-kin-openapi-0.85.0/routers/legacy/router.go 2021-12-03 09:49:46.000000000 +0000 @@ -125,7 +125,10 @@ } } pathParams = make(map[string]string, 8) - paramNames, _ := server.ParameterNames() + paramNames, err := server.ParameterNames() + if err != nil { + return nil, nil, err + } for i, value := range paramValues { name := paramNames[i] pathParams[name] = value