annotation mapping & bean-validation
16. October 2022
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.
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 the 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: 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.
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
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.
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.
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:
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.