The openapi-processor-spring/micronaut release 2022.5 adds a new annotation type mapping feature. It provides the possibility to add additional annotations to generated interfaces & classes.
what is openapi-processor?
openapi-processor is a small framework to process OpenAPI yaml files. Currently, openapi-processor provides java code generation for Spring Boot & Micronaut.
It does support gradle and maven with plugins to convert OpenAPI yaml files to java (controller) interfaces & (payload) pojo classes as part of the build process.
It generates Java because the controller interface and pojo classes are easily usable from other jvm languages. Usually you will just use them without looking at the generated code.
annotation mapping
annotation type mapping allows to add annotations to a generated model class or to an endpoint method parameters of that class.
Let's look at a contrived example to add a custom bean validation to the pojo model class of a schema.
See annotation mapping documentation for more.
the example api
here is a simple api that takes a Foo schema as request body. The schema has some (useless ;-) number constraints on its properties.
openapi.yaml
openapi: 3.1.0
info:
title: annotation mapping example
version: 1.0.0
paths:
/foo:
post:
summary: annotation mapping example endpoint.
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:
'201':
description: empty response
components:
schemas:
Foo:
type: object
properties:
foo1:
type: integer
minimum: 0
foo2:
type: integer
minimum: -10
bean validation annotations
Enabling bean validation in the mapping.yaml
(the processor configuration) will generate a Foo
class with bean validation annotations for the property constraints.
generated file
package io.openapiprocessor.openapi.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openapiprocessor.openapi.support.Generated;
import javax.validation.constraints.DecimalMin;
@Generated(value = "openapi-processor-spring", version = "2022.5")
public class Foo {
@DecimalMin(value = "0") // <1>
@JsonProperty("foo1")
private Integer foo1;
@DecimalMin(value = "-10") // <1>
@JsonProperty("foo2")
private Integer foo2;
public Integer getFoo1() {
return foo1;
}
public void setFoo1(Integer foo1) {
this.foo1 = foo1;
}
public Integer getFoo2() {
return foo2;
}
public void setFoo2(Integer foo2) {
this.foo2 = foo2;
}
}
<1> the bean validation annotations created from the OpenAPI constraints.
custom bean validation annotation
Now we like to add a validation that checks the sum of the two Integer
properties by writing @Sum(24)
.
Let's create the annotation
manually created custom bean validation annotation
package io.openapiprocessor.samples.validations;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Constraint (validatedBy = {FooSumValidator.class})
@Target ({ ElementType.TYPE, ElementType.PARAMETER })
@Retention (value = RetentionPolicy.RUNTIME)
public @interface Sum {
String message () default "invalid sum";
Class<?>[] groups () default {};
Class<? extends Payload>[] payload () default {};
int value();
}
and the validation code.
manually created validation
package io.openapiprocessor.samples.validations;
import io.openapiprocessor.openapi.model.Foo;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class FooSumValidator implements ConstraintValidator<Sum, Foo> {
private Integer sum;
@Override
public void initialize (Sum constraintAnnotation) {
sum = constraintAnnotation.value ();
}
@Override
public boolean isValid (Foo value, ConstraintValidatorContext context) {
return value.getFoo1 () + value.getFoo2 () == sum;
}
}
mapping for the custom annotation
Now comes the interesting part, the annotation type mapping that tells the processor to add our custom annotation to the generated Foo
pojo model class.
processor configuration mapping.yaml
openapi-processor-mapping: v2.1 # <1>
options:
package-name: io.openapiprocessor.openapi
bean-validation: true
map:
types:
# <2>
- type: Foo @ io.openapiprocessor.samples.validations.Sum(24)
<1> the new mapping version. Using another version will produce a warning that the mapping is invalid.
<2> the annotation mapping that tells the processor to @nnotate the Foo
schema (Foo
is the name of the OpenAPI schema) with the given annotation to the pojo model class generated for the Foo
schema. The annotation is given with the fully qualified name (required to create the import) and (optionally) with fixed parameters.
model class with custom annotation
Now, with the annotation mapping in the configuration the processor will generate Foo
like this:
generated file with custom annotation
package io.openapiprocessor.openapi.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openapiprocessor.openapi.support.Generated;
import javax.validation.constraints.DecimalMin;
import io.openapiprocessor.samples.validations.Sum;
@Sum(24) // <1>
@Generated(value = "openapi-processor-spring", version = "2022.5")
public class Foo {
@DecimalMin(value = "0")
@JsonProperty("foo1")
private Integer foo1;
@DecimalMin(value = "-10")
@JsonProperty("foo2")
private Integer foo2;
public Integer getFoo1() {
return foo1;
}
public void setFoo1(Integer foo1) {
this.foo1 = foo1;
}
public Integer getFoo2() {
return foo2;
}
public void setFoo2(Integer foo2) {
this.foo2 = foo2;
}
}
<1> our custom bean validation annotation.
summary
This little article described how to add a custom annotation to a generated class by adding an annotation type mapping to the processor mapping configuration.
To learn more about openapi-processor and how to generate controller interfaces and model classes from an OpenAPI description take a look at the documentation.
Top comments (0)