Skip to content

Commit 01cc448

Browse files
committed
#198, add documentation
1 parent 2aebaa6 commit 01cc448

File tree

4 files changed

+198
-2
lines changed

4 files changed

+198
-2
lines changed

docs/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
** xref:processor/parameter.adoc[Parameter]
88
** xref:processor/requestbody.adoc[Request Body]
99
** xref:processor/response.adoc[Responses]
10+
** xref:processor/enums.adoc[Enums]
1011
** xref:processor/one-of-interface.adoc[oneOf]
1112
** xref:processor/deprecated.adoc[Deprecated]
1213
** xref:processor/identifier.adoc[Identifier]

docs/modules/ROOT/pages/mapping/structure.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The mapping file needs the following key on the top-level. Best place is the fir
1515
1616
[source,yaml]
1717
----
18-
openapi-processor-mapping: v3
18+
openapi-processor-mapping: v5
1919
----
2020
====
2121

docs/modules/ROOT/pages/processor/configuration.adoc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ A mapping yaml looks like this:
88

99
[source,yaml]
1010
----
11-
openapi-processor-mapping: v4
11+
openapi-processor-mapping: v5
1212
1313
options:
1414
package-name: io.openapiprocessor.sample
1515
model-name-suffix: Resource
1616
model-type: record
17+
enum-type: string
1718
one-of-interface: true
1819
bean-validation: jakarta
1920
generated-date: true
@@ -208,6 +209,30 @@ public class Foo {
208209
}
209210
----
210211

212+
[#_enum_type]
213+
=== enum type:
214+
215+
(*optional**, `default`, `string` or `framework`, default is `default`)
216+
217+
*mapping.yaml*
218+
[source,yaml]
219+
----
220+
openapi-processor-mapping: v5
221+
222+
options:
223+
enum-type: string
224+
----
225+
226+
There are 3 ways to handle OpenAPI enum definitions, `default`, `string` and `framework`.
227+
228+
*`default`* generates a typical java enum class.
229+
230+
The other two can be used if `default` does not work. This is described in more detail under xref:processor/enums.adoc[enums].
231+
232+
*`string`* does not generate an enum and simply uses `java.lang.String`. In case bean validation is enabled it will generate a custom bean validation annotation that checks if the incoming values is one of the `enum` values given in the OpenAPI description.
233+
234+
*`framework`* does generate a slightly different enum classes than `default` and a Spring `ConverterFactory` that can deserialize incoming values to proper enum values.
235+
211236
== map:
212237

213238
Using type mapping we can tell the processor to map types (schemas) from an `openapi.yaml`
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
= Enums
2+
include::partial$links.adoc[]
3+
4+
[#_default]
5+
== default
6+
7+
By default, openapi-processor creates a java `Enum` from an OpenAPI schema that's using the `enum` keyword.Something like this:
8+
9+
[source,yaml,title=OpenAPI enum]
10+
----
11+
components:
12+
schemas:
13+
Type:
14+
type: string
15+
enum:
16+
- one
17+
- two
18+
----
19+
20+
[source,java,title=generated Java enum]
21+
----
22+
package io.openapiprocessor.openapi2.model;
23+
24+
import com.fasterxml.jackson.annotation.JsonCreator;
25+
import com.fasterxml.jackson.annotation.JsonValue;
26+
import io.openapiprocessor.openapi2.support.Generated;
27+
28+
@Generated(value = "openapi-processor-spring", version = "latest")
29+
public enum Type {
30+
ONE("one"),
31+
TWO("two");
32+
33+
private final String value;
34+
35+
Type(String value) {
36+
this.value = value;
37+
}
38+
39+
@JsonValue
40+
public String getValue() {
41+
return this.value;
42+
}
43+
44+
@JsonCreator
45+
public static Type fromValue(String value) {
46+
for (Type val : Type.values()) {
47+
if (val.value.equals(value)) {
48+
return val;
49+
}
50+
}
51+
throw new IllegalArgumentException(value);
52+
}
53+
}
54+
----
55+
56+
This works without issues if used as part of a request payload.
57+
58+
Unfortunately it may cause an error, like the following if the enum is used as a query parameter:
59+
60+
====
61+
Failed to convert value of type `'java.lang.String'` to required type `'io.openapiprocessor.openapi.model.Type'`; Failed to convert from type [`java.lang.String`] to type [`@org.springframework.web.bind.annotation.RequestParam io.openapiprocessor.openapi.model.Type`] for value [`one`]
62+
====
63+
64+
The reason is, that Spring uses `org.springframework.core.convert.converter.Converter` implementations to deserialize parameters and the default enum deserialization expects the incoming string value to exactly match an enum value.
65+
66+
That is, to successfully convert to the enum value `ONE` the incoming value string has to be `ONE`. It will not accept the lowercase `one`.
67+
68+
The converter doesn't use jackson, so it won't use the `@JsonCreator` method to convert from the incoming lowercase value to the corresponding enum value.
69+
70+
To handle this issue we can either use the `enum-type` <<_enum_type_string>> or <<_enum_type_framework>>.
71+
72+
[#_enum_type_string]
73+
== string
74+
75+
Do not create a Java enum classes for OpenAPI enums and simply use `java.lang.String`.
76+
77+
[source,yaml,title=mapping.yaml]
78+
----
79+
openapi-processor-mapping: v5
80+
81+
options:
82+
enum-type: string
83+
----
84+
85+
This is an alternative to generating enum classes. It will pass the enum value string as given in the api request to avoid the issue described in the <<_default>> section.
86+
87+
[source,java,title=api interface]
88+
----
89+
public interface FooApi {
90+
91+
@PostMapping(path = "/foo", produces = {"application/json"})
92+
Foo postFoo(@RequestParam(name = "enum", required = false) String aEnum);
93+
94+
}
95+
----
96+
97+
In this simple form it doesn't provide any help to make sure that the incoming values is a valid value as described in the OpenAPI.
98+
99+
By enabling bean-validation the processor will generate and use a custom validation annotation to check that the incoming string is an allowed value.
100+
101+
[source,yaml,title=mapping.yaml]
102+
----
103+
openapi-processor-mapping: v5
104+
105+
options:
106+
bean-validation: jakarta
107+
enum-type: string
108+
----
109+
110+
[source,java,title=api interface with validation]
111+
----
112+
public interface FooApi {
113+
114+
@PostMapping(path = "/foo", produces = {"application/json"})
115+
Foo postFoo(@RequestParam(name = "enum", required = false) @Values(values = {"one", "two"}) String aEnum);
116+
117+
}
118+
----
119+
120+
[NOTE]
121+
====
122+
make sure you annotate the controller with `@Validated` to run the `@Values` check.
123+
124+
[source,java,title=api interface with validation]
125+
----
126+
@Validated
127+
@RestController
128+
public class ApiController implements FooApi {
129+
// ...
130+
}
131+
----
132+
====
133+
134+
[#_enum_type_framework]
135+
== framework
136+
137+
This is another alternative to the <<_default>> enum classes to avoid the issue described above.
138+
139+
It creates Java enum classes and a Spring `ConverterFactory` with the name `+{package-name}+.spring.StringToEnumConverterFactory` that does create enum converters for all generated enums.The enum converters convert incoming strings to their enum by comparing with the OpenAPI enum values.
140+
141+
[source,yaml,title=mapping.yaml]
142+
----
143+
openapi-processor-mapping: v5
144+
145+
options:
146+
enum-type: framework
147+
----
148+
149+
To enable the converter factory use a `WebMvcConfigurer` (or `WebFluxConfigurer`) like the code below:
150+
151+
[source,java,title=enable enum converter factory]
152+
----
153+
package io.openapiprocessor.samples;
154+
155+
import io.openapiprocessor.openapi.spring.StringToEnumConverterFactory;
156+
import org.springframework.context.annotation.Configuration;
157+
import org.springframework.format.FormatterRegistry;
158+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
159+
160+
@Configuration
161+
public class WebConfig implements WebMvcConfigurer {
162+
163+
@SuppressWarnings("rawtypes")
164+
@Override
165+
public void addFormatters(FormatterRegistry registry) {
166+
registry.addConverterFactory(new StringToEnumConverterFactory());
167+
}
168+
}
169+
170+
----

0 commit comments

Comments
 (0)