Skip to content

Commit

Permalink
Remove default ApiResponse (with code '0') from generated swagger ann…
Browse files Browse the repository at this point in the history
…otations
  • Loading branch information
altro3 committed Feb 19, 2024
1 parent fdad6a4 commit bfddeaf
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 23 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ micronaut-data = "4.6.2"
micronaut-test = "4.2.1"
micronaut-kotlin = "4.2.0"
micronaut-logging = "1.2.3"
micronaut-session = "4.2.0"
micronaut-docs = "2.0.0"

[libraries]
Expand All @@ -49,6 +50,7 @@ micronaut-reactor = { module = "io.micronaut.reactor:micronaut-reactor-bom", ver
micronaut-gradle-plugin = { module = "io.micronaut.gradle:micronaut-minimal-plugin", version.ref = "micronaut-gradle-plugin"}
micronaut-groovy = { module = "io.micronaut.groovy:micronaut-groovy-bom", version.ref = "micronaut-groovy" }
micronaut-validation = { module = "io.micronaut.validation:micronaut-validation-bom", version.ref = "micronaut-validation" }
micronaut-session = { module = "io.micronaut.session:micronaut-session-bom", version.ref = "micronaut-session" }
micronaut-data = { module = "io.micronaut.data:micronaut-data-bom", version.ref = "micronaut-data" }
micronaut-test = { module = "io.micronaut.test:micronaut-test-bom", version.ref = "micronaut-test" }
micronaut-kotlin = { module = "io.micronaut.kotlin:micronaut-kotlin-bom", version.ref = "micronaut-kotlin" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,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);
Expand Down
1 change: 1 addition & 0 deletions openapi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
// this dependency needs to be updated manually. It's used by html2md
api(libs.managed.jsoup)

testImplementation(mnSession.micronaut.session)
testImplementation(mn.micronaut.management)
testImplementation(mn.micronaut.inject.kotlin.test)
testImplementation(mn.micronaut.inject.groovy.test)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -883,26 +883,18 @@ private Parameter processMethodParameterAnnotation(VisitorContext context, io.sw
newParameter.setExplode(true);
}
} else if (parameter.isAnnotationPresent(Header.class)) {
String headerName = parameter.stringValue(Header.class, "name")
.orElse(parameter.stringValue(Header.class)
.orElseGet(() -> NameUtils.hyphenate(parameterName)));

if (isIgnoredHeader(headerName)) {
var headerName = getHeaderName(parameter, parameterName);
if (headerName == null) {
return null;
}

newParameter = new HeaderParameter();
newParameter.setName(headerName);
} else if (parameter.isAnnotationPresent(Headers.class)) {

List<AnnotationValue<Header>> headerAnnotations = parameter.getAnnotationValuesByType(Header.class);
if (CollectionUtils.isNotEmpty(headerAnnotations)) {
var headerAnn = headerAnnotations.get(0);
var headerName = headerAnn.stringValue("name")
.orElse(headerAnn.stringValue()
.orElseGet(() -> NameUtils.hyphenate(parameterName)));

if (isIgnoredHeader(headerName)) {
var headerName = getHeaderName(parameter, parameterName);
if (headerName == null) {
return null;
}
newParameter = new HeaderParameter();
Expand Down Expand Up @@ -1067,6 +1059,26 @@ private Parameter processMethodParameterAnnotation(VisitorContext context, io.sw
return newParameter;
}

private String getHeaderName(TypedElement parameter, String parameterName) {
// skip params like this: @Header Map<String, String>
if (isIgnoredParameter(parameter)) {
return null;
}
String headerName = parameter.stringValue(Header.class, "name")
.orElse(parameter.stringValue(Header.class)
.orElseGet(() -> NameUtils.hyphenate(parameterName)));

if (isIgnoredHeader(headerName)) {
return null;
}

return headerName;
}

private boolean isIgnoredHeaderParameter(TypedElement parameter) {
return parameter.getType().isAssignable(Map.class);
}

private void processBody(VisitorContext context, OpenAPI openAPI,
io.swagger.v3.oas.models.Operation swaggerOperation, JavadocDescription javadocDescription,
boolean permitsRequestBody, List<MediaType> consumesMediaTypes, TypedElement parameter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,7 @@ protected Schema<?> resolveSchema(OpenAPI openAPI, @Nullable Element definingEle
schema = getPrimitiveType(type, typeName);
} else if (!isArray && primitiveType != null) {
schema = primitiveType.createProperty();
} else if (type.isAssignable(Map.class.getName())) {
} else if (type.isAssignable(Map.class)) {
schema = new MapSchema();
if (CollectionUtils.isEmpty(typeArgs)) {
schema.setAdditionalProperties(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.nio.ByteBuffer;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionStage;
Expand All @@ -30,6 +31,7 @@
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.Internal;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.multipart.FileUpload;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
import io.micronaut.inject.ast.ClassElement;
Expand Down Expand Up @@ -218,17 +220,14 @@ public static boolean isIgnoredParameter(TypedElement parameter) {
return isHidden
|| parameter.isAnnotationPresent(Hidden.class)
|| parameter.isAnnotationPresent(JsonIgnore.class)
|| parameter.isAnnotationPresent(Header.class) && parameter.getType().isAssignable(Map.class)
|| parameter.booleanValue(Parameter.class, "hidden").orElse(false)
|| isParamAnnotationPresent(parameter, "io.micronaut.session.annotation.SessionValue")
|| isParamAnnotationPresent(parameter, "org.springframework.web.bind.annotation.SessionAttribute")
|| isParamAnnotationPresent(parameter, "org.springframework.web.bind.annotation.SessionAttributes")
|| parameter.hasAnnotation("io.micronaut.session.annotation.SessionValue")
|| parameter.hasAnnotation("org.springframework.web.bind.annotation.SessionAttribute")
|| parameter.hasAnnotation("org.springframework.web.bind.annotation.SessionAttributes")
|| isIgnoredParameterType(parameter.getType());
}

private static boolean isParamAnnotationPresent(Element element, String className) {
return element.findAnnotation(className).isPresent();
}

public static boolean isIgnoredParameterType(ClassElement parameterType) {
return parameterType == null
|| parameterType.isAssignable(Principal.class)
Expand Down Expand Up @@ -276,7 +275,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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,52 @@ import spock.lang.Issue

class OpenApiControllerVisitorSpec extends AbstractOpenApiTypeElementSpec {

void "test some ignored parameters"() {

given:
buildBeanDefinition('test.MyBean', '''
package test;
import java.util.Map;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Header;
import io.micronaut.session.annotation.SessionValue;
@Controller
class ControllerThree {
@Get("/myObj")
@SessionValue("myAttr")
MyObj myMethod(
@Header("my-header") String header,
@Header Map<String, String> allHeaders,
@SessionValue @Nullable MyObj myObj) {
return null;
}
}
class MyObj {
public String myProp;
}
@jakarta.inject.Singleton
class MyBean {}
''')
when:
def operation = Utils.testReference?.paths?."/myObj"?.get

then:
operation
operation.parameters
operation.parameters.size() == 1
operation.parameters[0].name == "my-header"
operation.parameters[0].in == "header"
}

void "test hidden endpoint with inheritance"() {

given:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,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,
) {
Expand Down Expand Up @@ -327,7 +327,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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Animal> 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
}

}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ micronautBuild {
importMicronautCatalog("micronaut-validation")
importMicronautCatalog("micronaut-data")
importMicronautCatalog("micronaut-kotlin")
importMicronautCatalog("micronaut-session")
}

0 comments on commit bfddeaf

Please sign in to comment.