mapping OpenAPI formats

3.June 2023

Assume we have this simple (but useless ;-) OpenAPI description. It uses a custom integer format to specify that the integer parameter and response represents a year.

openapi: 3.1
info:
  title: pending API
  version: 1.0.0

paths:
  /query:
    get:
      description: echo query parameter with type mapping
      parameters:
        - name: year
          description: the year
          in: query
          required: true
          schema:
            type: integer  (1)
            format: year
      responses:
        '200':
          description: echo response
          content:
            text/plain:
              schema:
                type: integer  (1)
                format: year
1 the integer type with custom format.

By default, openapi-processor-spring does not know what to do with the custom format and simply maps the OpenAPI integer to a Java Integer.

the generated interface code
package io.openapiprocessor.oap.api;

import io.openapiprocessor.oap.support.Generated;
import javax.validation.constraints.NotNull;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Generated(value = "openapi-processor-spring", version = "2023.2")
public interface Api {

    /**
     * echo query parameter with type mapping
     *
     * @return echo response
     */
    @GetMapping(
            path = "/query",
            produces = {"text/plain"})
    Integer getQuery(@RequestParam(name = "year") @NotNull Integer year);

}

Since Java has a java.time.Year class it could be useful to use it instead of Integer.

We can easily help openapi-processor to generate the interface with java.time.Year by adding a type mapping to the mapping.yaml configuration.

mapping.yaml
openapi-processor-mapping: v3

options:
  generated-date: false
  package-name: io.openapiprocessor.oap
  bean-validation: javax
  javadoc: true

map:
  types:
    - type: integer:year => java.time.Year (1)
1 this is the interesting part of the mapping.yaml. It tells the processor to map the integer custom format to the java.time.Year class.

The left side of the arrow is the type : format combination used in the OpenAPI and on the right side is the fully qualified java class name that is used in the generated code.

Regenerating the code will now generate the interface like this:

the generated interface code
package io.openapiprocessor.oap.api;

import io.openapiprocessor.oap.support.Generated;
import java.time.Year;
import javax.validation.constraints.NotNull;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Generated(value = "openapi-processor-spring", version = "2023.2")
public interface Api {

    /**
     * echo query parameter with type mapping
     *
     * @return echo response
     */
    @GetMapping(
            path = "/query",
            produces = {"text/plain"})
    Year getQuery(@RequestParam(name = "year") @NotNull Year year); (1)

}
1 it is now using java.time.Year instead of Integer.

Easy :-)

If such a custom type is used in the properties of an OpenAPI schema, openapi-processor will use the same type mapping definition.

We can use this simple mechanism for many other types to improve the generated code. Here are a few examples:

type mapping examples
  - type: string:uuid => java.util.UUID
  - type: string:date-time => java.time.Instant
  - type: string:offset-date-time => java.time.OffsetDateTime

I’m sure you can find more useful mappings. :-)

summary

This short article described how easy it is to improve the openapi-processor generated code with type mapping.