Skip to content

Commit 4773972

Browse files
authored
Add OpenAPI generation for maps of string -> object (#15519)
1 parent ae8ab9b commit 4773972

File tree

3 files changed

+166
-5
lines changed

3 files changed

+166
-5
lines changed

mmv1/openapi_generate/parser.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func parseOpenApi(resourcePath, resourceName string, root *openapi3.T) []any {
303303
if strings.Contains(strings.ToLower(param.Value.Name), strings.ToLower(resourceName)) {
304304
idParam = param.Value.Name
305305
}
306-
paramObj := writeObject(param.Value.Name, param.Value.Schema, propType(param.Value.Schema), true)
306+
paramObj := WriteObject(param.Value.Name, param.Value.Schema, propType(param.Value.Schema), true)
307307
description := param.Value.Description
308308
if strings.TrimSpace(description) == "" {
309309
description = "No description"
@@ -336,7 +336,7 @@ func propType(prop *openapi3.SchemaRef) openapi3.Types {
336336
}
337337
}
338338

339-
func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, urlParam bool) api.Type {
339+
func WriteObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, urlParam bool) api.Type {
340340
var field api.Type
341341

342342
switch name {
@@ -394,8 +394,17 @@ func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, u
394394
}
395395

396396
field.Type = "NestedObject"
397-
398-
field.Properties = buildProperties(obj.Value.Properties, obj.Value.Required)
397+
if obj.Value.AdditionalProperties.Schema != nil {
398+
field.Type = "Map"
399+
field.KeyName = "TODO: CHANGEME"
400+
var valueType api.Type
401+
valueType.Name = "TODO: CHANGEME"
402+
valueType.Type = "NestedObject"
403+
valueType.Properties = buildProperties(obj.Value.AdditionalProperties.Schema.Value.Properties, obj.Value.AdditionalProperties.Schema.Value.Required)
404+
field.ValueType = &valueType
405+
} else {
406+
field.Properties = buildProperties(obj.Value.Properties, obj.Value.Required)
407+
}
399408
case "array":
400409
field.Type = "Array"
401410
var subField api.Type
@@ -454,7 +463,7 @@ func buildProperties(props openapi3.Schemas, required []string) []*api.Type {
454463
properties := []*api.Type{}
455464
for _, k := range slices.Sorted(maps.Keys(props)) {
456465
prop := props[k]
457-
propObj := writeObject(k, prop, propType(prop), false)
466+
propObj := WriteObject(k, prop, propType(prop), false)
458467
if slices.Contains(required, k) {
459468
propObj.Required = true
460469
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package openapi_generate
2+
3+
import (
4+
"context"
5+
"github.com/getkin/kin-openapi/openapi3"
6+
"testing"
7+
)
8+
9+
func TestMapType(t *testing.T) {
10+
_ = NewOpenapiParser("/fake", "/fake")
11+
ctx := context.Background()
12+
loader := &openapi3.Loader{Context: ctx, IsExternalRefsAllowed: true}
13+
doc, _ := loader.LoadFromFile("./test_data/test_api.yaml")
14+
_ = doc.Validate(ctx)
15+
16+
petSchema := doc.Paths.Map()["/pets"].Post.Parameters[0].Value.Schema
17+
mmObject := WriteObject("pet", petSchema, propType(petSchema), false)
18+
if mmObject.KeyName == "" || mmObject.Type != "Map" {
19+
t.Error("Failed to parse map type")
20+
}
21+
if len(mmObject.ValueType.Properties) != 4 {
22+
t.Errorf("Expected 4 properties, found %d", len(mmObject.ValueType.Properties))
23+
}
24+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
openapi: "3.0.0"
2+
info:
3+
version: 1.0.0
4+
title: Swagger Petstore
5+
license:
6+
name: MIT
7+
servers:
8+
- url: http://petstore.swagger.io/v1
9+
paths:
10+
/pets:
11+
get:
12+
summary: List all pets
13+
operationId: listPets
14+
tags:
15+
- pets
16+
parameters:
17+
- name: limit
18+
in: query
19+
description: How many items to return at one time (max 100)
20+
required: false
21+
schema:
22+
type: integer
23+
format: int32
24+
responses:
25+
200:
26+
description: An paged array of pets
27+
headers:
28+
x-next:
29+
description: A link to the next page of responses
30+
schema:
31+
type: string
32+
content:
33+
application/json:
34+
schema:
35+
$ref: "#/components/schemas/Pets"
36+
default:
37+
description: unexpected error
38+
content:
39+
application/json:
40+
schema:
41+
$ref: "#/components/schemas/Error"
42+
post:
43+
summary: Create a pet
44+
operationId: createPets
45+
tags:
46+
- pets
47+
parameters:
48+
- name: pet
49+
in: body
50+
required: true
51+
description: The pet to create
52+
schema:
53+
type: object
54+
additionalProperties:
55+
$ref: "#/components/schemas/Pet"
56+
responses:
57+
201:
58+
description: Null response
59+
default:
60+
description: unexpected error
61+
content:
62+
application/json:
63+
schema:
64+
$ref: "#/components/schemas/Error"
65+
/pets/{petId}:
66+
get:
67+
summary: Info for a specific pet
68+
operationId: showPetById
69+
tags:
70+
- pets
71+
parameters:
72+
- name: petId
73+
in: path
74+
required: true
75+
description: The id of the pet to retrieve
76+
schema:
77+
type: string
78+
responses:
79+
200:
80+
description: Expected response to a valid request
81+
content:
82+
application/json:
83+
schema:
84+
$ref: "#/components/schemas/Pets"
85+
default:
86+
description: unexpected error
87+
content:
88+
application/json:
89+
schema:
90+
$ref: "#/components/schemas/Error"
91+
components:
92+
schemas:
93+
Pet:
94+
required:
95+
- id
96+
- name
97+
properties:
98+
id:
99+
type: integer
100+
format: int64
101+
name:
102+
type: string
103+
tag:
104+
type: string
105+
mapType:
106+
type: object
107+
additionalProperties:
108+
$ref: "#/components/schemas/Food"
109+
Food:
110+
required:
111+
- name
112+
properties:
113+
name:
114+
type: string
115+
Pets:
116+
type: array
117+
items:
118+
$ref: "#/components/schemas/Pet"
119+
Error:
120+
required:
121+
- code
122+
- message
123+
properties:
124+
code:
125+
type: integer
126+
format: int32
127+
message:
128+
type: string

0 commit comments

Comments
 (0)