diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 23da60599c..2a92358fa3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,13 +23,13 @@ micronaut-platform = "4.5.0" micronaut-jaxrs = "4.5.0" micronaut-security = "4.9.0" micronaut-serde = "2.10.2" -micronaut-rxjava2 = "2.3.0" +micronaut-rxjava2 = "2.4.0" micronaut-rxjava3 = "3.4.0" micronaut-reactor = "3.4.0" micronaut-gradle-plugin = "4.4.0" micronaut-groovy = "4.3.0" -micronaut-validation = "4.6.0" -micronaut-data = "4.8.1" +micronaut-validation = "4.6.1" +micronaut-data = "4.8.3" micronaut-test = "4.3.0" micronaut-kotlin = "4.3.0" micronaut-logging = "1.3.0" diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java b/openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java index 604624cb58..e83d718c11 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java @@ -52,6 +52,7 @@ import io.micronaut.inject.ast.GenericPlaceholderElement; import io.micronaut.inject.ast.MemberElement; import io.micronaut.inject.ast.PropertyElement; +import io.micronaut.inject.ast.PropertyElementQuery; import io.micronaut.inject.ast.TypedElement; import io.micronaut.inject.ast.WildcardElement; import io.micronaut.inject.visitor.VisitorContext; @@ -1485,9 +1486,19 @@ private static void populateSchemaProperties(OpenAPI openAPI, VisitorContext con if (classElement != null && !ClassUtils.isJavaLangType(classElement.getName())) { List beanProperties; try { - beanProperties = classElement.getBeanProperties().stream() - .filter(p -> !"groovy.lang.MetaClass".equals(p.getType().getName())) - .toList(); + beanProperties = classElement.getBeanProperties( + PropertyElementQuery.of(classElement) + .excludedAnnotations(Set.of( + Hidden.class.getName(), + JsonIgnore.class.getName() + )) + ).stream() + .filter(p -> + !"groovy.lang.MetaClass".equals(p.getType().getName()) + && !"java.lang.Class".equals(p.getType().getName()) + && !getAnnotationMetadata(p).booleanValue(io.swagger.v3.oas.annotations.media.Schema.class, PROP_HIDDEN).orElse(false) + ) + .toList(); } catch (Exception e) { warn("Error with getting properties for class " + classElement.getName() + ": " + e + "\n" + Utils.printStackTrace(e), context, classElement); // Workaround for https://github.com/micronaut-projects/micronaut-openapi/issues/313 @@ -2298,12 +2309,7 @@ private static void processPropertyElements(OpenAPI openAPI, VisitorContext cont } for (TypedElement publicField : publicFields) { - boolean isHidden = getAnnotationMetadata(publicField).booleanValue(io.swagger.v3.oas.annotations.media.Schema.class, PROP_HIDDEN).orElse(false); - var jsonAnySetterAnn = getAnnotation(publicField, JsonAnySetter.class); - if (isAnnotationPresent(publicField, JsonIgnore.class) - || isAnnotationPresent(publicField, Hidden.class) - || (jsonAnySetterAnn != null && jsonAnySetterAnn.booleanValue("enabled").orElse(true)) - || isHidden) { + if (isHiddenElement(publicField)) { continue; } @@ -2353,6 +2359,17 @@ private static void processPropertyElements(OpenAPI openAPI, VisitorContext cont } } + private static boolean isHiddenElement(TypedElement elementType) { + boolean isHidden = getAnnotationMetadata(elementType) + .booleanValue(io.swagger.v3.oas.annotations.media.Schema.class, PROP_HIDDEN).orElse(false); + var jsonAnySetterAnn = getAnnotation(elementType, JsonAnySetter.class); + return elementType.getType().isAssignable(Class.class) + || isAnnotationPresent(elementType, JsonIgnore.class) + || isAnnotationPresent(elementType, Hidden.class) + || (jsonAnySetterAnn != null && jsonAnySetterAnn.booleanValue("enabled").orElse(true)) + || isHidden; + } + private static boolean allowedByJsonView(TypedElement publicField, String[] classLvlJsonViewClasses, ClassElement jsonViewClassEl, VisitorContext context) { String[] fieldJsonViewClasses = getAnnotationMetadata(publicField).stringValues(JsonView.class); if (ArrayUtils.isEmpty(fieldJsonViewClasses)) { diff --git a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiClassPropertySpec.groovy b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiClassPropertySpec.groovy new file mode 100644 index 0000000000..67fc851e97 --- /dev/null +++ b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiClassPropertySpec.groovy @@ -0,0 +1,96 @@ +package io.micronaut.openapi.visitor + +import io.micronaut.openapi.AbstractOpenApiTypeElementSpec +import io.swagger.v3.oas.models.OpenAPI + +class OpenApiClassPropertySpec extends AbstractOpenApiTypeElementSpec { + + void "test class property is not exposed"() { + + when: + buildBeanDefinition("test.MyBean", ''' +package test; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.micronaut.core.annotation.Introspected; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; + +@Controller +class PersonController { + + @Get("/person/{name}") + HttpResponse get(@NotBlank String name) { + return HttpResponse.ok(); + } +} + +/** + * The person information. + */ +@Introspected +class Person { + + private String name; + private Class personClass; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public void setPersonClass(Class personClass) { + this.personClass = personClass; + } + + public Class getPersonClass() { + return personClass; + } + + public Class getTestProp() { + return personClass; + } + + public String getNotHiddenProp() { + return ""; + } + + @Hidden + public String getHiddenProp2() { + return ""; + } + + @JsonIgnore + public String getHiddenProp3() { + return ""; + } + + @Schema(hidden = true) + public String getHiddenProp4() { + return ""; + } +} + +@jakarta.inject.Singleton +public class MyBean {} + +''') + + then: + OpenAPI openAPI = Utils.testReference + openAPI?.paths?.get("/person/{name}")?.get + openAPI.components.schemas["Person"] + openAPI.components.schemas["Person"].type == "object" + + openAPI.components.schemas["Person"].properties + openAPI.components.schemas["Person"].properties.size() == 2 + + openAPI.components.schemas["Person"].properties["name"] + openAPI.components.schemas["Person"].properties["notHiddenProp"] + } +}