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 type mapping:

    it adds an annotation to a parameter of the source type. Since the request body is passed as parameter the mapping will work for it too.

It is defined like below, and it should be added to the map/types or map/parameters section in the mapping.yaml.

It is also available as an endpoint (http method) mapping to restrict 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}
  • type is required.

    • {source type} is the type name used in the OpenAPI description and names the type 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).

The samples project has a small example using annotation mappings similar to the one described below.

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: v2

options:
  package-name: io.openapiprocessor.openapi
  javadoc: true
  format-code: true
  bean-validation: true

map:
  types:
    - type: Bar @ io.openapiprocessor.samples.validations.Sum(24) (1)

  parameters:
    - type: Foo @ io.openapiprocessor.samples.validations.Sum(value = 42) (2)

      # this formats do work too (3)
      # - 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 the Bar mapping is using a global type annotation mapping, so the annotation is added to the generated Bar class.
2 the Foo mapping adds the annotation to the parameter of the endpoint methods that use Foo.
3 this is a list of examples that shows annotation parameters. It is nearly java code.

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)

}
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 :-)