Skip to content

Commit

Permalink
Fix resolveSchemaRef() to load correctly an other spec. file refere…
Browse files Browse the repository at this point in the history
…nced by `$ref` (#583)
  • Loading branch information
masu-mi committed Aug 29, 2022
1 parent 00d1ae8 commit 8154829
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
82 changes: 82 additions & 0 deletions openapi3/load_cicular_ref_with_external_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//go:build go1.16
// +build go1.16

package openapi3_test

import (
"embed"
"net/url"
"testing"

"github.com/stretchr/testify/require"

"github.com/getkin/kin-openapi/openapi3"
)

//go:embed testdata/circularRef/*
var circularResSpecs embed.FS

func TestLoadCircularRefFromFile(t *testing.T) {
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
loader.ReadFromURIFunc = func(loader *openapi3.Loader, uri *url.URL) ([]byte, error) {
return circularResSpecs.ReadFile(uri.Path)
}

got, err := loader.LoadFromFile("testdata/circularRef/base.yml")
if err != nil {
t.Error(err)
}

foo := &openapi3.SchemaRef{
Ref: "",
Value: &openapi3.Schema{
ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
Properties: map[string]*openapi3.SchemaRef{
"foo2": { // reference to an external file
Ref: "other.yml#/components/schemas/Foo2",
Value: &openapi3.Schema{
ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
Properties: map[string]*openapi3.SchemaRef{
"id": {
Value: &openapi3.Schema{
Type: "string",
ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
}},
},
},
},
},
},
}
bar := &openapi3.SchemaRef{
Ref: "",
Value: &openapi3.Schema{
ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
Properties: map[string]*openapi3.SchemaRef{},
},
}
// circular reference
bar.Value.Properties["foo"] = &openapi3.SchemaRef{Ref: "#/components/schemas/Foo", Value: foo.Value}
foo.Value.Properties["bar"] = &openapi3.SchemaRef{Ref: "#/components/schemas/Bar", Value: bar.Value}

want := &openapi3.T{
OpenAPI: "3.0.3",
Info: &openapi3.Info{
Title: "Recursive cyclic refs example",
Version: "1.0",

ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
},
Components: openapi3.Components{
ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
Schemas: map[string]*openapi3.SchemaRef{
"Foo": foo,
"Bar": bar,
},
},
ExtensionProps: openapi3.ExtensionProps{Extensions: map[string]interface{}{}},
}

require.Equal(t, want, got)
}
19 changes: 18 additions & 1 deletion openapi3/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,8 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
return err
}
component.Value = resolved.Value
documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
foundPath := loader.getResolvedRefPath(ref, &resolved, documentPath, componentPath)
documentPath = loader.documentPathForRecursiveRef(documentPath, foundPath)
}
}
value := component.Value
Expand Down Expand Up @@ -746,6 +747,22 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
return nil
}

func (loader *Loader) getResolvedRefPath(ref string, resolved *SchemaRef, cur, found *url.URL) string {
referencedFilename := strings.Split(ref, "#")[0]
if referencedFilename == "" {
if cur != nil {
return path.Base(cur.Path)
}
return ""
}
// ref. to external file
if resolved.Ref != "" {
return resolved.Ref
}
// found dest spec. file
return path.Dir(found.Path)[len(loader.rootDir):]
}

func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecuritySchemeRef, documentPath *url.URL) (err error) {
if component != nil && component.Value != nil {
if loader.visitedSecurityScheme == nil {
Expand Down
16 changes: 16 additions & 0 deletions openapi3/testdata/circularRef/base.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
openapi: "3.0.3"
info:
title: Recursive cyclic refs example
version: "1.0"
components:
schemas:
Foo:
properties:
foo2:
$ref: "other.yml#/components/schemas/Foo2"
bar:
$ref: "#/components/schemas/Bar"
Bar:
properties:
foo:
$ref: "#/components/schemas/Foo"
10 changes: 10 additions & 0 deletions openapi3/testdata/circularRef/other.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
openapi: "3.0.3"
info:
title: Recursive cyclic refs example
version: "1.0"
components:
schemas:
Foo2:
properties:
id:
type: string

0 comments on commit 8154829

Please sign in to comment.