cjbooms / fabrikt

Generates Kotlin Code from OpenApi3 Specifications
Apache License 2.0
151 stars 39 forks source link

Wrapper classes for primitive types #114

Open Gama11 opened 2 years ago

Gama11 commented 2 years ago

In our codebase, we have a lot of single-argument data classes to wrap raw strings / integers / UUIDs / etc. This improves type safety (mixing up different things that happen to have the same type now causes a compiler error) and readability. With openapi-generator, we were able to get the generated models to use these (hand-written) wrappers by using their type and import mapping features.

However, I'm thinking that ideally, the wrappers would be generated as well. Basically, the idea would be that if there is an explicit named schema, it is always generated. Right now, the following simply "inlines" the AccountToken schema, so Account has a token: UUID argument:

openapi: 3.0.0
components:
  schemas:
    Account:
      type: object
      required:
        - token
      properties:
        token:
          $ref: '#/components/schemas/AccountToken'
    AccountToken:
      type: string
      format: uuid

With a potential new CLI flag, it could instead generate something like this instead:

data class Account(
  @param:JsonProperty("token")
  @get:JsonProperty("token")
  @get:NotNull
  val token: AccountToken
)

data class AccountToken(
  @get:JsonValue
  @get:NotNull
  val value: UUID
)

The @get:JsonValue ensures that Jackson (de)serialization works as expected, i.e. treating it as a JSON string rather than a JSON object. In the future, it might even make sense for the wrappers to be @JvmInline value classes, right now it seems Jackson doesn't like those.


The other complication here (and technically a separate problem) is that we have a lot of these wrapper schemas in shared spec files (think $ref: 'shared/tokens.yml#/components/schemas/AccountToken') used by many different APIs, and we generate each API into a separate Gradle module. By default that would lead to a lot of duplicated types being generated that are not compatible with each other. To avoid that we would need some mechanism to say "if the schema comes from a separate yml file, don't generate a type and instead assume it can be found in this package" (so perhaps a map from .yml file path to package paths).


These two things seem to be the biggest remaining blockers for us to be able to switch from openapi-generator to fabrikt.

Would you be interested in supporting these use cases in fabrikt? If desired I could have a stab at implementing this in the form of a pull request sometime. I'm open to discussing alternative ideas as well. :)

cjbooms commented 2 years ago

Open to any PRs you might want to contribute to achieve this. But I won't be in a position to help much with the implementation aside from reviewing any PRs you create.

The type mapping functionality might be an easier first thing to try and replicate.