Skip to content

Commit 1e7b31f

Browse files
committed
map single query parameter object to map
1 parent 95ea08c commit 1e7b31f

7 files changed

Lines changed: 198 additions & 167 deletions

File tree

src/main/groovy/com/github/hauner/openapi/spring/converter/DataTypeConverter.groovy

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import com.github.hauner.openapi.spring.model.datatypes.CollectionDataType
3232
import com.github.hauner.openapi.spring.model.datatypes.DataTypeConstraints
3333
import com.github.hauner.openapi.spring.model.datatypes.ListDataType
3434
import com.github.hauner.openapi.spring.model.datatypes.LocalDateDataType
35-
import com.github.hauner.openapi.spring.model.datatypes.MapDataType
3635
import com.github.hauner.openapi.spring.model.datatypes.MappedDataType
36+
import com.github.hauner.openapi.spring.model.datatypes.MappedMapDataType
3737
import com.github.hauner.openapi.spring.model.datatypes.ObjectDataType
3838
import com.github.hauner.openapi.spring.model.datatypes.DataType
3939
import com.github.hauner.openapi.spring.model.datatypes.DoubleDataType
@@ -74,17 +74,11 @@ class DataTypeConverter {
7474
*/
7575
DataType convert (SchemaInfo dataTypeInfo, DataTypes dataTypes) {
7676

77-
if (dataTypeInfo.isArray ()) {
78-
createArrayDataType (dataTypeInfo, dataTypes)
79-
80-
} else if (dataTypeInfo.isRefObject ()) {
81-
def datatype = dataTypes.findRef (dataTypeInfo.ref)
82-
if (datatype) {
83-
return datatype
84-
}
77+
if (dataTypeInfo.isRefObject ()) {
78+
createRefDataType(dataTypeInfo, dataTypes)
8579

86-
def refTypeInfo = dataTypeInfo.buildForRef ()
87-
convert (refTypeInfo, dataTypes)
80+
} else if (dataTypeInfo.isArray ()) {
81+
createArrayDataType (dataTypeInfo, dataTypes)
8882

8983
} else if (dataTypeInfo.isObject ()) {
9084
createObjectDataType (dataTypeInfo, dataTypes)
@@ -117,41 +111,48 @@ class DataTypeConverter {
117111
arrayType
118112
}
119113

114+
private DataType createRefDataType (SchemaInfo schemaInfo, DataTypes dataTypes) {
115+
convert (schemaInfo.buildForRef (), dataTypes)
116+
}
117+
120118
private DataType createObjectDataType (SchemaInfo schemaInfo, DataTypes dataTypes) {
121119
def objectType
122120

123121
TargetType targetType = getMappedDataType (new ObjectSchemaType (schemaInfo))
124122
if (targetType) {
125-
objectType = new MappedDataType (
126-
type: targetType.name,
127-
pkg: targetType.pkg,
128-
genericTypes: targetType.genericNames
129-
)
130-
131-
dataTypes.add (schemaInfo.name, objectType)
132-
return objectType
123+
switch (targetType?.typeName) {
124+
case Map.name:
125+
case 'org.springframework.util.MultiValueMap':
126+
objectType = new MappedMapDataType (
127+
type: targetType.name,
128+
pkg: targetType.pkg,
129+
genericTypes: targetType.genericNames
130+
)
131+
return objectType
132+
default:
133+
objectType = new MappedDataType (
134+
type: targetType.name,
135+
pkg: targetType.pkg,
136+
genericTypes: targetType.genericNames
137+
)
138+
139+
// probably not required anymore
140+
dataTypes.add (schemaInfo.name, objectType)
141+
return objectType
142+
}
133143
}
134144

135-
switch (schemaInfo.getXJavaType ()) {
136-
case Map.name:
137-
objectType = new MapDataType ()
138-
dataTypes.add (schemaInfo.name, objectType)
139-
break
140-
141-
default:
142-
objectType = new ObjectDataType (
143-
type: schemaInfo.name,
144-
pkg: [options.packageName, 'model'].join ('.')
145-
)
146-
147-
schemaInfo.eachProperty { String propName, SchemaInfo propDataTypeInfo ->
148-
def propType = convert (propDataTypeInfo, dataTypes)
149-
objectType.addObjectProperty (propName, propType)
150-
}
145+
objectType = new ObjectDataType (
146+
type: schemaInfo.name,
147+
pkg: [options.packageName, 'model'].join ('.')
148+
)
151149

152-
dataTypes.add (objectType)
150+
schemaInfo.eachProperty { String propName, SchemaInfo propDataTypeInfo ->
151+
def propType = convert (propDataTypeInfo, dataTypes)
152+
objectType.addObjectProperty (propName, propType)
153153
}
154154

155+
dataTypes.add (objectType)
155156
objectType
156157
}
157158

src/main/groovy/com/github/hauner/openapi/spring/model/datatypes/MappedDataType.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ package com.github.hauner.openapi.spring.model.datatypes
2323
*/
2424
class MappedDataType implements DataType {
2525

26-
private String type
26+
protected String type
2727
String pkg = 'unknown'
2828
List<String> genericTypes = []
2929

@@ -57,7 +57,7 @@ class MappedDataType implements DataType {
5757
}
5858
}
5959

60-
private String getClassName (String ref) {
60+
protected String getClassName (String ref) {
6161
ref.substring (ref.lastIndexOf ('.') + 1)
6262
}
6363

src/main/groovy/com/github/hauner/openapi/spring/model/datatypes/MapDataType.groovy renamed to src/main/groovy/com/github/hauner/openapi/spring/model/datatypes/MappedMapDataType.groovy

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019 the original authors
2+
* Copyright 2020 the original authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,31 +17,9 @@
1717
package com.github.hauner.openapi.spring.model.datatypes
1818

1919
/**
20-
* OpenAPI type 'object' with 'x-type-java': java.util.Map extension.
20+
* OpenAPI schema mapped to a map java type.
2121
*
2222
* @author Martin Hauner
2323
*/
24-
@Deprecated
25-
class MapDataType implements DataType {
26-
27-
@Override
28-
String getName () {
29-
'Map'
30-
}
31-
32-
@Override
33-
String getPackageName () {
34-
'java.util'
35-
}
36-
37-
@Override
38-
Set<String> getImports () {
39-
[[packageName, 'Map'].join('.')]
40-
}
41-
42-
@Override
43-
Set<String> getReferencedImports () {
44-
['java.util.Map', 'java.lang.String']
45-
}
46-
24+
class MappedMapDataType extends MappedDataType {
4725
}
Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019 the original authors
2+
* Copyright 2019-2020 the original authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,8 @@
1616

1717
package com.github.hauner.openapi.spring.model.parameters
1818

19-
import com.github.hauner.openapi.spring.model.datatypes.MapDataType
2019
import com.github.hauner.openapi.spring.model.datatypes.MappedDataType
20+
import com.github.hauner.openapi.spring.model.datatypes.MappedMapDataType
2121
import com.github.hauner.openapi.spring.model.datatypes.ObjectDataType
2222

2323
/**
@@ -31,32 +31,44 @@ class QueryParameter extends Parameter {
3131
"RequestParam"
3232
}
3333

34-
boolean isRequired () {
35-
if (dataType instanceof MapDataType) {
36-
return true
37-
}
38-
39-
this.@required
40-
}
41-
4234
/**
4335
* If the query parameter is mapped to a pojo object it should not have a {@code @RequestParam}
4436
* annotation.
4537
*/
4638
boolean withAnnotation () {
47-
! (
48-
dataType instanceof ObjectDataType
49-
|| dataType instanceof MappedDataType
50-
)
39+
// Map should be annotated
40+
if (dataType instanceof MappedMapDataType) {
41+
return true
42+
}
43+
44+
// Pojo's should NOT be annotated
45+
if (dataType instanceof ObjectDataType) {
46+
return false
47+
}
48+
49+
// usually a pojo....
50+
if (dataType instanceof MappedDataType) {
51+
return false
52+
}
53+
54+
true
5155
}
5256

5357
/**
54-
* todo validate.
55-
*
5658
* If the query parameter is mapped to a pojo object it should not have any parameters.
5759
*/
5860
boolean withParameters () {
59-
! (dataType instanceof ObjectDataType)
61+
// Map should not have parameters
62+
if (dataType instanceof MappedMapDataType) {
63+
return false
64+
}
65+
66+
// Pojo should not have parameters
67+
if (dataType instanceof ObjectDataType) {
68+
return false
69+
}
70+
71+
true
6072
}
6173

6274
}

src/test/groovy/com/github/hauner/openapi/spring/converter/DataTypeConverterObjectTypeMappingSpec.groovy

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.github.hauner.openapi.spring.converter.mapping.ParameterTypeMapping
2222
import com.github.hauner.openapi.spring.converter.mapping.ResponseTypeMapping
2323
import com.github.hauner.openapi.spring.converter.mapping.TypeMapping
2424
import com.github.hauner.openapi.spring.model.Api
25+
import spock.lang.Ignore
2526
import spock.lang.Specification
2627
import spock.lang.Unroll
2728

@@ -391,4 +392,114 @@ paths:
391392
]
392393
}
393394

395+
void "converts query param object schema to Map<> set via mapping" () {
396+
def openApi = parse ("""\
397+
openapi: 3.0.2
398+
info:
399+
title: API
400+
version: 1.0.0
401+
402+
paths:
403+
/endpoint-map:
404+
get:
405+
parameters:
406+
- name: props
407+
description: query, map from single property
408+
in: query
409+
required: false
410+
schema:
411+
\$ref: '#/components/schemas/Props'
412+
responses:
413+
'204':
414+
description: empty
415+
416+
components:
417+
418+
schemas:
419+
420+
Props:
421+
type: object
422+
properties:
423+
prop1:
424+
type: string
425+
prop2:
426+
type: string
427+
""")
428+
429+
when:
430+
def options = new ApiOptions(
431+
packageName: 'pkg',
432+
typeMappings: [
433+
new EndpointTypeMapping (path: '/endpoint-map',
434+
typeMappings: [
435+
new TypeMapping (
436+
sourceTypeName: 'Props',
437+
targetTypeName: 'java.util.Map',
438+
genericTypeNames: ['java.lang.String', 'java.lang.String'])
439+
])
440+
])
441+
Api api = new ApiConverter (options).convert (openApi)
442+
443+
then:
444+
def itf = api.interfaces.first ()
445+
def ep = itf.endpoints.first ()
446+
def p = ep.parameters.first ()
447+
p.dataType.name == 'Map<String, String>'
448+
}
449+
450+
void "converts query param object schema to MultiValueMap<> set via mapping" () {
451+
def openApi = parse ("""\
452+
openapi: 3.0.2
453+
info:
454+
title: API
455+
version: 1.0.0
456+
457+
paths:
458+
/endpoint-map:
459+
get:
460+
parameters:
461+
- name: props
462+
description: query, map from single property
463+
in: query
464+
required: false
465+
schema:
466+
\$ref: '#/components/schemas/Props'
467+
responses:
468+
'204':
469+
description: empty
470+
471+
components:
472+
473+
schemas:
474+
475+
Props:
476+
type: object
477+
properties:
478+
prop1:
479+
type: string
480+
prop2:
481+
type: string
482+
""")
483+
484+
when:
485+
def options = new ApiOptions(
486+
packageName: 'pkg',
487+
typeMappings: [
488+
new EndpointTypeMapping (path: '/endpoint-map',
489+
typeMappings: [
490+
new TypeMapping (
491+
sourceTypeName: 'Props',
492+
targetTypeName: 'org.springframework.util.MultiValueMap',
493+
genericTypeNames: ['java.lang.String', 'java.lang.String'])
494+
])
495+
])
496+
Api api = new ApiConverter (options).convert (openApi)
497+
498+
then:
499+
def itf = api.interfaces.first ()
500+
def ep = itf.endpoints.first ()
501+
def p = ep.parameters.first ()
502+
p.dataType.name == 'MultiValueMap<String, String>'
503+
}
504+
394505
}

0 commit comments

Comments
 (0)