diff --git a/go.mod b/go.mod index 5fbd1f88a..4e9ff5813 100644 --- a/go.mod +++ b/go.mod @@ -8,3 +8,5 @@ require ( gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v2 v2.2.2 // indirect ) + +go 1.13 diff --git a/openapi3/swagger_utils.go b/openapi3/swagger_utils.go index 983bdc207..0877982b4 100644 --- a/openapi3/swagger_utils.go +++ b/openapi3/swagger_utils.go @@ -25,10 +25,15 @@ func clearResolvedExternalRef(rr RefOrValue) { // ClearResolvedExternalRefs Recursively iterate over the swagger structure, resetting Ref structs where // the reference is remote and was resolved func ClearResolvedExternalRefs(swagger *Swagger) { - resetExternalRef(reflect.ValueOf(swagger)) + visited := map[reflect.Value]struct{}{} + resetExternalRef(reflect.ValueOf(swagger), visited) } -func resetExternalRef(c reflect.Value) { +func resetExternalRef(c reflect.Value, visited map[reflect.Value]struct{}) { + if _, ok := visited[c]; ok { + return + } + visited[c] = struct{}{} switch c.Kind() { // If it is a struct, check if it's the desired type first before drilling into fields // Further if this is a Ref struct, reset the reference if it's remote and resolved @@ -39,28 +44,28 @@ func resetExternalRef(c reflect.Value) { clearResolvedExternalRef(rov) } } - for i := 0; i < c.NumField(); i += 1 { - resetExternalRef(c.Field(i)) + for i := 0; i < c.NumField(); i++ { + resetExternalRef(c.Field(i), visited) } // If it is a pointer or interface we need to unwrap and call once again case reflect.Interface, reflect.Ptr: c2 := c.Elem() if c2.IsValid() { - resetExternalRef(c2) + resetExternalRef(c2, visited) } // If it is a slice we iterate over each each element case reflect.Slice: - for i := 0; i < c.Len(); i += 1 { - resetExternalRef(c.Index(i)) + for i := 0; i < c.Len(); i++ { + resetExternalRef(c.Index(i), visited) } // If it is a map we iterate over each of the key,value pairs case reflect.Map: mi := c.MapRange() for mi.Next() { - resetExternalRef(mi.Value()) + resetExternalRef(mi.Value(), visited) } // And everything else will simply be ignored diff --git a/openapi3/swagger_utils_test.go b/openapi3/swagger_utils_test.go index d3c56c879..0a3963a9f 100644 --- a/openapi3/swagger_utils_test.go +++ b/openapi3/swagger_utils_test.go @@ -24,7 +24,8 @@ func TestResettingExternalRefs(t *testing.T) { openapi3.ClearResolvedExternalRefs(doc) - for _, s := range []string{"ref1", "ref2", "ref3", "ref4", "ref5", "ref6"} { + fields := []string{"ref1", "ref2", "ref3", "ref4", "ref5", "ref6", "ref7", "ref8", "ref9"} + for _, s := range fields { sr := doc.Components.Schemas["AnotherTestSchema"].Value.Properties[s] require.True(t, sr.IsValue()) require.False(t, sr.Resolved()) diff --git a/openapi3/testdata/components.openapi.yml b/openapi3/testdata/components.openapi.yml index 6c307423b..3d89390d0 100644 --- a/openapi3/testdata/components.openapi.yml +++ b/openapi3/testdata/components.openapi.yml @@ -10,6 +10,12 @@ components: type: string CustomTestSchema: "$ref": "#/components/schemas/Name" + GraphTestSchema: + properties: + children: + type: array + items: + "$ref": "#/components/schemas/GraphTestSchema" responses: Name: description: description diff --git a/openapi3/testdata/test.refcache.openapi.yml b/openapi3/testdata/test.refcache.openapi.yml index da7a4f97d..bcf0d480d 100644 --- a/openapi3/testdata/test.refcache.openapi.yml +++ b/openapi3/testdata/test.refcache.openapi.yml @@ -25,3 +25,6 @@ components: "$ref": http://localhost:7965/components3.openapi.yml#/components/schemas/NestedRefToComp2Schema ref8: "$ref": http://localhost:7965/components.openapi.yml#/components/schemas/CustomTestSchema + ref9: + "$ref": http://localhost:7965/components.openapi.yml#/components/schemas/GraphTestSchema +