Skip to content

Commit 3beb231

Browse files
authored
Merge pull request #45 from hauner/#33
resolves #33
2 parents d880a87 + c3701c0 commit 3beb231

File tree

20 files changed

+681
-18
lines changed

20 files changed

+681
-18
lines changed

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import com.github.hauner.openapi.spring.model.datatypes.NoneDataType
4343
import com.github.hauner.openapi.spring.model.datatypes.OffsetDateTimeDataType
4444
import com.github.hauner.openapi.spring.model.datatypes.SetDataType
4545
import com.github.hauner.openapi.spring.model.datatypes.StringDataType
46+
import com.github.hauner.openapi.spring.model.datatypes.StringEnumDataType
4647

4748
/**
4849
* Converter to map OpenAPI schemas to Java data types.
@@ -88,7 +89,7 @@ class DataTypeConverter {
8889
createObjectDataType (dataTypeInfo, dataTypes)
8990

9091
} else {
91-
createSimpleDataType (dataTypeInfo)
92+
createSimpleDataType (dataTypeInfo, dataTypes)
9293
}
9394
}
9495

@@ -153,7 +154,7 @@ class DataTypeConverter {
153154
objectType
154155
}
155156

156-
private DataType createSimpleDataType (SchemaInfo schemaInfo) {
157+
private DataType createSimpleDataType (SchemaInfo schemaInfo, DataTypes dataTypes) {
157158

158159
TargetType targetType = getMappedDataType (new PrimitiveSchemaType(schemaInfo))
159160
if (targetType) {
@@ -190,7 +191,7 @@ class DataTypeConverter {
190191
simpleType = new BooleanDataType ()
191192
break
192193
case 'string':
193-
simpleType = new StringDataType ()
194+
simpleType = createStringDataType (schemaInfo, dataTypes)
194195
break
195196
case 'string/date':
196197
simpleType = new LocalDateDataType ()
@@ -205,6 +206,22 @@ class DataTypeConverter {
205206
simpleType
206207
}
207208

209+
private DataType createStringDataType (SchemaInfo info, DataTypes dataTypes) {
210+
if (!info.isEnum()) {
211+
return new StringDataType ()
212+
}
213+
214+
// in case of an inline definition the name may be lowercase, make sure the enum
215+
// class gets an uppercase name!
216+
def enumType = new StringEnumDataType (
217+
type: info.name.capitalize (),
218+
pkg: [options.packageName, 'model'].join ('.'),
219+
values: info.enumValues)
220+
221+
dataTypes.add (enumType)
222+
enumType
223+
}
224+
208225
TargetType getMappedDataType (SchemaType schemaType) {
209226
// check endpoint mappings
210227
List<TypeMappingX> endpointMatches = schemaType.matchEndpointMapping (options.typeMappings)

src/main/groovy/com/github/hauner/openapi/spring/converter/schema/SchemaInfo.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ class SchemaInfo {
152152
schema.$ref != null
153153
}
154154

155+
boolean isEnum () {
156+
schema.enum != null
157+
}
158+
159+
List<?> getEnumValues () {
160+
schema.enum
161+
}
162+
155163
private boolean hasExtensions () {
156164
schema.extensions != null
157165
}

src/main/groovy/com/github/hauner/openapi/spring/generatr/SpringGeneratr.groovy

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import com.github.hauner.openapi.api.OpenApiGeneratr
2020
import com.github.hauner.openapi.spring.converter.ApiConverter
2121
import com.github.hauner.openapi.spring.converter.ApiOptions
2222
import com.github.hauner.openapi.spring.writer.ApiWriter
23+
import com.github.hauner.openapi.spring.writer.DataTypeWriter
2324
import com.github.hauner.openapi.spring.writer.HeaderWriter
2425
import com.github.hauner.openapi.spring.writer.InterfaceWriter
2526
import com.github.hauner.openapi.spring.writer.MethodWriter
27+
import com.github.hauner.openapi.spring.writer.StringEnumWriter
2628
import io.swagger.v3.parser.OpenAPIV3Parser
2729
import io.swagger.v3.parser.core.models.ParseOptions
2830
import io.swagger.v3.parser.core.models.SwaggerParseResult
@@ -63,10 +65,13 @@ class SpringGeneratr implements OpenApiGeneratr<SpringGeneratrOptions> {
6365
def cv = new ApiConverter(options)
6466
def api = cv.convert (result.openAPI)
6567

68+
def headerWriter = new HeaderWriter()
6669
def writer = new ApiWriter (options,
6770
new InterfaceWriter(
68-
headerWriter: new HeaderWriter(),
69-
methodWriter: new MethodWriter())
71+
headerWriter: headerWriter,
72+
methodWriter: new MethodWriter()),
73+
new DataTypeWriter(headerWriter: headerWriter),
74+
new StringEnumWriter(headerWriter: headerWriter)
7075
)
7176

7277
writer.write (api)

src/main/groovy/com/github/hauner/openapi/spring/model/DataTypes.groovy

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.github.hauner.openapi.spring.model
1818

1919
import com.github.hauner.openapi.spring.model.datatypes.DataType
2020
import com.github.hauner.openapi.spring.model.datatypes.ObjectDataType
21+
import com.github.hauner.openapi.spring.model.datatypes.StringEnumDataType
2122

2223
/**
2324
* Container for Java data types from OpenAPI '#/component/schemas'.
@@ -49,6 +50,18 @@ class DataTypes {
4950
} as List<ObjectDataType>
5051
}
5152

53+
/**
54+
* provides the enum data types (model classes) used by the api endpoints. For this object
55+
* the generatr will create enum classes.
56+
*
57+
* @return list of enum data types
58+
*/
59+
List<StringEnumDataType> getEnumDataTypes () {
60+
types.values ().findAll {
61+
it instanceof StringEnumDataType
62+
} as List<StringEnumDataType>
63+
}
64+
5265
void add (List<DataType> dataTypes) {
5366
dataTypes.each {
5467
types.put (it.name, it)

src/test/groovy/com/github/hauner/openapi/spring/converter/ApiConverterSchemaSpec.groovy renamed to src/main/groovy/com/github/hauner/openapi/spring/model/datatypes/StringEnumDataType.groovy

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,39 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.github.hauner.openapi.spring.converter
17+
package com.github.hauner.openapi.spring.model.datatypes
1818

19-
import spock.lang.Specification
19+
/**
20+
* OpenAPI type 'string' with enum constraint maps to enum class.
21+
*
22+
* @author Martin Hauner
23+
*/
24+
class StringEnumDataType implements DataType {
25+
26+
String type
27+
String pkg = 'unknown'
28+
List<String> values = []
29+
30+
@Override
31+
String getName () {
32+
type
33+
}
2034

35+
@Override
36+
String getPackageName () {
37+
pkg
38+
}
2139

22-
class ApiConverterSchemaSpec extends Specification {
40+
@Override
41+
Set<String> getImports () {
42+
[[packageName, name].join ('.')]
43+
}
44+
45+
@Override
46+
Set<String> getReferencedImports () {
47+
[]
48+
}
2349

2450
}
51+
52+

src/main/groovy/com/github/hauner/openapi/spring/writer/ApiWriter.groovy

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.github.hauner.openapi.spring.writer
1919
import com.github.hauner.openapi.spring.converter.ApiOptions
2020
import com.github.hauner.openapi.spring.model.Api
2121
import com.github.hauner.openapi.spring.model.datatypes.ObjectDataType
22+
import com.github.hauner.openapi.spring.model.datatypes.StringEnumDataType
2223
import groovy.util.logging.Slf4j
2324

2425
/**
@@ -32,6 +33,7 @@ class ApiWriter {
3233
private ApiOptions options
3334
InterfaceWriter interfaceWriter
3435
DataTypeWriter dataTypeWriter
36+
StringEnumWriter enumWriter
3537

3638
File apiFolder
3739
File modelFolder
@@ -41,12 +43,17 @@ class ApiWriter {
4143
this.options = options
4244
this.interfaceWriter = interfaceWriter
4345
this.dataTypeWriter = new DataTypeWriter(headerWriter: new HeaderWriter ())
46+
this.enumWriter = new StringEnumWriter(headerWriter: new HeaderWriter ())
4447
}
4548

46-
ApiWriter(ApiOptions options, InterfaceWriter interfaceWriter, DataTypeWriter dataTypeWriter) {
49+
ApiWriter(ApiOptions options,
50+
InterfaceWriter interfaceWriter,
51+
DataTypeWriter dataTypeWriter,
52+
StringEnumWriter enumWriter) {
4753
this.options = options
4854
this.interfaceWriter = interfaceWriter
4955
this.dataTypeWriter = dataTypeWriter
56+
this.enumWriter = enumWriter
5057
}
5158

5259
void write(Api api) {
@@ -65,6 +72,13 @@ class ApiWriter {
6572
dataTypeWriter.write (writer, it as ObjectDataType)
6673
writer.close ()
6774
}
75+
76+
api.models.enumDataTypes.each {
77+
def target = new File (modelFolder, "${it.name}.java")
78+
def writer = new FileWriter(target)
79+
enumWriter.write (writer, it as StringEnumDataType)
80+
writer.close ()
81+
}
6882
}
6983

7084
private void createTargetFolders () {

src/main/groovy/com/github/hauner/openapi/spring/writer/DataTypeWriter.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,5 @@ class DataTypeWriter {
8686
new ImportFilter ().filter (packageName, imports)
8787
.sort ()
8888
}
89+
8990
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2019 the original authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.github.hauner.openapi.spring.writer
18+
19+
import com.github.hauner.openapi.spring.model.datatypes.StringEnumDataType
20+
import com.github.hauner.openapi.support.Identifier
21+
22+
/**
23+
* Writer for String enum.
24+
*
25+
* @author Martin Hauner
26+
*/
27+
class StringEnumWriter {
28+
29+
HeaderWriter headerWriter
30+
31+
void write (Writer target, StringEnumDataType dataType) {
32+
headerWriter.write (target)
33+
target.write ("package ${dataType.packageName};\n\n")
34+
35+
target.write ("public enum ${dataType.type} {\n\n")
36+
37+
def values = []
38+
dataType.values.each {
39+
values.add (" ${Identifier.toEnum (it)}(\"${it}\")")
40+
}
41+
target.write (values.join (",\n") + ";\n\n")
42+
target.write(" private final String value;\n\n")
43+
44+
target.write ("""\
45+
private ${dataType.type}(String value) {
46+
this.value = value;
47+
}
48+
49+
""")
50+
51+
target.write("""\
52+
@JsonValue
53+
public String getValue() {
54+
return this.value;
55+
}
56+
57+
""")
58+
59+
target.write("""\
60+
@JsonCreator
61+
public static ${dataType.type} fromValue(String value) {
62+
for (${dataType.type} val: ${dataType.type}.values()) {
63+
if (val.value.equals(value)) {
64+
return val;
65+
}
66+
}
67+
throw new IllegalArgumentException(value);
68+
}
69+
70+
""")
71+
72+
target.write ("}\n")
73+
}
74+
75+
}

src/main/groovy/com/github/hauner/openapi/support/Identifier.groovy

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,48 @@ class Identifier {
6363
sb.toString ()
6464
}
6565

66+
/**
67+
* converts a Json string as defined by http://www.json.org/ to a valid (upper case) java
68+
* enum identifier. One way, ie it is not reversible.
69+
*
70+
* conversion rules:
71+
* characters that are not valid java identifiers will be removed. The characters " ", "_",
72+
* "-" (valid or not) are interpreted as word separators and are replaced by "_" and the words
73+
* are converted to upper case.
74+
*
75+
* @param json a valid json "string"
76+
*
77+
* @return a valid upper case enum java identifier
78+
*/
79+
static String toEnum (String json) {
80+
def sb = new StringBuilder()
81+
82+
def wordSplit = false
83+
json.toCharArray ().eachWithIndex { char c, int idx ->
84+
85+
def cu = c.toUpperCase ()
86+
if (idx == 0) {
87+
if (isValidStart (c)) {
88+
sb.append (cu)
89+
}
90+
} else {
91+
if (isValidPart (c)) {
92+
if (wordSplit) {
93+
sb.append ("_")
94+
sb.append (cu)
95+
wordSplit = false
96+
} else {
97+
sb.append (cu)
98+
}
99+
} else {
100+
wordSplit = true
101+
}
102+
}
103+
}
104+
105+
sb.toString ()
106+
}
107+
66108
private static boolean isValidStart (char c) {
67109
Character.isJavaIdentifierStart (c) && !isWordSplitPart (c)
68110
}

0 commit comments

Comments
 (0)