javalin / javalin-openapi

Annotation processor for compile-time OpenAPI & JsonSchema, with out-of-the-box support for Javalin 5.x, Swagger & ReDoc
https://github.com/javalin/javalin-openapi/wiki
Apache License 2.0
44 stars 17 forks source link

Support for custom type mappings #80

Closed litdomenj closed 1 year ago

litdomenj commented 2 years ago

We are using Vavr in our projects and would like to map Vavr data structures (Option, List etc.) to the Openapi schema. Such a feature would also be helpful for setting DateTime mappings globally.

This was possible in the original javalin-openapi plugin by providing a custom Jackson object mapper using ModelConverter.

I am wondering if something similar could be supported in this plugin?

dzikoysk commented 2 years ago

This is something I'm trying to explore, make annotation processor configurable (simple parameter flags are not enough). The best solution I've found would be to create standalone module that I could load during compilation-time, but I feel it would be inconvenient for people to create a new module in their projects just to configure compilation phase.

The semi-good solution for e.g. Vavr & DateTime would be to just cover that in our built-in non-ref types, we already do that for some of them:

https://github.com/javalin/javalin-openapi/blob/66129ff4f0b52ad41d2a34e1da9863ce1279467c/openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/processor/shared/JsonTypes.kt#L46-L50

And there's also an annotation that enforces other type:

@OpenApiPropertyType(definedBy = String.class)

If we can't use dedicated module, we could try to create some kind of global annotations that would work like configuration.

litdomenj commented 2 years ago

I think a standalone module would be a good solution. Custom mappers could then be provided by using an application-level annotation - I am thinking of something like this:

  @OpenApiCustomTypeMappers(mappers = {
        @OpenApiCustomTypeMapper(type = "io.vavr.control.Option.class", mapper = "VavrOptionOpenApiMapper.class"),
        @OpenApiCustomTypeMapper(type = "io.vavr.collection.List.class", mapper = "VavrListOpenApiMapper.class"),
        ...
    })
  class MyApplication { ... }

Regarding the second solution which involves adding additional built-in types - I really think we need a more generic solution - next week someone might wish to have support for Guava collections or custom formatting...

Regarding @OpenApiPropertyType annotation - it is a bit cumbersome to be adding annotations to all dto fields. I would prefer if this was globally configurable.

dzikoysk commented 2 years ago

Yup, sth like that. To be honest, such plugin to annotation processor could enforce those mappers by default, so @OpenApiCustomTypeMappers could be unnecessary in general.

I'm also not a fan of built-in types, because just like you said, it's not really extensible, nevertheless that's the easiest solution that covers quite a lot of the most common usages.

We can support @OpenApiPropertyType directly on classes, so all usages of given type will be automatically mapped to specified custom type. Regardless of this issue, I think I'll implement this anyway as I think it's pretty nice enhancement to the existing API.

dzikoysk commented 1 year ago

I've checked various possibilities and it looks like it's impossible to link given class with annotation processor or even provide standalone module with configuration. The funny part is that I'm unable to even access resources directory directly, I'll try to experiment with Groovy as scripting engine.

dzikoysk commented 1 year ago

Groovy script is probably our only option to support that and it works quite good so far, here's an experimental definition of custom type mapping:

https://github.com/javalin/javalin-openapi/blob/6fcee6ae00255f9757fbe532693a7a5978fc1121/openapi-annotation-processor/src/test/compile/openapi.groovy#L20-L23

Anyway it'll work only for simple classes, we'll also need to cover complex cases with some kind of a lambda to e.g. extract generic type from Option<Type> etc. I'll try to take a look on that tomorrow.