Releases: openapi-processor/openapi-processor-core
2022.6
annotation mapping
annotation mappings allows to add additional annotations to an OpenAPI source type.
(this originates from openapi-processor/openapi-processor-spring#143 advanced validation support, but has changed into a more general solution)
Currently, this is available as
-
global annotation type mapping:
it adds an annotation to the model class generated for the source type.
-
global & endpoint parameter annotation type mapping:
it adds an annotation to a parameter of the source type. Since a request body is passed as parameter the mapping will work for it too.
The global annotation mapping should be added to the map/types or map/parameters section in the mapping.yaml.
The endpoint (http method) mapping restricts the mapping to a specific endpoint. This will go to the map/paths/<endpoint-path>/parameters section in the mapping.yaml.
The annotation type mapping is similar to other mappings and is defined like this:
- type: {source type} @ {annotation type}Here is an examples mapping.yaml that defines additional annotations for two schemas (model classes) Foo & Bar:
openapi-processor-mapping: v2.1 # <1>
options:
package-name: io.openapiprocessor.openapi
bean-validation: true
map:
types:
- type: Bar @ io.openapiprocessor.samples.validations.Sum(24) # <2>
parameters:
- type: Foo @ io.openapiprocessor.samples.validations.Sum(value = 42) # <3>
# this formats do work too <4>
# - type: Foo @ annotation.Bar
# - type: Foo @ annotation.Bar()
# - type: Foo @ annotation.Bar("bar")
# - type: Foo @ annotation.Bar(value = "bar", foo = 2)The Sum annotation in the example is a custom bean validation but the feature itself is not limited to bean validation.
<1> use v2.1 as the mapping version to avoid validation warnings in the mapping file.
<2> the Bar mapping is using a global type annotation mapping, so the annotation is added to the generated Bar class.
<3> the Foo mapping adds the annotation to the parameter of the endpoint methods that use Foo.
<4> this is a list of examples that shows annotation parameters.
The Bar annotation mapping will produce the Bar model class with the additional annotation on the class:
@Sum(24) // <1>
public class Bar { /* ... */ }The Foo annotation parameter mapping will produce the endpoint with the additional annotation on the Foo parameter:
@PostMapping(/*...*/)
Foo postFoo(@RequestBody @Sum(value = 42) @Valid @NotNull Foo body);The full example is available in the spring validation sample.
openapi-processor/openapi-processor-spring#144, use annotation for generated code instead of comment
the processor now generates a @Generated annotation and adds it to all generated interfaces and classes instead of the text header.
Some tools recognize the @Generated annotation and exclude them from processing. For example, jacoco will automatically exclude the @Generated files from the code coverage.
this will look like this:
package io.openapiprocessor.release;
import io.openapiprocessor.release.support.Generated;
import org.springframework.web.bind.annotation.GetMapping;
@Generated(value = "openapi-processor-spring", version = "2022.6", date = "2022-09-28T18:37:33.250622+02:00")
public interface ReleaseApi {
// ...
}Because of the longish date the code formatter will probably add a few line breaks.
Generation of the data parameter can be disabled by setting the generated-date option to false:
openapi-processor-mapping: v2.1 # <1>
options:
package-name: io.openapiprocessor.openapi
generated-date: false<1> use v2.1 as the mapping version to avoid validation warnings in the mapping file.
openapi-processor/openapi-processor-spring#140 additional parameter configuration did not working in global context
using an additional parameter in the global context was not implemented.
map:
parameters:
- add: request => javax.servlet.http.HttpServletRequest#99 windows path handling
was broken since 2022.5
2022.5
OpenAPI 3.0 parser & JSON schema validation
openapi-processor provides another OpenAPI 3.0 parser. It includes JSON schema validation with detailed reporting.
To enable it, set the parser configuration to INTERNAL.
// build.gradle processor configuration
openapiProcessor {
spring {
processor 'io.openapiprocessor:openapi-processor-spring:2022.4'
apiPath "${projectDir}/src/api/openapi.yaml"
targetDir "$projectDir/build/openapi"
mapping "${projectDir}/src/api/mapping.yaml"
// use internal OpenAPI parser
parser 'INTERNAL'
}
}OpenAPI 3.1 (experimental)
The internal OpenAPI parser supports OpenAPI 3.1 but does not yet have schema validation.
To enable it, set the parser configuration to INTERNAL. It will automatically detect OpenAPI 3.0 & 3.1.
The processor does handle the renamed/changed OpenAPI 3.1 properties as needed for code generation:
type keyword
The type keyword allows a list of types. Defining a nullable type is done by adding "null" to the list of types.
# OpenAPI v3.0
type: string
nullable: true
# OpenAPI v3.1
type:
- "string"
- "null" The processor does support the new nullable definition. Apart from that it will use the first non-null type as the type for code generation.
exclusiveMinimum and exclusiveMaximum keywords
# OpenAPI v3.0
maximum: 42
exclusiveMaximum: true
# OpenAPI v3.1
exclusiveMaximum: 42which is used for adding bean validations.
openapi-processor/openapi-processor-spring#141, missing import for javax.validation.constraints.Pattern
the imports for bean validation annotations were missing for item constraints of a mapped array. Having an api description like this
paths:
/test:
get:
parameters:
- in: query
name: patternParam
required: false
description: query parameter with @Pattern annotation
schema:
$ref: '#/components/schemas/PatternParam'
responses:
'200':
description: ok
schemas:
PatternParam:
type: array
items:
type: string
pattern: '.*'and a mapping
openapi-processor-spring: v2
options:
package-name: generated
bean-validation: true
map:
types:
- type: array => java.util.Listdid not generate the javax.validation.constraints.Pattern import.
package generated.api;
import annotation.Mapping;
import annotation.Parameter;
import java.util.List;
import javax.validation.constraints.Pattern;
public interface Api {
@Mapping("/test")
void getTest(@Parameter List<@Pattern(regexp = ".*") String> patternParam);
}(ignore the @Mapping/@Parameter annotations, this is pseudo code used by the integration tests)
2022.4.1
2022.4
#92, processor does not handle empty schema
it is now possible (and necessary) to create a type mapping for an empty schema if the type is needed. Having an OpenAPI fragment like this
components:
schemas:
Empty: {}the processor does not generate an Empty class (the schema has no object properties and it assumes that it is not necessary to create a model class for it) but the type Empty could still be referenced by another type (e.g. as property).
To avoid compilation errors it is necessary to add a type mapping for Empty:
openapi-processor-mapping: v2
options:
package-name: generated
map:
types:
- type: Empty => java.lang.Object
#91, model-name-suffix was not properly supported on generic parameters
having a type mapping with a generated generic parameter (i.e Foo in the example) in combination with model-name-suffix
openapi-processor-mapping: v2
options:
package-name: generated
model-name-suffix: Resource
map:
types:
- type: FooPage => org.springframework.data.domain.Page<{package-name}.model.Foo>did ignore the model-name-suffix on the generic parameter and failed to generate the FooResource model.
2022.3
#78, generate marker interface for oneOf objects
by enabling one-of-interface in the options
openapi-processor-mapping: v2
options:
one-of-interface: truethe processor will create a marker interface for a oneOf of objects that is implemented by all objects in the oneOf list.
For an api like this:
paths:
/foo:
get:
responses:
'200':
description: oneOf
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
components:
schemas:
Foo:
type: object
properties:
foo:
$ref: '#/components/schemas/FooOneOf'
FooOneOf:
oneOf:
- $ref: '#/components/schemas/FooOne'
- $ref: '#/components/schemas/FooTwo'the processor generates the class Foo:
// simplified
public class Foo {
private FooOneOf foo;
}a marker interface:
public interface FooOneOf {}and the two model class FooOne & FooTwo, that implement the marker interface:
// simplified
public class FooOne implements FooOneOf { /* ... */ }// simplified
public class FooTwo implements FooOneOf { /* ... */ }Without one-of-interface: true the processor does not generate a marker interface and the response class Foo uses Object as the type of the foo member.
// simplified Foo model class
public class Foo {
private Object foo;
}2022.2
multi wrapper (e.g. Flux) with array type mapping failed
having a simple array response
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Foo'with a common type mapping for array
openapi-processor-mapping: v2
options:
package-name: generated
map:
multi: reactor.core.publisher.Flux
types:
- type: array => java.util.Listcaused a class cast exception.
#86, merge allOf and sibling properties
having a schema like:
schema:
type: object
allOf:
- type: object
properties:
foo:
type: string
properties:
bar:
type: stringdid generate a model class without the properties of the root schema.
The processor will now create a model object with the properties of the root schema and all properties of the allOf schemas. In this case the merged model class will have a foo property and a bar property.
#85, duplicate properties in allOf
having a schema like:
schema:
allOf:
- type: object
properties:
foo:
type: string
- type: object
properties:
foo:
type: stringdid generate an invalid model class with two foo properties.
The processor will now use only the last foo and it will only look at the name of the property.
2022.1
openapi-processor/openapi-processor-spring#136, readOnly/writeOnly
readOnly/writeOnly on object schema properties
Foo:
type: object
properties:
barRead:
readOnly: true
allOf:
- $ref: '#/components/schemas/Bar'
barWrite:
writeOnly: true
allOf:
- $ref: '#/components/schemas/Bar'will translate to @JsonProperty annotations with read-only or write-only access:
public class Foo {
@JsonProperty(value = "barRead", access = JsonProperty.Access.READ_ONLY)
private Bar barRead;
@JsonProperty(value = "barWrite", access = JsonProperty.Access.WRITE_ONLY)
private Bar barWrite;
// ....
}#82, @Email bean validation
In case bean validation is enabled a string schema with format email
schema:
type: string
format: emailwill be annotated with @Email (javax.validation.constraints.Email)
2021.6
openapi-processor/openapi-processor-spring#133, improved error reporting
Sometimes parsing errors of the OpenAPI description were not be properly reported (e.g. by the maven plugin). Parsing/validation errors are now handled and reported at the processor level and reporting no longer depends on the plugin that calls the processor (gradle/maven).
openapi-processor/openapi-processor-spring#134, nested oneOf & anyOf
if an oneOf/anyOf was used in a schema property the processor generated code that used a non-existing class as java type for the property.
For example given the following OpenAPI description
openapi: 3.0.3
info:
title: nested composed schema
version: 1.0.0
paths:
/foo-nested-one-of:
get:
responses:
'200':
description: nested oneOf
content:
application/json:
schema:
$ref: '#/components/schemas/FooNestedOneOf'
components:
schemas:
FooNestedOneOf:
type: object
properties:
foo:
oneOf:
- $ref: '#/components/schemas/One'
- $ref: '#/components/schemas/Two'
- $ref: '#/components/schemas/Three'the processor generated the pojo as:
public class FooNestedOneOf {
@JsonProperty("foo")
private FooNestedOneOfFoo foo;
public FooNestedOneOfFoo getFoo() {
return foo;
}
public void setFoo(FooNestedOneOfFoo foo) {
this.foo = foo;
}
}where FooNestedOneOfFoo did not exist.
It is now using Object as type of ' foo` and generates:
public class FooNestedOneOf {
@JsonProperty("foo")
private Object foo;
public Object getFoo() {
return foo;
}
public void setFoo(Object foo) {
this.foo = foo;
}
}dependency updates
updated swagger parser to 2.0.28 (was 2.0.27)
2021.5.1
openapi-processor/openapi-processor-spring#132, fixed missing bean validation on request body
the processor did not always generate proper bean validation annotations on a parameter/request body. In the example below the @Size() annotation was missing on the request body array with size constraints:
openapi-processor-mapping: v2
options:
# ...
bean-validation: true
map:
types:
- type: array => java.util.List
...
requestBody:
content:
application/json:
schema:
type: array
items:
type: string
minLength: 2
maxLength: 2
...public interface Api {
@PostMapping(path = "/foo", consumes = {"application/json"})
void postFoo(@RequestBody(required = false) List<@Size(min = 2, max = 2) String> body);
}This fix includes a general change in the annotation order. The bean validation annotation moved nearer to the parameter type behind the mapping annotations. (Note that @Parameter is just a placeholder for the Spring/Micronaut mapping annotations)
before
@Mapping("/items")
void getItems(
@Size(min = 2) @Parameter String[] min,
@Size(max = 4) @Parameter String[] max,
@Size(min = 2, max = 4) @Parameter String[] minMax);after
@Mapping("/items")
void getItems(
@Parameter @Size(min = 2) String[] min,
@Parameter @Size(max = 4) String[] max,
@Parameter @Size(min = 2, max = 4) String[] minMax);2021.5
#68, improved response type handling
this is a breaking change because it changes the code that gets generated by default. To keep the old behavior set
result-stylein the mapping.yaml toall. See the example below.
old behavior
currently if an endpoint returns multiple types, a success response (typically 200 ok) and at least one error response, the processor generates endpoint methods with an Object return value (or if generic something like ResponseType<?>) to handle the (usually) type wise unrelated ok and error responses.
For example, the following endpoint:
paths:
/foo:
get:
responses:
'200':
description: json or plain text result
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
text/plain:
schema:
type: string
default:
description: error
content:
application/xml:
schema:
$ref: '#/components/schemas/Error'will produce this java interface:
public interface Api {
@Mapping("/foo")
Object getFooApplicationJson();
@Mapping("/foo")
Object getFooTextPlain();
}It generates a method for each success response with an Object response because the error and the success response (usually) do not have the same type as the error response.
Object is not a very useful result type because the compiler can't check if whatever we return in the implementation matches the openapi response type.
new behavior
Since it is common practice to handle errors by throwing exceptions (e.g. in combination with the Spring @ResponseStatus to provide the http response code) the endpoint methods don't need to return different types and it is possible to simply use the type of the success response.
With this release the processor will, by default, generate the endpoint methods with specific return types:
public interface Api {
@Mapping("/foo")
Foo getFooApplicationJson();
@Mapping("/foo")
String getFooTextPlain();
}configuration
To switch between old and new behavior there is a new mapping configuration to control the style of the return type named result-style. It has two possible values: success or all. This is currently a global switch.
The default is success, i.e. the processor will automatically generate the code using the new behavior. In case the old behavior is required set the result-style to all.
openapi-processor-mapping: v2
options:
package-name: ...
map:
#result-style: success # use the success result type, this is the default
result-style: all # use an Object return type