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

Field description, example, pattern are lost #93

Closed IvanPizhenko closed 1 year ago

IvanPizhenko commented 2 years ago

Assume I have:

data class Something(
  @Schema(type = "integer", format = "int64", description = "row id")
  val id: Long
)

and then use it like this:

OpenApiResponse(CREATED_201.toString(), [OpenApiContent(Something::class)], "Something is created"),

Javalin 4 has been generating it correctly:

      "Something": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "row id",
            "format": "int64"
          }
        }
      },

In Javalin 5 I get:

      "Something": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "number", 
<<< NOTE HERE: "description" specified in @Schema is lost!
            "format": "int64"
          }
        },
        "required": [
          "id"
        ]
      },

"description" lost.

On other fields I have lost "example" and "pattern". Example:

  @Schema(
    type = "string",
    format = "fingerprint",
    pattern = patternFingerprint,
    description = "... some description ...",
    example = exampleFingerprint
  )
  val fingerprint: String,

Javalin 4:

          "fingerprint": {
            "pattern": "^[0-9A-F]{40}$|^[0-9A-F]{54}$",
            "type": "string",
            "description": "... some description ...",
            "format": "fingerprint",
            "example": "... example value ..."
          },

Javalin 5:

          "fingerprint": {
            "type": "string"
          },
dzikoysk commented 2 years ago

What's the @Schema annotation? This plugin does not define this class.

IvanPizhenko commented 2 years ago

Javalin 4 was aware of it.

package io.swagger.v3.oas.annotations.media;

/**
 * The annotation may be used to define a Schema for a set of elements of the OpenAPI spec, and/or to define additional
 * properties for the schema. It is applicable e.g. to parameters, schema classes (aka "models"), properties of such
 * models, request and response content, header.
 *
 * <p>swagger-core resolver and swagger-jaxrs2 reader engine consider this annotation along with JAX-RS annotations,
 * element type and context as input to resolve the annotated element into an OpenAPI schema definition for such element.</p>
 * <p>The annotation may be used also to override partly (e.g. the name) or fully (e.g providing a completely different
 * representation) the schema of an element; for example if a specific class is provided as value of {@link Schema#implementation()},
 * it will override the element type</p>
 *
 * <p>The annotation {@link ArraySchema} shall be used for array elements; {@link ArraySchema} and {@link Schema} cannot
 * coexist</p>
 *
 * @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#schemaObject">Schema (OpenAPI specification)</a>
 * @see ArraySchema
 **/
@Target({FIELD, METHOD, PARAMETER, TYPE, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Schema {
...
}
IvanPizhenko commented 2 years ago

Currently comes from:

artifactId=swagger-annotations
groupId=io.swagger.core.v3
version=2.2.4
dzikoysk commented 2 years ago

We're not including Swagger library as dependency in specification & openapi plugin and we won't, so it's not an option to use the same type. You can define your own @Schema (or any other annotation with custom properties) and just annotate it with @CustomAnnotation, annotation processor will add those properties to your scheme. Here's an example:

https://github.com/javalin/javalin-openapi/blob/e48c6eda117982a7f6330fcfbf40a2ba93f87eed/examples/javalin-gradle-kotlin/src/main/java/io/javalin/openapi/plugin/test/JavalinTest.java#L426-L441

If you don't want to use annotation, you can also use @Custom:

https://github.com/javalin/javalin-openapi/blob/e48c6eda117982a7f6330fcfbf40a2ba93f87eed/examples/javalin-gradle-kotlin/src/main/java/io/javalin/openapi/plugin/test/JavalinTest.java#L418-L422

IvanPizhenko commented 2 years ago

Hmm, ok. I will try.

IvanPizhenko commented 2 years ago

Doesn't work. Example:

Declared:

@Target(AnnotationTarget.FIELD)
@CustomAnnotation
annotation class Schema(
  val allowableValues: Array<String> = [],
  val description: String = "",
  val example: String = "",
  val format: String = "",
  val pattern: String = ""
)

Data class:

data class KeypairCreateResponse(
  @Schema(
    format = "fingerprint",
    pattern = patternFingerprint,
    description = "keypair fingerprint",
    example = exampleFingerprint
  )
  val fingerprint: String
)

generated:

      "KeypairCreateResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "fingerprint": {
            "type": "string"
          }
        },
        "required": [
          "fingerprint"
        ]
      },
dzikoysk commented 2 years ago

Change target to property getter and use it with prefix @get:Scheme. By default Kotlin doesn't generate those annotations in sources (it stores them in @Metadata annotation), so we need to enforce this behavior:

Annotation processor is Java tool, so it sometimes limits use cases in Kotlin that often do not provide all information on interop level, that's kinda a reason why KSP was created as a replacement to kapt. I'm going to sleep, but I hope you'll figure it out

IvanPizhenko commented 2 years ago

@dzikoysk No effect.

dzikoysk commented 2 years ago

Works for me with this setup:

obraz

Arrays are not supported by custom annotations yet, so I had to comment that property. I'll reference this problem in new issue.

IvanPizhenko commented 2 years ago

Hmm, yes, this way it works, but still I need array fields in the annotation.

IvanPizhenko commented 2 years ago

Btw, what is @JsonSchema and do I need to use it?

dzikoysk commented 2 years ago

I'm just working on support for arrays, it'll be in the next snapshot. @JsonSchema annotation generates json schema file per each annotated class and stores it in /json-schemes/{class-name}. You don't need to use it, I just have logger that prints my schemes, so it was faster for me to copy your code and see the result.

IvanPizhenko commented 2 years ago

ok, thank you

dzikoysk commented 2 years ago

Let me know if it works for your use-case :)

IvanPizhenko commented 2 years ago

I've re-enabled allowableValues, and now it generates all previous JSON correctly, with all non-array fields included, but it doesn't output allowableValues.

dzikoysk commented 2 years ago

Looks fine for me:

obraz

obraz

You can try to make clean build, to make sure it's regenerated.

IvanPizhenko commented 2 years ago

Still doesn't work for me. Can you please add non-array field to your annotation and check that?

dzikoysk commented 2 years ago

Works as well:

obraz

obraz

Could you share your setup: annotation + usage on entity.

IvanPizhenko commented 2 years ago

I have to switch now to some other higher priority tasks, but will continue with this as long as I finish them. Please meanwhile keep this issue open.

dzikoysk commented 1 year ago

Feel free to re-open this issue if you'll be still affected, for now I'll close it :)

IvanPizhenko commented 1 year ago

Still doesn't work for me....

IvanPizhenko commented 1 year ago

It seems like I can't reopen this issue, so will need to create a new one.