Skip to content

Commit

Permalink
openapi2conv: convert references in nested additionalProperties schem…
Browse files Browse the repository at this point in the history
…as (#1047)

* Convert references in nested additionalProperties schemas

When the schema specified in additionalProperties contains another
nested schema with additionalProperties, the references must be
converted at all of levels of the nested schemas.

* make toV3AdditionalProperties a non-exported function
  • Loading branch information
travisnewhouse authored Dec 24, 2024
1 parent f476f7b commit cea0a13
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 5 deletions.
27 changes: 22 additions & 5 deletions openapi2conv/openapi2_conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down Expand Up @@ -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/",
Expand Down
128 changes: 128 additions & 0 deletions openapi2conv/openapi2_conv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit cea0a13

Please sign in to comment.