From f4c8af0288d6aa3235279e905e8a6cd899ea7652 Mon Sep 17 00:00:00 2001 From: altro3 Date: Mon, 5 Feb 2024 13:53:39 +0700 Subject: [PATCH] Remove default ApiResponse (with code '0') from generated swagger annotations --- .../AbstractMicronautJavaCodegen.java | 8 + .../AbstractMicronautKotlinCodegen.java | 21 +++ .../openapi/visitor/ElementUtils.java | 2 +- .../OpenApiPojoControllerKotlinSpec.groovy | 4 +- .../visitor/OpenApiSchemaFieldSpec.groovy | 139 ++++++++++++++++++ 5 files changed, 171 insertions(+), 3 deletions(-) diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java index aa6b7487c4..72382b81ff 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java @@ -780,6 +780,14 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation param.vendorExtensions.put("hasMultipleParams", hasMultipleParams); } op.vendorExtensions.put("originReturnProperty", op.returnProperty); + if (op.responses != null && !op.responses.isEmpty()) { + for (var resp : op.responses) { + if (resp.isDefault) { + resp.code = "default"; + } + } + } + processParametersWithAdditionalMappings(op.allParams, op.imports); processWithResponseBodyMapping(op); processOperationWithResponseWrappers(op); diff --git a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java index 8b0e8ffd43..d230efb41c 100644 --- a/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java +++ b/openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautKotlinCodegen.java @@ -902,6 +902,27 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation param.vendorExtensions.put("hasMultipleParams", hasMultipleParams); } op.vendorExtensions.put("originReturnProperty", op.returnProperty); + if (op.responses != null && !op.responses.isEmpty()) { + for (var resp : op.responses) { + if (resp.isDefault) { + resp.code = "default"; + } + } + } + /* + 0 = {LoadedVisitor@8165} "io.micronaut.openapi.visitor.OpenApiGroupInfoVisitor@3d615b2e" +1 = {LoadedVisitor@8166} "io.micronaut.context.visitor.InternalApiTypeElementVisitor@3a1f2a1" +2 = {LoadedVisitor@8167} "io.micronaut.inject.beans.visitor.MapperVisitor@40ce9049" +3 = {LoadedVisitor@8168} "io.micronaut.context.visitor.ExecutableVisitor@52ca2652" +4 = {LoadedVisitor@8169} "io.micronaut.context.visitor.ContextConfigurerVisitor@65c5bf0e" +5 = {LoadedVisitor@8170} "io.micronaut.context.visitor.ConfigurationReaderVisitor@697d6ded" +6 = {LoadedVisitor@8171} "io.micronaut.security.annotation.expressions.SecuredEvaluationContextRegistrar@5d05f453" +7 = {LoadedVisitor@8172} "io.micronaut.validation.visitor.async.AsyncTypeElementVisitor@5efeb117" +8 = {LoadedVisitor@5507} "io.micronaut.openapi.visitor.OpenApiJacksonVisitor@543d5863" +9 = {LoadedVisitor@8173} "io.micronaut.inject.beans.visitor.IntrospectedTypeElementVisitor@160b4809" +10 = {LoadedVisitor@8174} "io.micronaut.context.visitor.BeanImportVisitor@149b4d20" + */ + processParametersWithAdditionalMappings(op.allParams, op.imports); processWithResponseBodyMapping(op); processOperationWithResponseWrappers(op); diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/ElementUtils.java b/openapi/src/main/java/io/micronaut/openapi/visitor/ElementUtils.java index a0fbf6a577..c0c89f11db 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/ElementUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/ElementUtils.java @@ -278,7 +278,7 @@ public static AnnotationMetadata getAnnotationMetadata(Element el) { if (constructorMetadata == null || constructorMetadata.isEmpty()) { return propMetadata; } - return new AnnotationMetadataHierarchy(true, new AnnotationMetadata[] {propMetadata, constructorMetadata}); + return new AnnotationMetadataHierarchy(true, propMetadata, constructorMetadata); } return el.getAnnotationMetadata(); } diff --git a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiPojoControllerKotlinSpec.groovy b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiPojoControllerKotlinSpec.groovy index c7a4386177..eac1fc545c 100644 --- a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiPojoControllerKotlinSpec.groovy +++ b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiPojoControllerKotlinSpec.groovy @@ -211,7 +211,7 @@ open class Animal ( @Size(max = 50) @Nullable @Schema(name = "class", requiredMode = Schema.RequiredMode.NOT_REQUIRED) -// @JsonProperty(JSON_PROPERTY_PROPERTY_CLASS) + @JsonProperty(JSON_PROPERTY_PROPERTY_CLASS) @JsonInclude(JsonInclude.Include.USE_DEFAULTS) open var propertyClass: String? = null, ) { @@ -264,7 +264,7 @@ class MyBean {} Utils.testReference != null when: "The OpenAPI is retrieved" - OpenAPI openAPI = Utils.testReference + def openAPI = Utils.testReference Schema schema = openAPI.components.schemas.Animal then: "the components are valid" diff --git a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiSchemaFieldSpec.groovy b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiSchemaFieldSpec.groovy index 6016cb0d88..edc313d97a 100644 --- a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiSchemaFieldSpec.groovy +++ b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiSchemaFieldSpec.groovy @@ -929,4 +929,143 @@ class MyBean {} dtoSchema.properties.zone_id.type == 'string' } + void "test annotations on constructor parameters level"() { + + when: + buildBeanDefinition('test.MyBean', ''' +package test; + +import java.math.BigDecimal; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.annotation.Body; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Put; +import io.micronaut.serde.annotation.Serdeable; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import reactor.core.publisher.Mono; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@Controller +class HelloController { + + @Put("/sendModelWithDiscriminator") + Mono sendModelWithDiscriminator( + @Body @NotNull @Valid Animal animal + ) { + return Mono.empty(); + } +} + +@Serdeable +@JsonIgnoreProperties( + value = "class", // ignore manually set class, it will be automatically generated by Jackson during serialization + allowSetters = true // allows the class to be set during deserialization +) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "class", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Bird.class, name = "ave"), +}) +class Animal { + + @JsonProperty("class") + protected String propertyClass; + @Schema(name = "color", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @Nullable + private ColorEnum color; + + public String getPropertyClass() { + return propertyClass; + } + + public void setPropertyClass(String propertyClass) { + this.propertyClass = propertyClass; + } + + public ColorEnum getColor() { + return color; + } + + public void setColor(ColorEnum color) { + this.color = color; + } +} + +@Serdeable +class Bird extends Animal { + + private Integer numWings; + @DecimalMax("123.78") + private BigDecimal beakLength; + private String featherDescription; + + Bird( + @Min(10) Integer numWings, + @JsonProperty("myLength") BigDecimal beakLength, + String featherDescription) { + + } + + public Integer getNumWings() { + return numWings; + } + + public void setNumWings(Integer numWings) { + this.numWings = numWings; + } + + public BigDecimal getBeakLength() { + return beakLength; + } + + public void setBeakLength(BigDecimal beakLength) { + this.beakLength = beakLength; + } + + public String getFeatherDescription() { + return featherDescription; + } + + public void setFeatherDescription(String featherDescription) { + this.featherDescription = featherDescription; + } +} + +@Serdeable +enum ColorEnum { + + @JsonProperty("red") + RED +} + +@jakarta.inject.Singleton +class MyBean {} +''') + then: "the state is correct" + Utils.testReference != null + + when: "The OpenAPI is retrieved" + def openApi = Utils.testReference + def schemas = openApi.components.schemas + + then: "the components are valid" + schemas.Animal + schemas.Bird + schemas.ColorEnum + + !schemas.Bird.allOf[1].properties.beakLength + schemas.Bird.allOf[1].properties.myLength + schemas.Bird.allOf[1].properties.myLength.maximum == 123.78 + schemas.Bird.allOf[1].properties.numWings.minimum == 10 + } + }