What’s New
October 2024
-
openapi-processor-spring/micronaut 2024.5
support for servers/server/url
it is now possible to tell the processor to generate a properties resource file with the path of a selected OpenAPI servers/server/url
.
Given an OpenAPI description with a servers
key:
openapi: 3.1.0
info:
title: server url example
version: 1.0.0
servers:
- url: "https://openapiprocessor.io/{path}"
variables:
path:
default: api
# ...
and a mapping
openapi-processor-mapping: v9
options:
base-path:
# false/true=0,1,2,... (default false)
server-url: true
it will generate a properties file api.properties
openapi.base.path = /api
that can be used to configure the (Spring) context-path
:
# application.properties
#spring.config.import = api.properties
server.servlet.context-path=${openapi.base.path}
See Server Url for more.
javadoc of record
a record should have its javadoc at the record using @param
s to describe the record properties.
Instead of
/**
* this is the <em>Foo</em> schema description
*/
@Generated(value = "openapi-processor-core", version = "test")
public record Foo(
/**
* <em>property</em> description
*/
@JsonProperty("foo-bar")
String fooBar
) {}
the processor now generates:
/**
* this is the <em>Foo</em> schema description
*
* @param fooBar <em>property</em> description
*/
@Generated(value = "openapi-processor-core", version = "test")
public record Foo(
@JsonProperty("foo-bar")
String fooBar
) {}
warn on endpoint without success response
the processor ignores endpoints that have no success response (i.e. 2xx response code). To detect this "error" at compile time the processor will now print a warning with the effected endpoint.
June 2024
-
openapi-processor-spring/micronaut 2024.4
add request body description to javadoc
The request body description is added as @param
to the generated javadoc.
openapi: 3.1.0
info:
title: javadoc
version: v1
paths:
/foo:
get:
requestBody:
description: this is the request body
# ...
missing @Generated
the generated Values
and ValueValidator
(used by enum-type
string
) were not annotated with @Generated
.
missing import of class annotation parameter
using a .class
parameter in a class annotation mapping did not add the import of the parameter class.
map:
types:
- type: Foo @ io.oap.ClassAnnotation(value = io.oap.Param.class)
In this example the import for Param
was missing.
disable @Generated
its is now possible to disable the @Generated
annotation. If it is disabled the processor will not add it to any generated type.
openapi-processor-mapping: v8
options:
# ...
# enable/disable generated annotation, true (default) or false.
generated-annotation: false
control @JsonProperty
annotation
By setting the json-property-annotation
option is is possible to control the generation of the @JsonProperty annotation. It allows three values: always
, auto
or never
.
-
always
: (the default) adds a@JsonProperty
annotation to all properties. -
auto
: only adds a@JsonProperty
annotation to a property if it is required, i.e. if the OpenAPI property name is not a valid java identifier or if a property uses thereadOnly
/writeOnly
(OpenAPI) flags. -
never
: never adds a@JsonProperty
annotation to the properties. This may generated invalid code if the property name is not a valid java identifier.
openapi-processor-mapping: v8
options:
# ...
# control @JsonProperty annotation, always (default), auto, never.
json-property-annotation: auto
May 2024
-
openapi-processor-spring/micronaut 2024.3
response $ref did not work
using responses
with `$ref`s did not work with all (supported) OpenAPI parsers.
-
internal OpenAPI parser did not work (the default parser).
-
openapi4j did not work (not maintained anymore).
-
Swagger parser worked.
It now works for all 3 (supported) OpenAPI parsers.
bad enum creation
the processor did not create a proper enum for an enum description like this:
components:
schemas:
AnEnum:
type: string
enum:
- "1"
- "2"
because 1
and 2
are not valid java identifiers, the processor generated
public enum Enum {
INVALID("1"),
INVALID("2");
...
}
The processor will now prefix invalid identifiers with "v" (value) to avoid this. The enum above will produce
public enum Enum {
V1("1"),
V2("2");
...
}
missing constraints with null
mapping
using a null mapping:
openapi-processor-mapping: v7
options:
bean-validation: true
map:
paths:
/foo:
null: org.openapitools.jackson.nullable.JsonNullable
on a property
properties:
bar:
nullable: true
type: string
maxLength: 4
did not add the constraint to the generated property.
April 2024
-
openapi-processor-spring/micronaut 2024.2
setting the new compatibility options did not work
fixed setting the new compatibility options, it was always using the default values.
February 2024
-
openapi-processor-spring/micronaut 2024.1
annotation mapping by OpenAPI extensions
it is now possible to use OpenAPI `x-`tensions to add additional annotations to schema properties:
Here is a simple schema that has x-`tensions on the `bar
property.
openapi: 3.1.0
# ...
components:
schemas:
Foo:
type: object
properties:
bar:
type: string
x-foo: single
x-bar:
- listA
- listB
we can now map the `x-`tensions/values to annotations like this:
openapi-processor-mapping: v6
map:
extensions:
x-foo: single @ io.oap.FooA(value = "any")
x-bar:
- listA @ io.oap.FooB
- listB @ io.oap.FooC
-
which will generate the additional annotations on the property:
package generated.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;
import io.oap.FooA;
import io.oap.FooB;
import io.oap.FooC;
@Generated(value = "openapi-processor-core", version = "test")
public class Foo {
@FooA(value = "any")
@FooB
@FooC
@JsonProperty("bar")
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
annotation mapping by parameter name
another small improvement to annotation mapping is that we can add annotations by parameter name:
openapi-processor-mapping: v6
map:
parameters:
- name: foo @ annotation.Foo
reactive bean validation
the position of the @Valid
annotation on reactive types has changed.
Until now the @Valid
was placed on the generic type of the reactive wrapper, like this:
@Mapping("/foo-flux")
void postFooFlux(@Parameter Flux<@Valid Bar> body);
but validation did not happen with Spring. Spring needs the @Valid
on the reactive wrapper to trigger the validation. Therefore @Valid
is now placed by default on the reactive wrapper:
@Mapping("/foo-flux")
void postFooFlux(@Parameter @Valid Flux<Bar> body);
It should only take a bit annotation clean up on the interface implementations to adapt your code to the new @Valid
position.
keeping the old behavior
To postpone the update, set the bean-validation-valid-on-reactive
option to false
.
openapi-processor-mapping: v6
options:
# ...
compatibility:
# optional, default is true
bean-validation-valid-on-reactive: false
I would like to remove this option in the future. If you still need the old @Valid
position please create an issue to help me understand why the old @Valid
position is still useful.
identifier word breaks
the processor does now recognize a change from letter to number as a word break. The improves generation of camel case identifiers.
given an identifier from the OpenAPI description, the processor would generate the following names for different kinds of identifiers:
OpenAPI | camel case | variable | class | enum | |
---|---|---|---|---|---|
new |
foo2Bar |
foo2Bar |
foo2Bar |
Foo2Bar |
FOO2_BAR |
old |
foo2Bar |
foo2bar |
foo2bar |
Foo2bar |
FOO2BAR |
Support Mono<ResponseEntity> as result type
previous versions allowed to configure a result wrapper (e.g. Spring ResponseEntity
) and reactive types via single
and multi
mapping.
openapi-processor-mapping: v6
options:
# ...
map:
result: org.springframework.http.ResponseEntity
single: reactor.core.publisher.Mono
multi: reactor.core.publisher.Flux
Using both always wraps the reactive types with the result
type. For example with Spring ResponseEntity
(result type
) and the reactor types Mono
and Flux
as
ResponseEntity<Mono<...>>
ResponseEntity<Flux<...>>
Unfortunately if you need the reactive result to modify the http response, something like this:
// does not work
public ResponseEntity<Mono<Result>> someEndpoint() {
return someBean.getResult()
.map(r -> ResponseEntity
.ok()
.eTag(r.eTag())
.body(Mono.just(r)));
}
it will not work because the final type of the statement is Mono<ResponseEntity<Mono<Result>>>
and not the expected ResponseEntity<Mono<Result>>
.
With this release we can fix that by setting the result
mapping to
openapi-processor-mapping: v6
options:
# ...
map:
# wrap the ResponseEntity with Mono
result: reactor.core.publisher.Mono<org.springframework.http.ResponseEntity>
single: reactor.core.publisher.Mono
multi: reactor.core.publisher.Flux
which will generate the endpoint signature as
public Mono<ResponseEntity<Mono<Result>>> someEndpoint() {
// ...
}
and the above code will now work.
It is recommended to configure this on the endpoint level if you just need this for a few endpoints.
See also Spring ResponseEntity
documentation.
November 2023
-
openapi-processor-spring/micronaut 2023.6
support different enum styles
it is now possible to (globally) configure different enum types in mapping.yaml
:
openapi-processor-mapping: v5
options:
package-name: generated
enum-type: default|string|framework
default
, which is default, creates a simple java enum with all uppercase enum values. It will create the same code as previous versions.
string
, simply uses String
and does not create an enum class. This is useful if automatic conversion of the incoming value to a java enum value does not work. In case bean-validation
is enabled the processor adds a (generated) validation annotation that verifies that the string is a valid (OpenAPI) enum value.
public interface FooApi {
@Mapping(path = "/foo", produces = {"application/json"})
Foo postFoo(@Parameter(name = "enum") @Values(values = {"one", "two"}) String aEnum);
}
framework
, is a placeholder for framework specific enum generation, only supported by openapi-processor-spring.
It generates a Java enum class similar to the default
enum type. In addition, it generates a Spring ConverterFactory
that Spring can use to convert incoming enum values to its corresponding java enum value (if passed as a parameter, not as part of the body).
To enable the ConverterFactory
, create a configuration class like this:
package io.openapiprocessor.samples;
import {package-name}.spring.StringToEnumConverterFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@SuppressWarnings("rawtypes")
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new StringToEnumConverterFactory());
}
}
This registration is one time task. The converter factory does create an enum converter for all generated enum classes.
There is a sample that is using different enum-type
configurations (string
& framework
).
September 2023
-
openapi-processor-spring/micronaut 2023.4
json schema validation for OpenAPI 3.1
the processor does now run json schema validation on OpenAPI 3.1 documents.
automatically clear output directory
when called, the processor clears the target directory to avoid any left over/obsolete files from previous runs.
accept generic wildcard in mapping
it is now possible to use the generic ?
in a mapping.
openapi-processor-mapping: v4
options:
package-name: io.openapiprocessor.generated
map:
parameters:
- add: foo => io.openapiprocessor.GenericType<?>
Spring: missing import
Spring doesn’t have shortcut annotations, e.g. @GetMapping
instead of @RequestMapping(…, method = RequestMethod.HEAD)
for the http methods HEAD, OPTIONS & TRACE.
The processor does now add the missing import of RequestMethod
.
improved json schema of mapping.yaml
when using endpoint specific mappings the processor (sometimes) complained about invalid entries in the mapping.yaml
. This was caused by wrong definitions in the corresponding json schema.
don’t request json schema draft
a miss-configuration of the json-schema validator caused an unnecessary network request to download a json schema (draft 7) from https://json-schema.org.
July 2023
-
openapi-processor-spring/micronaut 2023.3
object @ annotation
Using the object
keyword it is possible to add an annotation to all generated schema/model classes using a single annotation mapping:
map:
types:
- type: object @ lombok.Builder
The object
string represents all generated object classes (i.e. schema/model classes) and will add the given annotation only at the class level:
@Builder
@Generated(...)
public class Foo {
...
}
java records
openapi-processor is now capable of generating java record
s instead of pojos for schemas. This is a global setting in the mapping.yaml
. It can either have the value default
(which is default) to generate pojos or record
to generate records.
openapi-processor-mapping: v4
options:
model-type: record
With model-type: record
the processor will generate record
s like this:
package generated.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;
@Generated(value = "openapi-processor-core", version = "test")
public record Foo(
@JsonProperty("bar")
String bar
) {}
and without model-type
or model-type: default
it will create a simple pojo:
package generated.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;
@Generated(value = "openapi-processor-core", version = "test")
public class Foo {
@JsonProperty("bar")
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
java-format
option
openapi-processor uses google-java-format to format the generated files (including javadoc). Unfortunately it depends on internal jdk packages that are strongly encapsulated since JDK 16. It is necessary to tell the jdk to export a few packages.
In theory, it is not hard to configure it but in real life it is a bit fiddly to get this working. To get started without fighting with it, the default is now false
instead of true
.
To (re-)enable code formatting add the format-code
option to the mapping.yaml
:
openapi-processor-mapping: v4
options:
format-code: true
June 2023
-
openapi-processor-spring/micronaut 2023.2
nested generic types
It is now possible to create type mappings with nested generics types. Here are a few examples:
openapi-processor-mapping: v3
options:
package-name: generated
map:
types:
- type: Foo => java.util.Map<java.lang.String, java.util.List<java.lang.String>>
paths:
/foo:
responses:
- content: application/json => java.util.Map<java.lang.String, java.lang.String>
/foo2:
responses:
- content: application/json => java.util.Map<java.lang.String, java.util.List<java.lang.String>>
This is useful to map an OpenAPI dictionary description using additionalProperties
to a proper java map type:
# a schema the defines a dictionary with string keys and string values
Map:
type: object
additionalProperties:
type: string
annotation mapping allows class parameter
annotation mapping now accepts a java class type as parameter. It is possible to add a mapping like this:
openapi-processor-mapping: v3
map:
types:
- type: string:foo @ io.oap.Annotation (value = io.oap.Bar.class)
improved validation output
schema validation by the internal parser has simpler & better output based on the JSON schema basic output format. It is not perfect but it is getting better.
It will provide better help on where the error is, but it may report multiple ambiguous errors.
If a schema property uses anyOf
or oneOf
and all possibilities don’t match (e.g. because there is a spelling error) the validator can’t know which one was meant and complains about all of them.
An example:
the error the value does not validate against the 'false' schema at instance …
usually means that a property has a spelling error.
If the OpenAPI allows a $ref
at the same location the validator reports a second error should have a property '$ref' at instance …
because a reference object must have a $ref
property.
January 2023
-
openapi-processor-spring/micronaut 2023.1
support requestBody
$ref
the processor is now able to resolve $ref
s of requestBody
(This works with all 3 OpenAPI parsers).
openapi: 3.1.0
info:
title: components requestBodies
version: '1.0'
paths:
/foo:
post:
responses:
'200':
description: ok
content:
application/json:
schema:
type: string
requestBody:
$ref: '#/components/requestBodies/Foo' (1)
components:
requestBodies:
Foo:
content:
application/json:
schema:
type: object
properties:
foo:
type: string
1 | $ref is direct child of requestBody . |
annotation mapping support for simple data types
it is now possible to add an annotation mapping for simple data types (format works too):
openapi-processor-mapping: v3
map:
types:
- type: string:uuid => java.util.UUID
- type: string:uuid @ annotation.Bar
openapi-processor will add it on any string:uuid
property used in the generated model classes:
@Generated
public class Foo {
@Bar
@JsonProperty("foo")
private UUID foo;
// ....
}
annotation mapping support for mapped types
in the previous version an annotation mapping was lost if the type was mapped at the same time to an existing class. It will now add the annotation to the existing class if possible.
Assume the following mapping:
openapi-processor-mapping: v3
options:
map:
types:
- type: Foo => openapiprocessor.MappedFoo
- type: Foo @ annotation.Bar (1)
parameters:
- type: Foo @ annotation.Bar (2)
MappedFoo
is a class that is not generated. Adding an annotation at the parameter level works as expected (mapping <2>
). But it is not possible to add the Bar
annotation directly at the class (mapping <1>
):
@Bar
@Generated
public class Foo {
// ....
}
instead, openapi-processor will add it on any MappedFoo
property used in the generated model classes:
@Generated
public class FooBar {
@Bar
@JsonProperty("foo")
private MappedFoo foo;
// ....
}
bean validation v3 support
Spring Boot 3 updates bean validations to v3. In v3 the package name changed from javax
to jakarta
. It is now possible to select between the v2 & v3 version in the mapping.yaml
.
the new mapping schema v3 adds javax
and jakarta
as possible values for the bean-validation
option. true/false
will still work as before.
# use v3 for proper validation of the mapping file
openapi-processor-mapping: v3
options:
# no bean validation, as before
bean-validation: false
# enable bean validation, as before (will use `javax...`)
bean-validation: true
# new: enable bean validation with `javax...`
bean-validation: javax
# new: enable bean validation with `jakarta...`
bean-validation: jakarta
bean validation support on mapped data types
openapi-processor now preserves bean validation annotations when the source data type is mapped to an existing class. This is most interesting for the @Valid
annotation.
It adds the annotations it would add on the source data type. In previous versions the annotations got lost when the data type was mapped to an existing class. Without`@Valid` the validation would not be triggered on the mapped object.
having this OpenAPI description
openapi: 3.1.0
info:
title: mapped bean validation
version: 1.0.0
paths:
/foo:
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
responses:
204:
description: none
components:
schemas:
Foo:
type: object
properties:
foo:
type: integer
minimum: 0
the endpoint looks like this without a mapping that replaces Foo
(ignore the @Mapping
/@Parameter
annotations, this is pseudocode used by the integration tests):
package generated.api;
import annotation.Mapping;
import annotation.Parameter;
import generated.model.Foo;
import javax.validation.Valid;
public interface Api {
@Mapping("/foo")
void postFoo(@Parameter @Valid Foo body); // has @Valid annotation
}
with a mapping that replaces Foo
with Bar
openapi-processor-mapping: v3
options:
package-name: generated
bean-validation: true
map:
types:
- type: Foo => openapiprocessor.Bar
it will now generate the endpoint with a @Valid
on the mapped data type.
package generated.api;
import annotation.Mapping;
import annotation.Parameter;
import javax.validation.Valid;
import openapiprocessor.Bar;
public interface Api {
@Mapping("/foo")
void postFoo(@Parameter @Valid Bar body); // new: has @Valid annotation
}