diff --git a/openapi3/loader.go b/openapi3/loader.go index 1ab693b0f..cb8ee998b 100644 --- a/openapi3/loader.go +++ b/openapi3/loader.go @@ -745,6 +745,10 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat foundPath := loader.getResolvedRefPath(ref, &resolved, documentPath, componentPath) documentPath = loader.documentPathForRecursiveRef(documentPath, foundPath) } + if loader.visitedSchema == nil { + loader.visitedSchema = make(map[*Schema]struct{}) + } + loader.visitedSchema[component.Value] = struct{}{} } value := component.Value if value == nil { diff --git a/openapi3/loader_test.go b/openapi3/loader_test.go index 684e1b44d..e792767fd 100644 --- a/openapi3/loader_test.go +++ b/openapi3/loader_test.go @@ -263,6 +263,29 @@ func TestLoadWithReferenceInReference(t *testing.T) { require.Equal(t, "string", doc.Paths["/api/test/ref/in/ref"].Post.RequestBody.Value.Content["application/json"].Schema.Value.Properties["definition_reference"].Value.Type) } +func TestLoadWithRecursiveReferenceInLocalReferenceInParentSubdir(t *testing.T) { + loader := NewLoader() + loader.IsExternalRefsAllowed = true + doc, err := loader.LoadFromFile("testdata/refInLocalRefInParentsSubdir/spec/openapi.json") + require.NoError(t, err) + require.NotNil(t, doc) + err = doc.Validate(loader.Context) + require.NoError(t, err) + require.Equal(t, "object", doc.Paths["/api/test/ref/in/ref"].Post.RequestBody.Value.Content["application/json"].Schema.Value.Properties["definition_reference"].Value.Type) +} + +func TestLoadWithRecursiveReferenceInRefrerenceInLocalReference(t *testing.T) { + loader := NewLoader() + loader.IsExternalRefsAllowed = true + doc, err := loader.LoadFromFile("testdata/refInLocalRef/openapi.json") + require.NoError(t, err) + require.NotNil(t, doc) + err = doc.Validate(loader.Context) + require.NoError(t, err) + require.Equal(t, "integer", doc.Paths["/api/test/ref/in/ref"].Post.RequestBody.Value.Content["application/json"].Schema.Value.Properties["data"].Value.Properties["definition_reference"].Value.Properties["ref_prop_part"].Value.Properties["idPart"].Value.Type) + require.Equal(t, "int64", doc.Paths["/api/test/ref/in/ref"].Post.RequestBody.Value.Content["application/json"].Schema.Value.Properties["data"].Value.Properties["definition_reference"].Value.Properties["ref_prop_part"].Value.Properties["idPart"].Value.Format) +} + func TestLoadWithReferenceInReferenceInProperty(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true diff --git a/openapi3/testdata/refInLocalRef/messages/data.json b/openapi3/testdata/refInLocalRef/messages/data.json new file mode 100644 index 000000000..cfdc18efb --- /dev/null +++ b/openapi3/testdata/refInLocalRef/messages/data.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "ref_prop_part": { + "$ref": "./dataPart.json" + } + } +} diff --git a/openapi3/testdata/refInLocalRef/messages/dataPart.json b/openapi3/testdata/refInLocalRef/messages/dataPart.json new file mode 100644 index 000000000..9ecb5850a --- /dev/null +++ b/openapi3/testdata/refInLocalRef/messages/dataPart.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "idPart": { + "type": "integer", + "format": "int64" + } + } +} diff --git a/openapi3/testdata/refInLocalRef/messages/request.json b/openapi3/testdata/refInLocalRef/messages/request.json new file mode 100644 index 000000000..7225ff190 --- /dev/null +++ b/openapi3/testdata/refInLocalRef/messages/request.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "required": [ + "definition_reference" + ], + "properties": { + "definition_reference": { + "$ref": "./data.json" + } + } +} diff --git a/openapi3/testdata/refInLocalRef/messages/response.json b/openapi3/testdata/refInLocalRef/messages/response.json new file mode 100644 index 000000000..b636f528b --- /dev/null +++ b/openapi3/testdata/refInLocalRef/messages/response.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + } + } +} diff --git a/openapi3/testdata/refInLocalRef/openapi.json b/openapi3/testdata/refInLocalRef/openapi.json new file mode 100644 index 000000000..f0c9915c7 --- /dev/null +++ b/openapi3/testdata/refInLocalRef/openapi.json @@ -0,0 +1,46 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Reference in reference example", + "version": "1.0.0" + }, + "paths": { + "/api/test/ref/in/ref": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties" : { + "data": { + "$ref": "#/components/schemas/Request" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "messages/response.json" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Request": { + "$ref": "messages/request.json" + } + } + } +} diff --git a/openapi3/testdata/refInLocalRefInParentsSubdir/messages/data.json b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/data.json new file mode 100644 index 000000000..cfdc18efb --- /dev/null +++ b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/data.json @@ -0,0 +1,12 @@ +{ + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "ref_prop_part": { + "$ref": "./dataPart.json" + } + } +} diff --git a/openapi3/testdata/refInLocalRefInParentsSubdir/messages/dataPart.json b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/dataPart.json new file mode 100644 index 000000000..9ecb5850a --- /dev/null +++ b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/dataPart.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "idPart": { + "type": "integer", + "format": "int64" + } + } +} diff --git a/openapi3/testdata/refInLocalRefInParentsSubdir/messages/request.json b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/request.json new file mode 100644 index 000000000..7225ff190 --- /dev/null +++ b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/request.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "required": [ + "definition_reference" + ], + "properties": { + "definition_reference": { + "$ref": "./data.json" + } + } +} diff --git a/openapi3/testdata/refInLocalRefInParentsSubdir/messages/response.json b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/response.json new file mode 100644 index 000000000..b636f528b --- /dev/null +++ b/openapi3/testdata/refInLocalRefInParentsSubdir/messages/response.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + } + } +} diff --git a/openapi3/testdata/refInLocalRefInParentsSubdir/spec/openapi.json b/openapi3/testdata/refInLocalRefInParentsSubdir/spec/openapi.json new file mode 100644 index 000000000..0bf9bd36e --- /dev/null +++ b/openapi3/testdata/refInLocalRefInParentsSubdir/spec/openapi.json @@ -0,0 +1,46 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Reference in reference example", + "version": "1.0.0" + }, + "paths": { + "/api/test/ref/in/ref": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "../messages/request.json" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ref_prop": { + "$ref": "#/components/schemas/Data" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Data": { + "$ref": "../messages/data.json" + } + } + } +}