Annotation mapping
It is possible to add additional annotations to a source type
. 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 mapping:
it adds an annotation to a parameter of the
source type
orparameter name
(this includes request body parameters).
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 mapping is similar to other mappings and is defined like this:
type: {source type} @ {annotation type}
-
type is required.
-
{source type} is the type name used in the OpenAPI description and names the type that should receive the additional annotation. This can be a {type}:{format} combination like
string:uuid
. -
{annotation type} is the fully qualified class name of the java annotation type.It may have parameters (see example below).
-
or with parameter name:
since 2024.1
name: {parameter name} @ {annotation type}
-
name is required.
-
{parameter name} is the name of the parameter in the generated interface that should receive the additional annotation.
-
{annotation type} is the fully qualified class name of the java annotation type.It may have parameters (see example below).
-
Here is a list of examples using different parameters:
- type: Foo @ annotation.Bar
- type: Foo @ annotation.Bar()
- type: Foo @ annotation.Bar("bar")
- type: Foo @ annotation.Bar(2)
- type: Foo @ annotation.Bar(true)
- type: Foo @ annotation.Bar(package.Foobar.class) (1)
- type: Foo @ annotation.Bar(value = "bar", foo = 2)
1 | since 2023.2 use a class as annotation parameter. |
object
source type
since 2023.3
it is also possible to add an annotation to all generated schema/model classes using a single annotation mapping:
- type: object @ annotation
The object
string represents all generated object classes (i.e. schema/model classes) and will add the given annotation only at the class level.
For example, a mapping like this:
map:
types:
- type: object @ lombok.Builder
@Builder
@Generated(...)
public class Foo {
...
}
The samples project has a small example using annotation mappings.
combining annotation mapping and type mapping
since 2023.1
Previously 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: v8
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>
) like it is possible on a generated class:
@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;
// ....
}
mapping example
Given the following OpenAPI description, that describe two (echo like) endpoints that receive an object via post and return the same object. In the mapping file we add a custom bean validation annotation. It checks the sum of both properties in Foo
and Bar
.
openapi: 3.1.0
info:
title: openapi-processor-spring sample api
version: 1.0.0
paths:
/foo:
post:
tags:
- foo
summary: annotation mapping example.
description: a simple endpoint where an annotation mapping is used on the request body
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
required: true
responses:
'200':
description: echo of the source parameter
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
/bar:
post:
tags:
- bar
summary: annotation mapping example.
description: a simple endpoint where an annotation mapping is used on the request body
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Bar'
required: true
responses:
'200':
description: echo of the source parameter
content:
application/json:
schema:
$ref: '#/components/schemas/Bar'
components:
schemas:
Foo:
type: object
properties:
foo1:
type: integer
minimum: 0
foo2:
type: integer
minimum: -10
Bar:
type: object
properties:
bar1:
type: integer
bar2:
type: integer
and a mapping.yaml
with annotation type mappings:
openapi-processor-mapping: v8 (1)
options:
package-name: io.openapiprocessor.openapi
javadoc: true
format-code: true
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)
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 (or later) 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 . |
Here are the generated interfaces, first the FooApi
:
package io.openapiprocessor.openapi.api;
import io.openapiprocessor.openapi.model.Foo;
import io.openapiprocessor.samples.validations.Sum;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public interface FooApi {
/**
* annotation mapping example.
*
* <p>a simple endpoint where an annotation mapping is used on the request body
*
* @return echo of the source parameter
*/
@PostMapping(
path = "/foo",
consumes = {"application/json"},
produces = {"application/json"})
Foo postFoo(@RequestBody @Sum(value = 42) @Valid @NotNull Foo body); (1)
}
1 | here is the additional annotation. |
and the BarApi
and the Bar
class:
package io.openapiprocessor.openapi.api;
import io.openapiprocessor.openapi.model.Bar;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public interface BarApi {
/**
* annotation mapping example.
*
* <p>a simple endpoint where an annotation mapping is used on the request body
*
* @return echo of the source parameter
*/
@PostMapping(
path = "/bar",
consumes = {"application/json"},
produces = {"application/json"})
Bar postBar(@RequestBody @Valid @NotNull Bar body); (1)
}
1 | no annotation here, mapping says it should be on the class: |
package io.openapiprocessor.openapi.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openapiprocessor.samples.validations.Sum;
@Sum(24) (1)
public class Bar {
@JsonProperty("bar1")
private Integer bar1;
@JsonProperty("bar2")
private Integer bar2;
public Integer getBar1() {
return bar1;
}
public void setBar1(Integer bar1) {
this.bar1 = bar1;
}
public Integer getBar2() {
return bar2;
}
public void setBar2(Integer bar2) {
this.bar2 = bar2;
}
}
1 | and here it is :-) |