Search code examples
kotlinopenapiopenapi-generatoropenapi-generator-gradle-plugin

OpenAPI Generator with 3 date types (LocalDate, LocalDateTime, ZonedDateTime)


I have an existing service that is manually written with Spring controllers which I am trying to migrate to using OpenAPI Generator. I've encountered the following blocking problem:

  1. The server is currently configured with APIs that return 3 different date types:
  • LocalDate
  • LocalDateTime
  • ZonedDateTime
  1. The OpenAPI specification supports two date types/formats:
  • date
  • date-time
  1. OpenAPI Generator Kotlin produces LocalDate and OffsetDateTime for the above two formats (respectively) by default. The generator can be configured to replace OffsetDateTime with ZonedDateTime, e.g.:
typeMappings.set(mapOf("DateTime" to "ZonedDateTime"))
importMappings.set(mapOf("ZonedDateTime" to "java.time.ZonedDateTime"))
  1. However, for existing APIs that return LocalDateTime, they are not left with a compatible data type. They must be configured as either date (LocalDate) or date-time (ZonedDateTime), neither of which have a compatible wire format for clients that are expecting and attempting to deserialize a LocalDateTime.

I'm aware that LocalDateTime is a bad data type to use for APIs since it lacks an explicit time zone, but this is blocking me from migrating the service to using OpenAPI Generator. The only workaround I can think of is treating the LocalDateTime properties as simple strings. Are there any better workarounds that would allow me to continue to use LocalDateTime?


Solution

  • Using strings could be a viable temporary option, if your clients are expected to migrate soon to an api, which is consuming timestamps with zone. It seems like you are generating your openApi-Code via gradle. You could use two seperate gradle tasks for generating code from two separate openApi-specs, each with its respecting typeMappings/importMappings. For example:

    tasks.register<GenerateTask>("generateCodeForZonedDateTime"){
        typeMappings.set(mapOf("DateTime" to "ZonedDateTime"))
        importMappings.set(mapOf("ZonedDateTime" to "java.time.ZonedDateTime"))
        modelPackage.set("com.example.codeforzoneddatetime")
        apiPackage.set("com.example.codeforzoneddatetime"
        inputSpec.set("openApiSpecWithEndpointsReturningZonedDateTime.yaml")
    }
    
    tasks.register<GenerateTask>("generateCodeForLocalDateTime"){
        typeMappings.set(mapOf("DateTime" to "LocalDateTime"))
        importMappings.set(mapOf("LocalDateTime" to "java.time.LocalDateTime"))
        modelPackage.set("com.example.codeforlocaldatetime")
        apiPackage.set("com.example.codeforlocaldatetime")
        inputSpec.set("openApiSpecWithEndpointsReturningLocalDateTime.yaml")
    }
    

    Your controller which is returning ZoneDateTimes would import
    com.example.codeforzoneddatetime
    and your controller returning LocalDateTimes would import code from
    com.example.codeforlocaldatetime

    If your clients are ready to take only ZonedDateTimes, you can use a single openApi-spec and a single GenerateTask again.