diff --git a/openapi2conv/openapi2_conv.go b/openapi2conv/openapi2_conv.go index ec7646d2..8aa45255 100644 --- a/openapi2conv/openapi2_conv.go +++ b/openapi2conv/openapi2_conv.go @@ -513,11 +513,7 @@ func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef { MaxProps: schema.Value.MaxProps, AllOf: make(openapi3.SchemaRefs, len(schema.Value.AllOf)), Properties: make(openapi3.Schemas), - AdditionalProperties: schema.Value.AdditionalProperties, - } - - if schema.Value.AdditionalProperties.Schema != nil { - v3Schema.AdditionalProperties.Schema.Ref = ToV3Ref(schema.Value.AdditionalProperties.Schema.Ref) + AdditionalProperties: toV3AdditionalProperties(schema.Value.AdditionalProperties), } if schema.Value.Discriminator != "" { @@ -551,6 +547,27 @@ func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef { } } +func toV3AdditionalProperties(from openapi3.AdditionalProperties) openapi3.AdditionalProperties { + return openapi3.AdditionalProperties{ + Has: from.Has, + Schema: convertRefsInV3SchemaRef(from.Schema), + } +} + +func convertRefsInV3SchemaRef(from *openapi3.SchemaRef) *openapi3.SchemaRef { + if from == nil { + return nil + } + to := *from + to.Ref = ToV3Ref(to.Ref) + if to.Value != nil { + v := *from.Value + to.Value = &v + to.Value.AdditionalProperties = toV3AdditionalProperties(to.Value.AdditionalProperties) + } + return &to +} + var ref2To3 = map[string]string{ "#/definitions/": "#/components/schemas/", "#/responses/": "#/components/responses/", diff --git a/openapi2conv/openapi2_conv_test.go b/openapi2conv/openapi2_conv_test.go index 31777215..659ccb69 100644 --- a/openapi2conv/openapi2_conv_test.go +++ b/openapi2conv/openapi2_conv_test.go @@ -65,6 +65,134 @@ func TestConvOpenAPIV2ToV3(t *testing.T) { require.JSONEq(t, exampleV3, string(data)) } +func TestConvOpenAPIV2ToV3WithAdditionalPropertiesSchemaRef(t *testing.T) { + v2 := []byte(` +{ + "basePath": "/v2", + "host": "test.example.com", + "info": { + "title": "MyAPI", + "version": "0.1" + }, + "paths": { + "/foo": { + "get": { + "operationId": "getFoo", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "returns all information", + "schema":{ + "type":"object", + "additionalProperties":{ + "$ref":"#/definitions/Foo" + } + } + } + }, + "summary": "get foo" + } + } + }, + "definitions": { + "Foo": { + "type": "object", + "properties": { + "a": { + "type": "string" + } + } + } + }, + "schemes": [ + "http" + ], + "swagger": "2.0" +} +`) + + var doc2 openapi2.T + err := json.Unmarshal(v2, &doc2) + require.NoError(t, err) + + doc3, err := ToV3(&doc2) + require.NoError(t, err) + err = doc3.Validate(context.Background()) + require.NoError(t, err) + + responseSchema := doc3.Paths.Value("/foo").Get.Responses.Value("200").Value.Content.Get("application/json").Schema.Value + require.Equal(t, &openapi3.Types{"object"}, responseSchema.Type) + require.Equal(t, "#/components/schemas/Foo", responseSchema.AdditionalProperties.Schema.Ref) +} + +func TestConvOpenAPIV2ToV3WithNestedAdditionalPropertiesSchemaRef(t *testing.T) { + v2 := []byte(` +{ + "basePath": "/v2", + "host": "test.example.com", + "info": { + "title": "MyAPI", + "version": "0.1" + }, + "paths": { + "/foo": { + "get": { + "operationId": "getFoo", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "returns all information", + "schema":{ + "type":"object", + "additionalProperties":{ + "type":"object", + "additionalProperties":{ + "$ref":"#/definitions/Foo" + } + } + } + } + }, + "summary": "get foo" + } + } + }, + "definitions": { + "Foo": { + "type": "object", + "properties": { + "a": { + "type": "string" + } + } + } + }, + "schemes": [ + "http" + ], + "swagger": "2.0" +} +`) + + var doc2 openapi2.T + err := json.Unmarshal(v2, &doc2) + require.NoError(t, err) + + doc3, err := ToV3(&doc2) + require.NoError(t, err) + err = doc3.Validate(context.Background()) + require.NoError(t, err) + + responseSchema := doc3.Paths.Value("/foo").Get.Responses.Value("200").Value.Content.Get("application/json").Schema.Value + require.Equal(t, &openapi3.Types{"object"}, responseSchema.Type) + require.Equal(t, &openapi3.Types{"object"}, responseSchema.AdditionalProperties.Schema.Value.Type) + require.Equal(t, "#/components/schemas/Foo", responseSchema.AdditionalProperties.Schema.Value.AdditionalProperties.Schema.Ref) +} + const exampleV2 = ` { "basePath": "/v2",