Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 13 additions & 8 deletions openapi3/swagger_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ func clearResolvedExternalRef(rr RefOrValue) {
// ClearResolvedExternalRefs Recursively iterate over the swagger structure, resetting <Type>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 <Type>Ref struct, reset the reference if it's remote and resolved
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion openapi3/swagger_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
6 changes: 6 additions & 0 deletions openapi3/testdata/components.openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions openapi3/testdata/test.refcache.openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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