From d4229f79e035e269852f9785767a61bf1e8cbcec Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Wed, 15 Jul 2020 11:14:54 +0200 Subject: [PATCH 1/3] reproduce Signed-off-by: Pierre Fenoll --- openapi3/swagger_loader_issue235_test.go | 16 ++++++++++++++++ openapi3/testdata/issue235.spec0.yml | 7 +++++++ openapi3/testdata/issue235.spec1.yml | 12 ++++++++++++ openapi3/testdata/issue235.spec2.yml | 7 +++++++ 4 files changed, 42 insertions(+) create mode 100644 openapi3/swagger_loader_issue235_test.go create mode 100644 openapi3/testdata/issue235.spec0.yml create mode 100644 openapi3/testdata/issue235.spec1.yml create mode 100644 openapi3/testdata/issue235.spec2.yml diff --git a/openapi3/swagger_loader_issue235_test.go b/openapi3/swagger_loader_issue235_test.go new file mode 100644 index 000000000..32d101d0e --- /dev/null +++ b/openapi3/swagger_loader_issue235_test.go @@ -0,0 +1,16 @@ +package openapi3 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIssue235(t *testing.T) { + loader := NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + doc, err := loader.LoadSwaggerFromFile("testdata/issue235.spec0.yml") + require.NoError(t, err) + err = doc.Validate(loader.Context) + require.NoError(t, err) +} diff --git a/openapi3/testdata/issue235.spec0.yml b/openapi3/testdata/issue235.spec0.yml new file mode 100644 index 000000000..e0bd52d44 --- /dev/null +++ b/openapi3/testdata/issue235.spec0.yml @@ -0,0 +1,7 @@ +components: + schemas: + ObjectA: + type: object + properties: + object_b: + $ref: 'issue235.spec0.yml#/components/schemas/ObjectD' diff --git a/openapi3/testdata/issue235.spec1.yml b/openapi3/testdata/issue235.spec1.yml new file mode 100644 index 000000000..a1bc67906 --- /dev/null +++ b/openapi3/testdata/issue235.spec1.yml @@ -0,0 +1,12 @@ +components: + schemas: + ObjectD: + type: object + properties: + result: + $ref: '#/components/schemas/ObjectE' + + ObjectE: + properties: + name: + $ref: issue235.spec2.yml#/components/schemas/ObjectX diff --git a/openapi3/testdata/issue235.spec2.yml b/openapi3/testdata/issue235.spec2.yml new file mode 100644 index 000000000..b0bcb0fa2 --- /dev/null +++ b/openapi3/testdata/issue235.spec2.yml @@ -0,0 +1,7 @@ +components: + schemas: + ObjectX: + type: object + properties: + name: + type: string From 55d3a2706f05a2170403e2c723f857abe0fc4d1f Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Wed, 15 Jul 2020 18:35:11 +0200 Subject: [PATCH 2/3] nits Signed-off-by: Pierre Fenoll --- openapi3/swagger_loader.go | 28 ++++++++++++++-------------- openapi3/testdata/issue235.spec0.yml | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/openapi3/swagger_loader.go b/openapi3/swagger_loader.go index cb0cda438..71216a28a 100644 --- a/openapi3/swagger_loader.go +++ b/openapi3/swagger_loader.go @@ -166,9 +166,11 @@ func (swaggerLoader *SwaggerLoader) loadSwaggerFromDataWithPathInternal(data []b } func (swaggerLoader *SwaggerLoader) ResolveRefsIn(swagger *Swagger, path *url.URL) (err error) { - swaggerLoader.visited = make(map[interface{}]struct{}) + if swaggerLoader.visited == nil { + swaggerLoader.visited = make(map[interface{}]struct{}) + } if swaggerLoader.visitedFiles == nil { - swaggerLoader.visitedFiles = make(map[string]struct{}) + swaggerLoader.reset() } // Visit all components @@ -232,7 +234,7 @@ func join(basePath *url.URL, relativePath *url.URL) (*url.URL, error) { } newPath, err := copyURL(basePath) if err != nil { - return nil, fmt.Errorf("Can't copy path: '%s'", basePath.String()) + return nil, fmt.Errorf("cannot copy path: %q", basePath.String()) } newPath.Path = path.Join(path.Dir(newPath.Path), relativePath.Path) return newPath, nil @@ -274,9 +276,7 @@ func (swaggerLoader *SwaggerLoader) resolveComponent(swagger *Swagger, ref strin cursor = swagger for _, pathPart := range strings.Split(fragment[1:], "/") { - - pathPart = strings.Replace(pathPart, "~1", "/", -1) - pathPart = strings.Replace(pathPart, "~0", "~", -1) + pathPart = unescapeRefString(pathPart) if cursor, err = drillIntoSwaggerField(cursor, pathPart); err != nil { return nil, nil, fmt.Errorf("Failed to resolve '%s' in fragment in URI: '%s': %v", ref, pathPart, err.Error()) @@ -369,7 +369,7 @@ func (swaggerLoader *SwaggerLoader) resolveHeaderRef(swagger *Swagger, component if component == nil { return errors.New("invalid header: value MUST be a JSON object") } - if ref := component.Ref; len(ref) > 0 { + if ref := component.Ref; ref != "" { if isSingleRefElement(ref) { var header Header if err := swaggerLoader.loadSingleElementFromURI(ref, path, &header); err != nil { @@ -416,7 +416,7 @@ func (swaggerLoader *SwaggerLoader) resolveParameterRef(swagger *Swagger, compon return errors.New("invalid parameter: value MUST be a JSON object") } ref := component.Ref - if len(ref) > 0 { + if ref != "" { if isSingleRefElement(ref) { var param Parameter if err := swaggerLoader.loadSingleElementFromURI(ref, documentPath, ¶m); err != nil { @@ -477,7 +477,7 @@ func (swaggerLoader *SwaggerLoader) resolveRequestBodyRef(swagger *Swagger, comp if component == nil { return errors.New("invalid requestBody: value MUST be a JSON object") } - if ref := component.Ref; len(ref) > 0 { + if ref := component.Ref; ref != "" { if isSingleRefElement(ref) { var requestBody RequestBody if err := swaggerLoader.loadSingleElementFromURI(ref, path, &requestBody); err != nil { @@ -532,7 +532,7 @@ func (swaggerLoader *SwaggerLoader) resolveResponseRef(swagger *Swagger, compone return errors.New("invalid response: value MUST be a JSON object") } ref := component.Ref - if len(ref) > 0 { + if ref != "" { if isSingleRefElement(ref) { var resp Response @@ -607,7 +607,7 @@ func (swaggerLoader *SwaggerLoader) resolveSchemaRef(swagger *Swagger, component return errors.New("invalid schema: value MUST be a JSON object") } ref := component.Ref - if len(ref) > 0 { + if ref != "" { if isSingleRefElement(ref) { var schema Schema if err := swaggerLoader.loadSingleElementFromURI(ref, documentPath, &schema); err != nil { @@ -692,7 +692,7 @@ func (swaggerLoader *SwaggerLoader) resolveSecuritySchemeRef(swagger *Swagger, c if component == nil { return errors.New("invalid securityScheme: value MUST be a JSON object") } - if ref := component.Ref; len(ref) > 0 { + if ref := component.Ref; ref != "" { if isSingleRefElement(ref) { var scheme SecurityScheme if err := swaggerLoader.loadSingleElementFromURI(ref, path, &scheme); err != nil { @@ -729,7 +729,7 @@ func (swaggerLoader *SwaggerLoader) resolveExampleRef(swagger *Swagger, componen if component == nil { return errors.New("invalid example: value MUST be a JSON object") } - if ref := component.Ref; len(ref) > 0 { + if ref := component.Ref; ref != "" { if isSingleRefElement(ref) { var example Example if err := swaggerLoader.loadSingleElementFromURI(ref, path, &example); err != nil { @@ -766,7 +766,7 @@ func (swaggerLoader *SwaggerLoader) resolveLinkRef(swagger *Swagger, component * if component == nil { return errors.New("invalid link: value MUST be a JSON object") } - if ref := component.Ref; len(ref) > 0 { + if ref := component.Ref; ref != "" { if isSingleRefElement(ref) { var link Link if err := swaggerLoader.loadSingleElementFromURI(ref, path, &link); err != nil { diff --git a/openapi3/testdata/issue235.spec0.yml b/openapi3/testdata/issue235.spec0.yml index e0bd52d44..91ff6c4e3 100644 --- a/openapi3/testdata/issue235.spec0.yml +++ b/openapi3/testdata/issue235.spec0.yml @@ -1,4 +1,21 @@ +openapi: 3.0.0 +info: + title: 'OAI Specification in YAML' + version: 0.0.1 +paths: + /test: + get: + responses: + "200": + $ref: '#/components/responses/GetTestOK' components: + responses: + GetTestOK: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObjectA' schemas: ObjectA: type: object From f4ce35c4b3791a5e7acbefd4abf67b34985e140c Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Wed, 15 Jul 2020 18:45:52 +0200 Subject: [PATCH 3/3] add TODO Signed-off-by: Pierre Fenoll --- openapi3/swagger_loader_issue235_test.go | 11 ++++++++++- openapi3/testdata/issue235.spec0-typo.yml | 24 +++++++++++++++++++++++ openapi3/testdata/issue235.spec0.yml | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 openapi3/testdata/issue235.spec0-typo.yml diff --git a/openapi3/swagger_loader_issue235_test.go b/openapi3/swagger_loader_issue235_test.go index 32d101d0e..79515c12d 100644 --- a/openapi3/swagger_loader_issue235_test.go +++ b/openapi3/swagger_loader_issue235_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestIssue235(t *testing.T) { +func TestIssue235OK(t *testing.T) { loader := NewSwaggerLoader() loader.IsExternalRefsAllowed = true doc, err := loader.LoadSwaggerFromFile("testdata/issue235.spec0.yml") @@ -14,3 +14,12 @@ func TestIssue235(t *testing.T) { err = doc.Validate(loader.Context) require.NoError(t, err) } + +func TestIssue235CircularDep(t *testing.T) { + t.Skip("TODO: return an error on circular dependencies between external files of a spec") + loader := NewSwaggerLoader() + loader.IsExternalRefsAllowed = true + doc, err := loader.LoadSwaggerFromFile("testdata/issue235.spec0-typo.yml") + require.Nil(t, doc) + require.Error(t, err) +} diff --git a/openapi3/testdata/issue235.spec0-typo.yml b/openapi3/testdata/issue235.spec0-typo.yml new file mode 100644 index 000000000..543600620 --- /dev/null +++ b/openapi3/testdata/issue235.spec0-typo.yml @@ -0,0 +1,24 @@ +openapi: 3.0.0 +info: + title: 'OAI Specification in YAML' + version: 0.0.1 +paths: + /test: + get: + responses: + "200": + $ref: '#/components/responses/GetTestOK' +components: + responses: + GetTestOK: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObjectA' + schemas: + ObjectA: + type: object + properties: + object_b: + $ref: 'issue235.spec0-typo.yml#/components/schemas/ObjectD' diff --git a/openapi3/testdata/issue235.spec0.yml b/openapi3/testdata/issue235.spec0.yml index 91ff6c4e3..d9236aaec 100644 --- a/openapi3/testdata/issue235.spec0.yml +++ b/openapi3/testdata/issue235.spec0.yml @@ -21,4 +21,4 @@ components: type: object properties: object_b: - $ref: 'issue235.spec0.yml#/components/schemas/ObjectD' + $ref: 'issue235.spec1.yml#/components/schemas/ObjectD'