Search code examples
javaopenapi-generator-maven-plugintype-mapping

Represent java.time.LocalDate and java.time.OffsetDateTime as java.time.Insant


I want to use typemappings from the openapi-generator-maven-plugin. My openapi spec file looks like the following:

paths:
  /maintenance-windows-avr/{day}:
    get:
      summary: "Get active maintenance windows for day"
      tags:
        - Maintenance Windows AVR
      operationId: getMaintenanceWindows
      parameters:
        - in: path
          name: day
          required: true
          schema:
            type: string
            format: date
          description: day to return active maintenance windows
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                type: object
                items:
                  $ref: "#/components/schemas/ResponseListMaintenanceWindowAVRDto"
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorDto"
components:
  schemas:
    ErrorDto:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string

    MaintenanceWindowAVRDto:
      type: object
      required:
        - id
        - start
        - end
        - hosts
      properties:
        id:
          type: string
          format: uuid
          description: id of maintenance window
        start:
          type: string
          format: date-time
          description: when the maintenance window is going to start
        end:
          type: string
          format: date-time
          description: when the maintenance is going to end
        hosts:
          type: array
          items:
            type: long
            format: int64
            description: list of host ids

    ResponseListMaintenanceWindowAVRDto:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/MaintenanceWindowAVRDto"

Which generates me the files MaintenanceWindowsAvrApi and MaintenanceWindowAVRDto which look like the following:

package eu.nts.nmp.csm.mac.api.generated.endpoint.avl;

import eu.nts.nmp.csm.mac.api.generated.model.avl.ErrorDto;
import java.time.LocalDate;
import eu.nts.nmp.csm.mac.api.generated.model.avl.ResponseListMaintenanceWindowAVRDto;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;


import java.io.InputStream;
import java.util.Map;
import java.util.List;
import javax.validation.constraints.*;
import javax.validation.Valid;

@Path("/maintenance-windows-avr/{day}")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2023-03-14T16:46:37.723499+01:00[Europe/Vienna]")public interface MaintenanceWindowsAvrApi {

    @GET
    @Produces({ "application/json" })
    Response getMaintenanceWindows(@PathParam("day") LocalDate day);
}
package eu.nts.nmp.csm.mac.api.generated.model.avl;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.validation.constraints.*;
import javax.validation.Valid;

import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;



@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2023-03-14T16:46:37.723499+01:00[Europe/Vienna]")public class MaintenanceWindowAVRDto   {
  
  private @Valid UUID id;
  private @Valid OffsetDateTime start;
  private @Valid OffsetDateTime end;
  private @Valid List<Long> hosts = new ArrayList<>();

  /**
   * id of maintenance window
   **/
  public MaintenanceWindowAVRDto id(UUID id) {
    this.id = id;
    return this;
  }

  

  
  @JsonProperty("id")
  @NotNull
  public UUID getId() {
    return id;
  }

  public void setId(UUID id) {
    this.id = id;
  }

/**
   * when the maintenance window is going to start
   **/
  public MaintenanceWindowAVRDto start(OffsetDateTime start) {
    this.start = start;
    return this;
  }

  

  
  @JsonProperty("start")
  @NotNull
  public OffsetDateTime getStart() {
    return start;
  }

  public void setStart(OffsetDateTime start) {
    this.start = start;
  }

/**
   * when the maintenance is going to end
   **/
  public MaintenanceWindowAVRDto end(OffsetDateTime end) {
    this.end = end;
    return this;
  }

  

  
  @JsonProperty("end")
  @NotNull
  public OffsetDateTime getEnd() {
    return end;
  }

  public void setEnd(OffsetDateTime end) {
    this.end = end;
  }

/**
   **/
  public MaintenanceWindowAVRDto hosts(List<Long> hosts) {
    this.hosts = hosts;
    return this;
  }

  

  
  @JsonProperty("hosts")
  @NotNull
  public List<Long> getHosts() {
    return hosts;
  }

  public void setHosts(List<Long> hosts) {
    this.hosts = hosts;
  }


  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    MaintenanceWindowAVRDto maintenanceWindowAVRDto = (MaintenanceWindowAVRDto) o;
    return Objects.equals(this.id, maintenanceWindowAVRDto.id) &&
        Objects.equals(this.start, maintenanceWindowAVRDto.start) &&
        Objects.equals(this.end, maintenanceWindowAVRDto.end) &&
        Objects.equals(this.hosts, maintenanceWindowAVRDto.hosts);
  }

  @Override
  public int hashCode() {
    return Objects.hash(id, start, end, hosts);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class MaintenanceWindowAVRDto {\n");
    
    sb.append("    id: ").append(toIndentedString(id)).append("\n");
    sb.append("    start: ").append(toIndentedString(start)).append("\n");
    sb.append("    end: ").append(toIndentedString(end)).append("\n");
    sb.append("    hosts: ").append(toIndentedString(hosts)).append("\n");
    sb.append("}");
    return sb.toString();
  }

  /**
   * Convert the given object to string with each line indented by 4 spaces
   * (except the first line).
   */
  private String toIndentedString(Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }


}

I want to map the LocalDate parameter in MaintenanceWindowsAvrApi file and the two OffsetDateTime fields start and end from the MaintenanceWindowAVRDto file to an Instant (java.time.Instant).

I have tried so many options but nothing gives me the result I want. My api pom file:

  <inputSpec>${project.basedir}/src/main/resources/META-INF/openapi-avr.yaml</inputSpec>
                            <generatorName>jaxrs-spec</generatorName>
                            
                            <configOptions>
                                <useSwaggerAnnotations>false</useSwaggerAnnotations>
                                <interfaceOnly>true</interfaceOnly>
                                <generatePom>false</generatePom>
                                <returnResponse>true</returnResponse>
                                <dateLibrary>java8</dateLibrary>
                                <java8>true</java8>
                                <useFlags>true</useFlags>
                                <sourceFolder>src/gen/java/main</sourceFolder>
                            </configOptions>

How can I use the typeMappings to resolve my problem?


Solution

  • Solution

    Add typeMappings configuration like this:

        <inputSpec>${project.basedir}/src/main/resources/META-INF/openapi-avr.yaml</inputSpec>
        <generatorName>jaxrs-spec</generatorName>
    
        <configOptions>
          <!-- [...] -->
        </configOptions>
    
        <typeMappings>
            <typeMapping>LocalDate=java.time.Instant</typeMapping>
            <typeMapping>OffsetDateTime=java.time.Instant</typeMapping>
        </typeMappings>
    

    This will replace every occurence of LocalDateTime and OffsetDateTime in the generated code by java.time.Instant. The import sections will not change. I.e. they will still contain and import java.time.LocalDate; and import java.time.OffsetDateTime;, even though these imports are not used anymore.

    Documentation about typeMappings can be found here and here.

    OpenAPI Generator Bug

    The intended usage of OpenAPI Generator would be to set

      <typeMapping>LocalDate=java.time.Instant</typeMapping>
    

    and

      <importMapping>Instant=java.time.Instant</importMapping>
    

    (And likewise for OffsetDateTime.)

    Then imports of LocalDate and OffsetDateTime would be replaced by import java.time.Instant; and Instant would be used in the code instead of java.time.Instant.

    However, there is a bug in OpenAPI Generator that prevents the usage of importMapping in Java-based generators. Therefore we currently need to configure the type mappings as shown in my solution above.

    unknown config option

    By the way, I think the useFlags setting in your code can be removed, because this is not a valid config option for OpenAPI Generator.