Skip to content

Commit cea0a13

Browse files
openapi2conv: convert references in nested additionalProperties schemas (#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
1 parent f476f7b commit cea0a13

File tree

2 files changed

+150
-5
lines changed

2 files changed

+150
-5
lines changed

openapi2conv/openapi2_conv.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -513,11 +513,7 @@ func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef {
513513
MaxProps: schema.Value.MaxProps,
514514
AllOf: make(openapi3.SchemaRefs, len(schema.Value.AllOf)),
515515
Properties: make(openapi3.Schemas),
516-
AdditionalProperties: schema.Value.AdditionalProperties,
517-
}
518-
519-
if schema.Value.AdditionalProperties.Schema != nil {
520-
v3Schema.AdditionalProperties.Schema.Ref = ToV3Ref(schema.Value.AdditionalProperties.Schema.Ref)
516+
AdditionalProperties: toV3AdditionalProperties(schema.Value.AdditionalProperties),
521517
}
522518

523519
if schema.Value.Discriminator != "" {
@@ -551,6 +547,27 @@ func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef {
551547
}
552548
}
553549

550+
func toV3AdditionalProperties(from openapi3.AdditionalProperties) openapi3.AdditionalProperties {
551+
return openapi3.AdditionalProperties{
552+
Has: from.Has,
553+
Schema: convertRefsInV3SchemaRef(from.Schema),
554+
}
555+
}
556+
557+
func convertRefsInV3SchemaRef(from *openapi3.SchemaRef) *openapi3.SchemaRef {
558+
if from == nil {
559+
return nil
560+
}
561+
to := *from
562+
to.Ref = ToV3Ref(to.Ref)
563+
if to.Value != nil {
564+
v := *from.Value
565+
to.Value = &v
566+
to.Value.AdditionalProperties = toV3AdditionalProperties(to.Value.AdditionalProperties)
567+
}
568+
return &to
569+
}
570+
554571
var ref2To3 = map[string]string{
555572
"#/definitions/": "#/components/schemas/",
556573
"#/responses/": "#/components/responses/",

openapi2conv/openapi2_conv_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,134 @@ func TestConvOpenAPIV2ToV3(t *testing.T) {
6565
require.JSONEq(t, exampleV3, string(data))
6666
}
6767

68+
func TestConvOpenAPIV2ToV3WithAdditionalPropertiesSchemaRef(t *testing.T) {
69+
v2 := []byte(`
70+
{
71+
"basePath": "/v2",
72+
"host": "test.example.com",
73+
"info": {
74+
"title": "MyAPI",
75+
"version": "0.1"
76+
},
77+
"paths": {
78+
"/foo": {
79+
"get": {
80+
"operationId": "getFoo",
81+
"produces": [
82+
"application/json"
83+
],
84+
"responses": {
85+
"200": {
86+
"description": "returns all information",
87+
"schema":{
88+
"type":"object",
89+
"additionalProperties":{
90+
"$ref":"#/definitions/Foo"
91+
}
92+
}
93+
}
94+
},
95+
"summary": "get foo"
96+
}
97+
}
98+
},
99+
"definitions": {
100+
"Foo": {
101+
"type": "object",
102+
"properties": {
103+
"a": {
104+
"type": "string"
105+
}
106+
}
107+
}
108+
},
109+
"schemes": [
110+
"http"
111+
],
112+
"swagger": "2.0"
113+
}
114+
`)
115+
116+
var doc2 openapi2.T
117+
err := json.Unmarshal(v2, &doc2)
118+
require.NoError(t, err)
119+
120+
doc3, err := ToV3(&doc2)
121+
require.NoError(t, err)
122+
err = doc3.Validate(context.Background())
123+
require.NoError(t, err)
124+
125+
responseSchema := doc3.Paths.Value("/foo").Get.Responses.Value("200").Value.Content.Get("application/json").Schema.Value
126+
require.Equal(t, &openapi3.Types{"object"}, responseSchema.Type)
127+
require.Equal(t, "#/components/schemas/Foo", responseSchema.AdditionalProperties.Schema.Ref)
128+
}
129+
130+
func TestConvOpenAPIV2ToV3WithNestedAdditionalPropertiesSchemaRef(t *testing.T) {
131+
v2 := []byte(`
132+
{
133+
"basePath": "/v2",
134+
"host": "test.example.com",
135+
"info": {
136+
"title": "MyAPI",
137+
"version": "0.1"
138+
},
139+
"paths": {
140+
"/foo": {
141+
"get": {
142+
"operationId": "getFoo",
143+
"produces": [
144+
"application/json"
145+
],
146+
"responses": {
147+
"200": {
148+
"description": "returns all information",
149+
"schema":{
150+
"type":"object",
151+
"additionalProperties":{
152+
"type":"object",
153+
"additionalProperties":{
154+
"$ref":"#/definitions/Foo"
155+
}
156+
}
157+
}
158+
}
159+
},
160+
"summary": "get foo"
161+
}
162+
}
163+
},
164+
"definitions": {
165+
"Foo": {
166+
"type": "object",
167+
"properties": {
168+
"a": {
169+
"type": "string"
170+
}
171+
}
172+
}
173+
},
174+
"schemes": [
175+
"http"
176+
],
177+
"swagger": "2.0"
178+
}
179+
`)
180+
181+
var doc2 openapi2.T
182+
err := json.Unmarshal(v2, &doc2)
183+
require.NoError(t, err)
184+
185+
doc3, err := ToV3(&doc2)
186+
require.NoError(t, err)
187+
err = doc3.Validate(context.Background())
188+
require.NoError(t, err)
189+
190+
responseSchema := doc3.Paths.Value("/foo").Get.Responses.Value("200").Value.Content.Get("application/json").Schema.Value
191+
require.Equal(t, &openapi3.Types{"object"}, responseSchema.Type)
192+
require.Equal(t, &openapi3.Types{"object"}, responseSchema.AdditionalProperties.Schema.Value.Type)
193+
require.Equal(t, "#/components/schemas/Foo", responseSchema.AdditionalProperties.Schema.Value.AdditionalProperties.Schema.Ref)
194+
}
195+
68196
const exampleV2 = `
69197
{
70198
"basePath": "/v2",

0 commit comments

Comments
 (0)