diff --git a/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPIManagementAnnotationMapper.java b/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPIManagementAnnotationMapper.java index f8cc2bcb88..32c9b5b4a6 100644 --- a/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPIManagementAnnotationMapper.java +++ b/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPIManagementAnnotationMapper.java @@ -15,15 +15,15 @@ */ package io.micronaut.openapi.annotation.mappers; -import java.util.Collections; -import java.util.List; - import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.inject.annotation.TypedAnnotationMapper; import io.micronaut.inject.visitor.VisitorContext; import io.micronaut.openapi.annotation.OpenAPIInclude; import io.micronaut.openapi.annotation.OpenAPIManagement; +import java.util.Collections; +import java.util.List; + import static io.micronaut.openapi.visitor.ElementUtils.EMPTY_ANNOTATION_VALUES_ARRAY; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_SECURITY; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_TAGS; diff --git a/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPISecurityAnnotationMapper.java b/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPISecurityAnnotationMapper.java index dafa0e3bab..0f7ffc54c2 100644 --- a/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPISecurityAnnotationMapper.java +++ b/openapi/src/main/java/io/micronaut/openapi/annotation/mappers/OpenAPISecurityAnnotationMapper.java @@ -15,15 +15,15 @@ */ package io.micronaut.openapi.annotation.mappers; -import java.util.Collections; -import java.util.List; - import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.inject.annotation.TypedAnnotationMapper; import io.micronaut.inject.visitor.VisitorContext; import io.micronaut.openapi.annotation.OpenAPIInclude; import io.micronaut.openapi.annotation.OpenAPISecurity; +import java.util.Collections; +import java.util.List; + import static io.micronaut.openapi.visitor.ElementUtils.EMPTY_ANNOTATION_VALUES_ARRAY; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_SECURITY; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_TAGS; diff --git a/openapi/src/main/java/io/micronaut/openapi/annotation/transformers/AbstractRetentionPolicyAnnotationTransformer.java b/openapi/src/main/java/io/micronaut/openapi/annotation/transformers/AbstractRetentionPolicyAnnotationTransformer.java index 0fb5ac24b5..4ec792c1aa 100644 --- a/openapi/src/main/java/io/micronaut/openapi/annotation/transformers/AbstractRetentionPolicyAnnotationTransformer.java +++ b/openapi/src/main/java/io/micronaut/openapi/annotation/transformers/AbstractRetentionPolicyAnnotationTransformer.java @@ -15,15 +15,15 @@ */ package io.micronaut.openapi.annotation.transformers; +import io.micronaut.core.annotation.AnnotationValue; +import io.micronaut.inject.annotation.TypedAnnotationTransformer; +import io.micronaut.inject.visitor.VisitorContext; + import java.lang.annotation.Annotation; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; -import io.micronaut.core.annotation.AnnotationValue; -import io.micronaut.inject.annotation.TypedAnnotationTransformer; -import io.micronaut.inject.visitor.VisitorContext; - /** * Changes the Retention Policy of the annotation to SOURCE. * diff --git a/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocDescription.java b/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocDescription.java index 818e366be0..5c5f94f72a 100644 --- a/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocDescription.java +++ b/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocDescription.java @@ -15,11 +15,11 @@ */ package io.micronaut.openapi.javadoc; +import io.micronaut.core.annotation.Nullable; + import java.util.HashMap; import java.util.Map; -import io.micronaut.core.annotation.Nullable; - /** * A parsed javadoc description. * diff --git a/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocParser.java b/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocParser.java index e9c648ff30..d92636c554 100644 --- a/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocParser.java +++ b/openapi/src/main/java/io/micronaut/openapi/javadoc/JavadocParser.java @@ -15,10 +15,6 @@ */ package io.micronaut.openapi.javadoc; -import java.util.Set; - -import io.micronaut.core.util.CollectionUtils; - import com.github.chhorz.javadoc.JavaDoc; import com.github.chhorz.javadoc.JavaDocParserBuilder; import com.github.chhorz.javadoc.OutputType; @@ -26,6 +22,9 @@ import com.github.chhorz.javadoc.tags.PropertyTag; import com.github.chhorz.javadoc.tags.ReturnTag; import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; +import io.micronaut.core.util.CollectionUtils; + +import java.util.Set; /** * Very simple javadoc parser that can used to parse out the first paragraph description and parameter / return descriptions. diff --git a/openapi/src/main/java/io/micronaut/openapi/postprocessors/JacksonDiscriminatorPostProcessor.java b/openapi/src/main/java/io/micronaut/openapi/postprocessors/JacksonDiscriminatorPostProcessor.java index e967b379ac..59d9754b75 100644 --- a/openapi/src/main/java/io/micronaut/openapi/postprocessors/JacksonDiscriminatorPostProcessor.java +++ b/openapi/src/main/java/io/micronaut/openapi/postprocessors/JacksonDiscriminatorPostProcessor.java @@ -15,15 +15,15 @@ */ package io.micronaut.openapi.postprocessors; -import java.util.ArrayList; -import java.util.List; - import io.micronaut.core.annotation.NonNull; import io.micronaut.core.util.StringUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; +import java.util.ArrayList; +import java.util.List; + import static io.micronaut.openapi.visitor.SchemaUtils.setSpecVersion; import static io.swagger.v3.oas.models.Components.COMPONENTS_SCHEMAS_REF; diff --git a/openapi/src/main/java/io/micronaut/openapi/view/AbstractViewConfig.java b/openapi/src/main/java/io/micronaut/openapi/view/AbstractViewConfig.java index 80a22b3bfe..68d4136526 100644 --- a/openapi/src/main/java/io/micronaut/openapi/view/AbstractViewConfig.java +++ b/openapi/src/main/java/io/micronaut/openapi/view/AbstractViewConfig.java @@ -118,8 +118,8 @@ protected void addAttribute(Map.Entry entry) { */ protected String toHtmlAttributes() { return options.entrySet().stream() - .map(e -> e.getKey() + "=\"" + e.getValue() + '"') - .collect(Collectors.joining(" ")); + .map(e -> e.getKey() + "=\"" + e.getValue() + '"') + .collect(Collectors.joining(" ")); } protected String getFinalUrlPrefix(OpenApiViewConfig.RendererType rendererType, VisitorContext context) { @@ -246,8 +246,11 @@ static T fromProperties(T cfg, Map entry.getKey().startsWith(cfg.prefix)) - .forEach(cfg::addAttribute); + for (var entry : properties.entrySet()) { + if (entry.getKey().startsWith(cfg.prefix)) { + cfg.addAttribute(entry); + } + } return cfg; } diff --git a/openapi/src/main/java/io/micronaut/openapi/view/OpenApiExplorerConfig.java b/openapi/src/main/java/io/micronaut/openapi/view/OpenApiExplorerConfig.java index 446dd8d443..7651819687 100644 --- a/openapi/src/main/java/io/micronaut/openapi/view/OpenApiExplorerConfig.java +++ b/openapi/src/main/java/io/micronaut/openapi/view/OpenApiExplorerConfig.java @@ -15,12 +15,6 @@ */ package io.micronaut.openapi.view; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.function.Function; - import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.StringUtils; import io.micronaut.inject.visitor.VisitorContext; @@ -28,6 +22,12 @@ import io.micronaut.openapi.visitor.Pair; import io.micronaut.openapi.visitor.group.OpenApiInfo; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + import static io.micronaut.openapi.visitor.StringUtil.SLASH; /** diff --git a/openapi/src/main/java/io/micronaut/openapi/view/RapidocConfig.java b/openapi/src/main/java/io/micronaut/openapi/view/RapidocConfig.java index 3171df8f5d..874b47ca23 100644 --- a/openapi/src/main/java/io/micronaut/openapi/view/RapidocConfig.java +++ b/openapi/src/main/java/io/micronaut/openapi/view/RapidocConfig.java @@ -15,13 +15,6 @@ */ package io.micronaut.openapi.view; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.function.Function; - import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.StringUtils; import io.micronaut.inject.visitor.VisitorContext; @@ -29,6 +22,13 @@ import io.micronaut.openapi.visitor.Pair; import io.micronaut.openapi.visitor.group.OpenApiInfo; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + import static io.micronaut.openapi.visitor.StringUtil.SLASH; /** diff --git a/openapi/src/main/java/io/micronaut/openapi/view/SwaggerUIConfig.java b/openapi/src/main/java/io/micronaut/openapi/view/SwaggerUIConfig.java index c5363dacc7..06ac62945e 100644 --- a/openapi/src/main/java/io/micronaut/openapi/view/SwaggerUIConfig.java +++ b/openapi/src/main/java/io/micronaut/openapi/view/SwaggerUIConfig.java @@ -61,7 +61,7 @@ final class SwaggerUIConfig extends AbstractViewConfig { private static final String OPTION_OAUTH2 = "oauth2"; private static final String PREFIX_SWAGGER_UI = "swagger-ui"; private static final String KEY_VALUE_SEPARATOR = ": "; - private static final String COMMNA_NEW_LINE = ",\n"; + private static final String COMMA_NEW_LINE = ",\n"; // https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/configuration.md private static final Map> VALID_OPTIONS = new HashMap<>(30); @@ -218,7 +218,7 @@ private String toOptions(@NonNull Map> validOpt .sorted(Map.Entry.comparingByKey()) .map(e -> ((keyPrefix != null && e.getKey().startsWith(keyPrefix)) ? e.getKey().substring(keyPrefix.length()) : e.getKey()) + KEY_VALUE_SEPARATOR + e.getValue()) - .collect(Collectors.joining(COMMNA_NEW_LINE)); + .collect(Collectors.joining(COMMA_NEW_LINE)); } @NonNull @@ -271,7 +271,7 @@ public String render(String template, @Nullable VisitorContext context) { template = replacePlaceHolder(template, PREFIX_SWAGGER_UI + ".attributes", toOptions(), EMPTY_STRING); if (theme != null && Theme.CLASSIC != theme) { - var themeCssLink = isDefaultThemeUrl ? finalUrlPrefix + theme.getCss() + ".css" : themeUrl; + var themeCssLink = isDefaultThemeUrl ? finalUrlPrefix + theme.getCss() + ".css" : themeUrl; template = template.replace("{{" + PREFIX_SWAGGER_UI + ".theme}}", "link(contextPath + \"" + themeCssLink + "\", head, \"text/css\", \"stylesheet\")"); } else { template = template.replace("{{" + PREFIX_SWAGGER_UI + ".theme}}", EMPTY_STRING); diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiEndpointVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiEndpointVisitor.java index bb4f6896e9..97aecf029e 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiEndpointVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiEndpointVisitor.java @@ -276,10 +276,11 @@ private void processTags(ClassElement element, VisitorContext context) { } private void processExternalDocs(ClassElement element, VisitorContext context) { - var externalDocsAnn = element.findAnnotation(io.swagger.v3.oas.annotations.ExternalDocumentation.class); - classExternalDocs = externalDocsAnn - .flatMap(o -> toValue(o.getValues(), context, ExternalDocumentation.class, null)) - .orElse(null); + var externalDocsAnn = element.findAnnotation(io.swagger.v3.oas.annotations.ExternalDocumentation.class).orElse(null); + if (externalDocsAnn == null) { + return; + } + classExternalDocs = toValue(externalDocsAnn.getValues(), context, ExternalDocumentation.class, null); } private boolean containsTag(String name, List tags) { @@ -525,8 +526,8 @@ public void visitMethod(MethodElement element, VisitorContext context) { } private void addParamsByUriTemplate(String path, Map pathVariables, - Map queryParams, - Operation operation) { + Map queryParams, + Operation operation) { // check path variables in URL template which do not map to method parameters for (var entry : pathVariables.entrySet()) { @@ -1371,7 +1372,7 @@ private Pair readWebhook(@Nullable AnnotationValue we Operation operation; HttpMethod method; if (operationAnn != null) { - operation = toValue(operationAnn.getValues(), context, Operation.class, null).orElse(null); + operation = toValue(operationAnn.getValues(), context, Operation.class, null); method = HttpMethod.parse(operationAnn.stringValue(PROP_METHOD).orElse(httpMethod.name())); } else { operation = new Operation(); @@ -1384,20 +1385,21 @@ private Pair readWebhook(@Nullable AnnotationValue we private Map readOperations(String path, HttpMethod httpMethod, List pathItems, MethodElement element, VisitorContext context, @Nullable ClassElement jsonViewClass) { var swaggerOperations = new HashMap(pathItems.size()); - var operationAnn = element.findAnnotation(io.swagger.v3.oas.annotations.Operation.class); + var operationAnn = element.findAnnotation(io.swagger.v3.oas.annotations.Operation.class).orElse(null); for (PathItem pathItem : pathItems) { - var swaggerOperation = operationAnn - .flatMap(o -> toValue(o.getValues(), context, Operation.class, jsonViewClass)) - .orElse(new Operation()); + var swaggerOperation = operationAnn != null ? toValue(operationAnn.getValues(), context, Operation.class, jsonViewClass) : null; + if (swaggerOperation == null) { + swaggerOperation = new Operation(); + } if (CollectionUtils.isNotEmpty(swaggerOperation.getParameters())) { swaggerOperation.getParameters().removeIf(Objects::isNull); } ParameterElement[] methodParams = element.getParameters(); - if (ArrayUtils.isNotEmpty(methodParams) && operationAnn.isPresent()) { - var paramAnns = operationAnn.get().getAnnotations(PROP_PARAMETERS, io.swagger.v3.oas.annotations.Parameter.class); + if (ArrayUtils.isNotEmpty(methodParams) && operationAnn != null) { + var paramAnns = operationAnn.getAnnotations(PROP_PARAMETERS, io.swagger.v3.oas.annotations.Parameter.class); if (CollectionUtils.isNotEmpty(paramAnns)) { for (ParameterElement methodParam : methodParams) { AnnotationValue paramAnn = null; @@ -1550,11 +1552,11 @@ private Parameter.StyleEnum paramStyle(ParameterStyle paramAnnStyle) { } private ExternalDocumentation readExternalDocs(MethodElement element, VisitorContext context) { - var externalDocsAnn = element.findAnnotation(io.swagger.v3.oas.annotations.ExternalDocumentation.class); - - return externalDocsAnn - .flatMap(o -> toValue(o.getValues(), context, ExternalDocumentation.class, null)) - .orElse(null); + var externalDocsAnn = element.findAnnotation(io.swagger.v3.oas.annotations.ExternalDocumentation.class).orElse(null); + if (externalDocsAnn == null) { + return null; + } + return toValue(externalDocsAnn.getValues(), context, ExternalDocumentation.class, null); } private void readSecurityRequirements(MethodElement element, String path, Operation operation, VisitorContext context) { @@ -1727,9 +1729,8 @@ private void processResponses(Operation operation, if (apiResponses.containsKey(responseCode)) { continue; } - Optional newResponse = toValue(responseAnn.getValues(), context, ApiResponse.class, jsonViewClass); - if (newResponse.isPresent()) { - ApiResponse newApiResponse = newResponse.get(); + ApiResponse newApiResponse = toValue(responseAnn.getValues(), context, ApiResponse.class, jsonViewClass); + if (newApiResponse != null) { if (responseAnn.booleanValue("useReturnTypeSchema").orElse(false) && element != null) { addResponseContent(element, context, Utils.resolveOpenApi(context), newApiResponse, jsonViewClass); } else { @@ -1797,7 +1798,7 @@ private Pair readSwaggerRequestBody(Element element, List< var jsonViewClass = element instanceof ParameterElement ? getJsonViewClass(element, context) : null; - RequestBody requestBody = toValue(requestBodyAnn.getValues(), context, RequestBody.class, jsonViewClass).orElse(null); + RequestBody requestBody = toValue(requestBodyAnn.getValues(), context, RequestBody.class, jsonViewClass); // if media type doesn't set in swagger annotation, check micronaut annotation if (contentAnn != null && contentAnn.stringValue(PROP_MEDIA_TYPE).isEmpty() @@ -1868,9 +1869,14 @@ private void processUrlCallbackExpression(VisitorContext context, var pathItem = new PathItem(); if (CollectionUtils.isNotEmpty(operationAnns)) { for (var operationAnn : operationAnns) { - final Optional operationMethod = operationAnn.get(PROP_METHOD, HttpMethod.class); - operationMethod.ifPresent(httpMethod -> toValue(operationAnn.getValues(), context, Operation.class, jsonViewClass) - .ifPresent(op -> setOperationOnPathItem(pathItem, httpMethod, op))); + HttpMethod httpMethod = operationAnn.get(PROP_METHOD, HttpMethod.class).orElse(null); + if (httpMethod == null) { + continue; + } + var op = toValue(operationAnn.getValues(), context, Operation.class, jsonViewClass); + if (op != null) { + setOperationOnPathItem(pathItem, httpMethod, op); + } } } Map callbacks = initCallbacks(operation); @@ -2103,8 +2109,10 @@ private List readTags(ClassElement element, VisitorContext context) { final List readTags(List> tagAnns, VisitorContext context) { var tags = new ArrayList(); for (var tagAnn : tagAnns) { - toValue(tagAnn.getValues(), context, Tag.class, null) - .ifPresent(tags::add); + var tag = toValue(tagAnn.getValues(), context, Tag.class, null); + if (tag != null) { + tags.add(tag); + } } return tags; } diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java index 144eea1567..713aded120 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/AbstractOpenApiVisitor.java @@ -35,7 +35,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -164,40 +163,38 @@ protected void processSecuritySchemes(ClassElement element, VisitorContext conte */ protected List processOpenApiAnnotation(Element element, VisitorContext context, Class annotationType, Class modelType, List tagList) { List> annotations = element.getAnnotationValuesByType(annotationType); - if (CollectionUtils.isNotEmpty(annotations)) { - if (CollectionUtils.isEmpty(tagList)) { - tagList = new ArrayList<>(); + if (CollectionUtils.isEmpty(tagList)) { + tagList = new ArrayList<>(); + } + if (CollectionUtils.isEmpty(annotations)) { + return tagList; + } + for (AnnotationValue tag : annotations) { + Map values; + var tagValues = tag.getValues(); + if (tag.getAnnotationName().equals(io.swagger.v3.oas.annotations.security.SecurityRequirement.class.getName()) + && !tagValues.isEmpty()) { + Object name = tagValues.get(PROP_NAME); + Object scopes = tagValues.computeIfAbsent(PROP_SCOPES, (key) -> new ArrayList()); + values = Collections.singletonMap((CharSequence) name, scopes); + } else { + values = tagValues; } - for (AnnotationValue tag : annotations) { - Map values; - var tagValues = tag.getValues(); - if (tag.getAnnotationName().equals(io.swagger.v3.oas.annotations.security.SecurityRequirement.class.getName()) - && !tagValues.isEmpty()) { - Object name = tagValues.get(PROP_NAME); - Object scopes = tagValues.get(PROP_SCOPES); - if (scopes == null) { - scopes = new ArrayList(); - } - values = Collections.singletonMap((CharSequence) name, scopes); - } else { - values = tagValues; - } - Optional tagOpt = toValue(values, context, modelType, null); - if (tagOpt.isPresent()) { - T tagObj = tagOpt.get(); - // skip all existed tags - boolean alreadyExists = false; - if (CollectionUtils.isNotEmpty(tagList) && tag.getAnnotationName().equals(io.swagger.v3.oas.annotations.tags.Tag.class.getName())) { - for (T existedTag : tagList) { - if (((Tag) existedTag).getName().equals(((Tag) tagObj).getName())) { - alreadyExists = true; - break; - } + T tagObj = toValue(values, context, modelType, null); + if (tagObj != null) { + // skip all existed tags + boolean alreadyExists = false; + if (CollectionUtils.isNotEmpty(tagList) && tag.getAnnotationName().equals(io.swagger.v3.oas.annotations.tags.Tag.class.getName())) { + var newTagName = ((Tag) tagObj).getName(); + for (T existedTag : tagList) { + if (((Tag) existedTag).getName().equals(newTagName)) { + alreadyExists = true; + break; } } - if (!alreadyExists) { - tagList.add(tagObj); - } + } + if (!alreadyExists) { + tagList.add(tagObj); } } } diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/AdocModule.java b/openapi/src/main/java/io/micronaut/openapi/visitor/AdocModule.java index 846be10973..1fe14e755e 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/AdocModule.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/AdocModule.java @@ -15,6 +15,11 @@ */ package io.micronaut.openapi.visitor; +import io.micronaut.core.util.StringUtils; +import io.micronaut.inject.visitor.VisitorContext; +import io.micronaut.openapi.adoc.OpenApiToAdocConverter; +import io.micronaut.openapi.visitor.group.OpenApiInfo; + import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; @@ -22,11 +27,6 @@ import java.nio.file.StandardOpenOption; import java.util.Map; -import io.micronaut.core.util.StringUtils; -import io.micronaut.inject.visitor.VisitorContext; -import io.micronaut.openapi.adoc.OpenApiToAdocConverter; -import io.micronaut.openapi.visitor.group.OpenApiInfo; - import static io.micronaut.openapi.visitor.ContextUtils.addGeneratedResource; import static io.micronaut.openapi.visitor.ContextUtils.info; import static io.micronaut.openapi.visitor.ContextUtils.warn; diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/AnnProcessorEnvironment.java b/openapi/src/main/java/io/micronaut/openapi/visitor/AnnProcessorEnvironment.java index 70b715894b..7a753ebba3 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/AnnProcessorEnvironment.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/AnnProcessorEnvironment.java @@ -15,16 +15,6 @@ */ package io.micronaut.openapi.visitor; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - import io.micronaut.context.ApplicationContextConfiguration; import io.micronaut.context.env.ActiveEnvironment; import io.micronaut.context.env.CachedEnvironment; @@ -42,6 +32,16 @@ import io.micronaut.core.util.StringUtils; import io.micronaut.inject.visitor.VisitorContext; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + import static io.micronaut.openapi.visitor.ConfigUtils.getProjectPath; import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_CONFIG_FILE_LOCATIONS; import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_ENVIRONMENT_ENABLED; diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/ConvertUtils.java b/openapi/src/main/java/io/micronaut/openapi/visitor/ConvertUtils.java index c85900bb22..925f008ff2 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/ConvertUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/ConvertUtils.java @@ -15,31 +15,11 @@ */ package io.micronaut.openapi.visitor; -import java.io.File; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.URI; -import java.net.URL; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import javax.xml.datatype.XMLGregorianCalendar; - +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.micronaut.core.annotation.AnnotationClassValue; import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.core.annotation.Internal; @@ -76,24 +56,56 @@ import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; -import com.fasterxml.jackson.annotation.JsonValue; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; +import javax.xml.datatype.XMLGregorianCalendar; +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; import static io.micronaut.openapi.OpenApiUtils.CONVERT_JSON_MAPPER; import static io.micronaut.openapi.OpenApiUtils.JSON_MAPPER; import static io.micronaut.openapi.visitor.ContextUtils.warn; import static io.micronaut.openapi.visitor.ElementUtils.isEnum; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ALLOWABLE_VALUES; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_BEARER_FORMAT; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_CONTENT; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DEFAULT; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DEFAULT_VALUE; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DESCRIPTION; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ENUM; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXAMPLES; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXTENSIONS; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_FLOWS; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_IN; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_MEDIA_TYPE; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_NAME; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ONE_FORMAT; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_OPEN_ID_CONNECT_URL; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_PARAM_NAME; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_REF; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_REF_DOLLAR; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_SCHEMA; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_SCHEME; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_SCOPES; +import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_TYPE; import static io.micronaut.openapi.visitor.ProtoUtils.isProtobufGenerated; import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_ARRAY; import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_BOOLEAN; @@ -104,20 +116,6 @@ import static io.micronaut.openapi.visitor.SchemaUtils.processExtensions; import static io.micronaut.openapi.visitor.StringUtil.COMMA; import static io.micronaut.openapi.visitor.Utils.resolveComponents; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_REF_DOLLAR; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ALLOWABLE_VALUES; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DEFAULT; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DEFAULT_VALUE; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DESCRIPTION; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ENUM; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXAMPLES; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_EXTENSIONS; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_IN; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_NAME; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_ONE_FORMAT; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_REF; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_SCHEMA; -import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_TYPE; /** * Convert utilities methods. 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 0ef8100482..fe0a6ee47b 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/ElementUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/ElementUtils.java @@ -308,7 +308,7 @@ public static boolean isIgnoredParameterType(ClassElement parameterType) { || parameterType.isAssignable("org.springframework.http.HttpMethod") || parameterType.isAssignable("org.springframework.validation.BindingResult") || parameterType.isAssignable("org.springframework.validation.Errors") - ; + ; } public static AnnotationMetadata getAnnotationMetadata(Element el) { diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/Endpoint.java b/openapi/src/main/java/io/micronaut/openapi/visitor/Endpoint.java index a222433672..a50aa94a47 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/Endpoint.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/Endpoint.java @@ -15,14 +15,14 @@ */ package io.micronaut.openapi.visitor; -import java.util.Collections; -import java.util.List; - import io.micronaut.inject.ast.ClassElement; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; +import java.util.Collections; +import java.util.List; + /** * Endpoint definition. * diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/EndpointsConfiguration.java b/openapi/src/main/java/io/micronaut/openapi/visitor/EndpointsConfiguration.java index a447bca70b..b421f38d7d 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/EndpointsConfiguration.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/EndpointsConfiguration.java @@ -15,14 +15,8 @@ */ package io.micronaut.openapi.visitor; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import io.micronaut.core.util.StringUtils; import io.micronaut.inject.visitor.VisitorContext; import io.micronaut.openapi.OpenApiUtils; @@ -30,8 +24,13 @@ import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; import static io.micronaut.openapi.visitor.ContextUtils.warn; import static io.micronaut.openapi.visitor.StringUtil.COMMA; diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/FileUtils.java b/openapi/src/main/java/io/micronaut/openapi/visitor/FileUtils.java index a53d025eee..3c3b6663a3 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/FileUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/FileUtils.java @@ -80,6 +80,10 @@ public static boolean isYaml(String path) { return path.endsWith(EXT_YML) || path.endsWith(EXT_YAML); } + public static boolean isJson(String path) { + return path.endsWith(EXT_JSON); + } + public static Path getViewsDestDir(Path defaultSwaggerFilePath, VisitorContext context) { String destDir = getConfigProperty(MICRONAUT_OPENAPI_VIEWS_DEST_DIR, context); if (StringUtils.isNotEmpty(destDir)) { diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java index 89d6c28571..36664116fe 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiApplicationVisitor.java @@ -53,7 +53,6 @@ import io.swagger.v3.oas.models.tags.Tag; import java.io.IOException; -import java.io.Serial; import java.io.StringWriter; import java.io.Writer; import java.nio.file.DirectoryStream; @@ -68,7 +67,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -218,9 +216,9 @@ private boolean ignore(ClassElement element, VisitorContext context) { * * @param element The element * @param context The visitor context - * @param openAPI The {@link OpenAPI} object for the application + * @param openApi The {@link OpenAPI} object for the application */ - private void mergeAdditionalSwaggerFiles(ClassElement element, VisitorContext context, OpenAPI openAPI) { + private void mergeAdditionalSwaggerFiles(ClassElement element, VisitorContext context, OpenAPI openApi) { String additionalSwaggerFiles = getConfigProperty(MICRONAUT_OPENAPI_ADDITIONAL_FILES, context); if (StringUtils.isEmpty(additionalSwaggerFiles)) { return; @@ -231,8 +229,11 @@ private void mergeAdditionalSwaggerFiles(ClassElement element, VisitorContext co return; } info("Merging Swagger OpenAPI YAML and JSON files from location: " + directory, context); - try (DirectoryStream stream = Files.newDirectoryStream(directory, path -> FileUtils.isYaml(path.toString().toLowerCase()) || path.toString().toLowerCase().endsWith(EXT_JSON))) { - stream.forEach(path -> { + try (DirectoryStream paths = Files.newDirectoryStream(directory, path -> { + var pathStr = path.toString().toLowerCase(); + return FileUtils.isYaml(pathStr) || FileUtils.isJson(pathStr); + })) { + for (var path : paths) { boolean isYaml = FileUtils.isYaml(path.toString().toLowerCase()); info("Reading Swagger OpenAPI " + (isYaml ? "YAML" : "JSON") + " file " + path.getFileName(), context); OpenAPI parsedOpenApi = null; @@ -241,30 +242,43 @@ private void mergeAdditionalSwaggerFiles(ClassElement element, VisitorContext co } catch (IOException e) { warn("Unable to read file " + path.getFileName() + ": " + e.getMessage(), context, element); } - copyOpenApi(openAPI, parsedOpenApi); - }); + copyOpenApi(openApi, parsedOpenApi); + } } catch (IOException e) { warn("Unable to read file from " + directory + ": " + e.getMessage(), context, element); } } private OpenAPI readOpenApi(ClassElement element, VisitorContext context) { - return element.findAnnotation(OpenAPIDefinition.class).flatMap(o -> { - Optional result = toValue(o.getValues(), context, OpenAPI.class, null); - result.ifPresent(openApi -> { - if (Utils.isOpenapi31()) { - openApi.openapi(OpenApiUtils.OPENAPI_31_VERSION) - .jsonSchemaDialect(ConfigUtils.getJsonSchemaDialect(context)) - .specVersion(SpecVersion.V31); - } - var securityRequirements = new ArrayList(); - for (var secRequirementAnn : o.getAnnotations(PROP_SECURITY, io.swagger.v3.oas.annotations.security.SecurityRequirement.class)) { - securityRequirements.add(ConvertUtils.mapToSecurityRequirement(secRequirementAnn)); - } - openApi.setSecurity(securityRequirements); - }); - return result; - }).orElse(new OpenAPI()); + var openApiDefAnn = element.findAnnotation(OpenAPIDefinition.class).orElse(null); + if (openApiDefAnn == null) { + var openApi = new OpenAPI(); + if (Utils.isOpenapi31()) { + openApi.openapi(OpenApiUtils.OPENAPI_31_VERSION) + .jsonSchemaDialect(ConfigUtils.getJsonSchemaDialect(context)) + .specVersion(SpecVersion.V31); + } + return openApi; + } + + var openApi = toValue(openApiDefAnn.getValues(), context, OpenAPI.class, null); + if (openApi == null) { + openApi = new OpenAPI(); + } + if (Utils.isOpenapi31()) { + openApi.openapi(OpenApiUtils.OPENAPI_31_VERSION) + .jsonSchemaDialect(ConfigUtils.getJsonSchemaDialect(context)) + .specVersion(SpecVersion.V31); + } + var secRequirementAnns = openApiDefAnn.getAnnotations(PROP_SECURITY, io.swagger.v3.oas.annotations.security.SecurityRequirement.class); + if (CollectionUtils.isNotEmpty(secRequirementAnns)) { + var securityRequirements = new ArrayList(); + for (var secRequirementAnn : secRequirementAnns) { + securityRequirements.add(ConvertUtils.mapToSecurityRequirement(secRequirementAnn)); + } + openApi.setSecurity(securityRequirements); + } + return openApi; } private void renderViews(String title, Map, OpenApiInfo> openApiInfos, Path destinationDir, VisitorContext context) throws IOException { @@ -284,52 +298,57 @@ private static PropertyNamingStrategies.NamingBase fromName(String name) { if (name == null) { return null; } - return switch (name.toUpperCase(Locale.US)) { - case "LOWER_CAMEL_CASE" -> new LowerCamelCasePropertyNamingStrategy(); - case "UPPER_CAMEL_CASE" -> (PropertyNamingStrategies.NamingBase) PropertyNamingStrategies.UPPER_CAMEL_CASE; - case "SNAKE_CASE" -> (PropertyNamingStrategies.NamingBase) PropertyNamingStrategies.SNAKE_CASE; - case "UPPER_SNAKE_CASE" -> (PropertyNamingStrategies.NamingBase) PropertyNamingStrategies.UPPER_SNAKE_CASE; - case "LOWER_CASE" -> (PropertyNamingStrategies.NamingBase) PropertyNamingStrategies.LOWER_CASE; - case "KEBAB_CASE" -> (PropertyNamingStrategies.NamingBase) PropertyNamingStrategies.KEBAB_CASE; - case "LOWER_DOT_CASE" -> (PropertyNamingStrategies.NamingBase) PropertyNamingStrategies.LOWER_DOT_CASE; + var strategy = switch (name.toUpperCase(Locale.ENGLISH)) { + case "LOWER_CAMEL_CASE" -> PropertyNamingStrategies.LOWER_CAMEL_CASE; + case "UPPER_CAMEL_CASE" -> PropertyNamingStrategies.UPPER_CAMEL_CASE; + case "SNAKE_CASE" -> PropertyNamingStrategies.SNAKE_CASE; + case "UPPER_SNAKE_CASE" -> PropertyNamingStrategies.UPPER_SNAKE_CASE; + case "LOWER_CASE" -> PropertyNamingStrategies.LOWER_CASE; + case "KEBAB_CASE" -> PropertyNamingStrategies.KEBAB_CASE; + case "LOWER_DOT_CASE" -> PropertyNamingStrategies.LOWER_DOT_CASE; default -> null; }; + return (PropertyNamingStrategies.NamingBase) strategy; } private void applyPropertyNamingStrategy(OpenAPI openAPI, VisitorContext context) { - final String namingStrategyName = getConfigProperty(MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY, context); - final PropertyNamingStrategies.NamingBase propertyNamingStrategy = fromName(namingStrategyName); - if (propertyNamingStrategy != null) { - info("Using " + namingStrategyName + " property naming strategy.", context); - if (openAPI.getComponents() != null && CollectionUtils.isNotEmpty(openAPI.getComponents().getSchemas())) { - openAPI.getComponents().getSchemas().values().forEach(model -> { - Map properties = model.getProperties(); - if (properties != null) { - Map newProperties = properties.entrySet().stream() - .collect(Collectors.toMap(entry -> propertyNamingStrategy.translate(entry.getKey()), - Map.Entry::getValue, (prop1, prop2) -> prop1, LinkedHashMap::new)); - model.getProperties().clear(); - model.setProperties(newProperties); - } - List required = model.getRequired(); - if (required != null) { - List updatedRequired = required.stream().map(propertyNamingStrategy::translate).toList(); - required.clear(); - required.addAll(updatedRequired); - } - }); + var namingStrategyName = getConfigProperty(MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY, context); + var propertyNamingStrategy = fromName(namingStrategyName); + if (propertyNamingStrategy == null) { + return; + } + info("Using " + namingStrategyName + " property naming strategy.", context); + if (openAPI.getComponents() == null || CollectionUtils.isEmpty(openAPI.getComponents().getSchemas())) { + return; + } + for (var schema : openAPI.getComponents().getSchemas().values()) { + Map properties = schema.getProperties(); + if (properties != null) { + var newProps = new LinkedHashMap(properties.size()); + for (var entry : properties.entrySet()) { + newProps.put(propertyNamingStrategy.translate(entry.getKey()), entry.getValue()); + } + schema.setProperties(newProps); + } + List required = schema.getRequired(); + if (CollectionUtils.isNotEmpty(required)) { + var newRequired = new ArrayList(required.size()); + for (var req : required) { + newRequired.add(propertyNamingStrategy.translate(req)); + } + schema.setRequired(newRequired); } } } private void applyPropertyServerContextPath(OpenAPI openAPI, VisitorContext context) { final String serverContextPath = getConfigProperty(MICRONAUT_OPENAPI_CONTEXT_SERVER_PATH, context); - if (serverContextPath == null || serverContextPath.isEmpty()) { + if (StringUtils.isEmpty(serverContextPath)) { return; } info("Applying server context path: " + serverContextPath + " to Paths.", context); Paths paths = openAPI.getPaths(); - if (paths == null || paths.isEmpty()) { + if (CollectionUtils.isEmpty(paths)) { return; } var newPaths = new Paths(); @@ -877,15 +896,4 @@ private void processEndpoints(VisitorContext context) { } } } - - static class LowerCamelCasePropertyNamingStrategy extends PropertyNamingStrategies.NamingBase { - - @Serial - private static final long serialVersionUID = -2750503285679998670L; - - @Override - public String translate(String propertyName) { - return propertyName; - } - } } diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiEndpointVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiEndpointVisitor.java index a8607f1c09..689e11fd3a 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiEndpointVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiEndpointVisitor.java @@ -15,13 +15,7 @@ */ package io.micronaut.openapi.visitor; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import javax.annotation.processing.SupportedOptions; - +import com.fasterxml.jackson.annotation.JsonAnySetter; import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.core.naming.NameUtils; import io.micronaut.core.util.ArrayUtils; @@ -40,7 +34,11 @@ import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; -import com.fasterxml.jackson.annotation.JsonAnySetter; +import javax.annotation.processing.SupportedOptions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import static io.micronaut.openapi.visitor.ConfigUtils.endpointsConfiguration; import static io.micronaut.openapi.visitor.ConfigUtils.isOpenApiEnabled; @@ -51,10 +49,10 @@ import static io.micronaut.openapi.visitor.ContextUtils.SERVERS_LIST_ARGUMENT; import static io.micronaut.openapi.visitor.ContextUtils.TAGS_LIST_ARGUMENT; import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ENABLED; -import static io.micronaut.openapi.visitor.Utils.DEFAULT_MEDIA_TYPES; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_DESCRIPTION; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_HIDDEN; import static io.micronaut.openapi.visitor.OpenApiModelProp.PROP_VALUE; +import static io.micronaut.openapi.visitor.Utils.DEFAULT_MEDIA_TYPES; /** * A {@link TypeElementVisitor} the builds the Swagger model from Micronaut diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiExtraSchemaVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiExtraSchemaVisitor.java index a747a5a876..0d954d8dd9 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiExtraSchemaVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiExtraSchemaVisitor.java @@ -68,8 +68,8 @@ public class OpenApiExtraSchemaVisitor implements TypeElementVisitor getSupportedAnnotationNames() { return CollectionUtils.setOf( - OpenAPIExtraSchema.class.getName(), - OpenAPIExtraSchemas.class.getName() + OpenAPIExtraSchema.class.getName(), + OpenAPIExtraSchemas.class.getName() ); } @@ -139,10 +139,10 @@ public void visitClass(ClassElement element, VisitorContext context) { // Classes with annotation without these members we process as extra schema if (isEmpty(classes) - && isEmpty(excludeClasses) - && isEmpty(excludeClassNames) - && isEmpty(packages) - && isEmpty(excludePackages)) { + && isEmpty(excludeClasses) + && isEmpty(excludeClassNames) + && isEmpty(packages) + && isEmpty(excludePackages)) { processExtraSchemaClass(element, context); continue; } diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiGroupInfoVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiGroupInfoVisitor.java index 5bb28303fe..416c45a962 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiGroupInfoVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiGroupInfoVisitor.java @@ -15,13 +15,6 @@ */ package io.micronaut.openapi.visitor; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.processing.SupportedOptions; - import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.core.annotation.Internal; import io.micronaut.core.order.Ordered; @@ -36,6 +29,12 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.security.SecurityRequirement; +import javax.annotation.processing.SupportedOptions; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static io.micronaut.openapi.visitor.ConfigUtils.isOpenApiEnabled; import static io.micronaut.openapi.visitor.ConfigUtils.isSpecGenerationEnabled; import static io.micronaut.openapi.visitor.ConvertUtils.toValue; diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiJacksonVisitor.java b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiJacksonVisitor.java index 4c38705be6..ef8f4b1bcd 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiJacksonVisitor.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/OpenApiJacksonVisitor.java @@ -15,12 +15,8 @@ */ package io.micronaut.openapi.visitor; -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Set; - -import javax.annotation.processing.SupportedOptions; - +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.micronaut.core.annotation.AnnotationClassValue; import io.micronaut.core.annotation.AnnotationValue; import io.micronaut.core.order.Ordered; @@ -31,8 +27,10 @@ import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; import io.swagger.v3.oas.annotations.media.Schema; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; +import javax.annotation.processing.SupportedOptions; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Set; import static io.micronaut.openapi.visitor.ConfigUtils.isOpenApiEnabled; import static io.micronaut.openapi.visitor.ConfigUtils.isSpecGenerationEnabled; diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/Pair.java b/openapi/src/main/java/io/micronaut/openapi/visitor/Pair.java index ce7f9e51ab..ecfe56cf59 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/Pair.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/Pair.java @@ -15,10 +15,7 @@ */ package io.micronaut.openapi.visitor; -import java.util.Map; import java.util.Objects; -import java.util.stream.Collector; -import java.util.stream.Collectors; /** * Helper class for handling two values. @@ -50,10 +47,6 @@ public T getSecond() { return second; } - public static Collector, ?, Map> toMap() { - return Collectors.toMap(Pair::getFirst, Pair::getSecond); - } - @Override public boolean equals(Object o) { diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/ProtoUtils.java b/openapi/src/main/java/io/micronaut/openapi/visitor/ProtoUtils.java index 9961c09f24..5872025acc 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/ProtoUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/ProtoUtils.java @@ -42,11 +42,11 @@ public static List filterProtobufProperties(ClassElement classE var propertiesWithoutProto = new ArrayList(); for (var prop : beanProperties) { if (prop.getName().equals("initialized") - || prop.getName().equals("defaultInstanceForType") - || prop.getName().equals("initializationErrorString") - || prop.getName().equals("descriptorForType") - || prop.getName().equals("allFields") - || prop.getName().equals("unknownFields") + || prop.getName().equals("defaultInstanceForType") + || prop.getName().equals("initializationErrorString") + || prop.getName().equals("descriptorForType") + || prop.getName().equals("allFields") + || prop.getName().equals("unknownFields") ) { continue; } @@ -56,15 +56,15 @@ public static List filterProtobufProperties(ClassElement classE var returnType = readMethod.getReturnType(); if (readMethod.getName().endsWith("OrBuilderList") - || (readMethod.getName().endsWith("Bytes") - && returnType.getName().equals("com.google.protobuf.ByteString")) + || (readMethod.getName().endsWith("Bytes") + && returnType.getName().equals("com.google.protobuf.ByteString")) ) { continue; } // process map properties if (returnType.getName().equals("java.util.Map") - && readMethod.getName().endsWith("Map")) { + && readMethod.getName().endsWith("Map")) { continue; } @@ -78,13 +78,13 @@ public static List filterProtobufProperties(ClassElement classE // for iterable fields need to skip getCount methods if (readMethod.getName().endsWith("Count") - && classElement.findMethod(readMethod.getName().substring(0, readMethod.getName().lastIndexOf("Count"))).isPresent()) { + && classElement.findMethod(readMethod.getName().substring(0, readMethod.getName().lastIndexOf("Count"))).isPresent()) { continue; } // skip protobuf internal properties for objects if (isProtobufGenerated(returnType) - && prop.getName().endsWith("OrBuilder")) { + && prop.getName().endsWith("OrBuilder")) { var propName = prop.getName().substring(0, prop.getName().lastIndexOf("OrBuilder")); var found = false; for (var propWithoutSuffix : beanProperties) { @@ -174,14 +174,14 @@ public static boolean isProtobufType(ClassElement type) { public static boolean isProtobufGenerated(ClassElement type) { return type != null && ( - type.isAssignable("com.google.protobuf.MessageOrBuilder") + type.isAssignable("com.google.protobuf.MessageOrBuilder") || type.isAssignable("com.google.protobuf.ProtocolMessageEnum") ); } public static boolean isProtobufMessageClass(ClassElement type) { return type != null && ( - type.isAssignable("com.google.protobuf.GeneratedMessageV3") + type.isAssignable("com.google.protobuf.GeneratedMessageV3") || type.isAssignable("com.google.protobuf.GeneratedMessage") || type.isAssignable("com.google.protobuf.GeneratedMessageLite") ); 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 e3b548d820..7cab7e3f01 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/SchemaDefinitionUtils.java @@ -211,7 +211,6 @@ import static io.micronaut.openapi.visitor.StringUtil.DOT; import static io.micronaut.openapi.visitor.Utils.isOpenapi31; import static io.micronaut.openapi.visitor.Utils.resolveOpenApi; -import static java.util.stream.Collectors.toMap; /** * Methods to construct OpenPI schema definition. @@ -432,7 +431,7 @@ public static Schema getSchemaDefinition(OpenAPI openAPI, var externalDocsValue = type.getDeclaredAnnotation(io.swagger.v3.oas.annotations.ExternalDocumentation.class); ExternalDocumentation externalDocs = null; if (externalDocsValue != null) { - externalDocs = toValue(externalDocsValue.getValues(), context, ExternalDocumentation.class, null).orElse(null); + externalDocs = toValue(externalDocsValue.getValues(), context, ExternalDocumentation.class, null); } if (externalDocs != null) { schema.setExternalDocs(externalDocs); @@ -1054,6 +1053,9 @@ public static Schema bindArraySchemaAnnotationValue(VisitorContext context, T * @return The map */ public static Map toValueMap(Map values, VisitorContext context, @Nullable ClassElement jsonViewClass) { + if (CollectionUtils.isEmpty(values)) { + return Collections.emptyMap(); + } var newValues = new HashMap(values.size()); for (Map.Entry entry : values.entrySet()) { CharSequence key = entry.getKey(); @@ -1276,7 +1278,7 @@ public static Map toValueMap(Map val } else if (io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.FALSE.toString().equals(value.toString())) { newValues.put(PROP_ADDITIONAL_PROPERTIES, false); } - // TODO + // TODO // } else if (AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION.toString().equals(value.toString())) { } else if (key.equals(PROP_ONE_TYPES) && isOpenapi31()) { newValues.put(PROP_TYPE, value); @@ -1436,14 +1438,14 @@ public static Schema bindSchemaAnnotationValue(VisitorContext context, TypedE * @param jsonViewClass Class from JsonView annotation * @return The converted instance */ - public static Optional toValue(Map values, VisitorContext context, Class type, @Nullable ClassElement jsonViewClass) { + public static T toValue(Map values, VisitorContext context, Class type, @Nullable ClassElement jsonViewClass) { JsonNode node = toJson(values, context, jsonViewClass); try { - return Optional.ofNullable(ConvertUtils.treeToValue(node, type, context)); + return ConvertUtils.treeToValue(node, type, context); } catch (JsonProcessingException e) { warn("Error converting [" + node + "]: to " + type + ":\n" + Utils.printStackTrace(e), context); } - return Optional.empty(); + return null; } /** @@ -1883,7 +1885,7 @@ private static void processSchemaAnn(Schema schemaToBind, VisitorContext context var schemaExtDocs = (AnnotationValue) annValues.get(PROP_EXTERNAL_DOCS); ExternalDocumentation externalDocs = null; if (schemaExtDocs != null) { - externalDocs = toValue(schemaExtDocs.getValues(), context, ExternalDocumentation.class, null).orElse(null); + externalDocs = toValue(schemaExtDocs.getValues(), context, ExternalDocumentation.class, null); } if (externalDocs != null) { schemaToBind.setExternalDocs(externalDocs); @@ -2857,7 +2859,8 @@ private static boolean doesParamExistsMandatoryInConstructor(Element element, @N if (classEl.isEnum()) { return true; } - return classEl.getPrimaryConstructor().flatMap(methodElement -> Arrays.stream(methodElement.getParameters()) + return classEl.getPrimaryConstructor() + .flatMap(methodElement -> Arrays.stream(methodElement.getParameters()) .filter(parameterElement -> parameterElement.getName().equals(element.getName())) .map(parameterElement -> !parameterElement.isNullable()) .findFirst()) @@ -2885,11 +2888,19 @@ private static Map getDiscriminatorMap(Map private static > void processAnnotationValue(VisitorContext context, AnnotationValue annotationValue, Map arraySchemaMap, List filters, Class type, @Nullable ClassElement jsonViewClass) { - Map values = annotationValue.getValues().entrySet().stream() - .filter(entry -> filters == null || !filters.contains((String) entry.getKey())) - .collect(toMap(e -> e.getKey().equals(PROP_REQUIRED_PROPERTIES) ? PROP_REQUIRED : e.getKey(), Map.Entry::getValue)); - toValue(values, context, type, jsonViewClass) - .ifPresent(s -> schemaToValueMap(arraySchemaMap, s)); + + var values = new LinkedHashMap(); + for (var entry : annotationValue.getValues().entrySet()) { + var key = entry.getKey(); + if (filters == null || !filters.contains((String) key)) { + values.put(key.equals(PROP_REQUIRED_PROPERTIES) ? PROP_REQUIRED : key, entry.getValue()); + } + } + + var s = toValue(values, context, type, jsonViewClass); + if (s != null) { + schemaToValueMap(arraySchemaMap, s); + } } private static Map resolveAnnotationValues(VisitorContext context, AnnotationValue av, @Nullable ClassElement jsonViewClass) { diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointGroupInfo.java b/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointGroupInfo.java index 871d71002e..53808b51ab 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointGroupInfo.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointGroupInfo.java @@ -15,11 +15,11 @@ */ package io.micronaut.openapi.visitor.group; +import io.micronaut.core.annotation.Internal; + import java.util.HashMap; import java.util.Map; -import io.micronaut.core.annotation.Internal; - /** * Entity to storage information about group with specific properties for this operation-group. * diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointInfo.java b/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointInfo.java index fde813b30c..ca8d78eab0 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointInfo.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/group/EndpointInfo.java @@ -15,14 +15,14 @@ */ package io.micronaut.openapi.visitor.group; -import java.util.List; -import java.util.Map; - import io.micronaut.core.annotation.Internal; import io.micronaut.http.HttpMethod; import io.micronaut.inject.ast.MethodElement; import io.swagger.v3.oas.models.Operation; +import java.util.List; +import java.util.Map; + /** * Entity to storage information about same swagger operations, but with different version / group. * Need it to merge them in post-processing. diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/group/GroupProperties.java b/openapi/src/main/java/io/micronaut/openapi/visitor/group/GroupProperties.java index 586065945d..ba26e15516 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/group/GroupProperties.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/group/GroupProperties.java @@ -15,10 +15,10 @@ */ package io.micronaut.openapi.visitor.group; -import java.util.List; - import io.micronaut.core.annotation.Internal; +import java.util.List; + /** * OpenAPI group properties. * diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/group/RouterVersioningProperties.java b/openapi/src/main/java/io/micronaut/openapi/visitor/group/RouterVersioningProperties.java index 8abb110e0c..385473ca0c 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/group/RouterVersioningProperties.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/group/RouterVersioningProperties.java @@ -15,10 +15,10 @@ */ package io.micronaut.openapi.visitor.group; -import java.util.List; - import io.micronaut.core.annotation.Internal; +import java.util.List; + /** * Micronaut router versioning properties. * diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapConverter.java b/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapConverter.java index 1a9f95ec3d..f7f2f70f48 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapConverter.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapConverter.java @@ -15,12 +15,6 @@ */ package io.micronaut.openapi.visitor.security; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - import io.micronaut.context.exceptions.ConfigurationException; import io.micronaut.core.annotation.Internal; import io.micronaut.core.convert.ConversionContext; @@ -30,6 +24,12 @@ import io.micronaut.core.type.Argument; import io.micronaut.http.HttpMethod; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + /** * Copy of class io.micronaut.security.config.InterceptUrlMapConverter from micronaut-security. * diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapPattern.java b/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapPattern.java index ec1e454527..52665ff8bf 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapPattern.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/security/InterceptUrlMapPattern.java @@ -15,12 +15,12 @@ */ package io.micronaut.openapi.visitor.security; -import java.util.List; - import io.micronaut.core.annotation.Internal; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpMethod; +import java.util.List; + /** * Copy of class io.micronaut.security.config.InterceptUrlMapPattern from micronaut-security. * diff --git a/openapi/src/main/java/io/micronaut/openapi/visitor/security/SecurityProperties.java b/openapi/src/main/java/io/micronaut/openapi/visitor/security/SecurityProperties.java index 42db4dcb74..6ad8691f0a 100644 --- a/openapi/src/main/java/io/micronaut/openapi/visitor/security/SecurityProperties.java +++ b/openapi/src/main/java/io/micronaut/openapi/visitor/security/SecurityProperties.java @@ -15,10 +15,10 @@ */ package io.micronaut.openapi.visitor.security; -import java.util.List; - import io.micronaut.core.annotation.Internal; +import java.util.List; + /** * Micronaut security properties. * diff --git a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiBasicSchemaSpec.groovy b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiBasicSchemaSpec.groovy index 8baae9c20b..ebad8f6cfe 100644 --- a/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiBasicSchemaSpec.groovy +++ b/openapi/src/test/groovy/io/micronaut/openapi/visitor/OpenApiBasicSchemaSpec.groovy @@ -404,6 +404,7 @@ public class MyBean {} openAPI.components.schemas["Person"].properties["totalGoals"].description == "The total number of person's goals." } + @RestoreSystemProperties void "test render OpenApiView specification with custom property naming strategy"() { given: System.setProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY, "SNAKE_CASE") @@ -415,16 +416,17 @@ package test; import io.micronaut.core.annotation.Introspected; import io.micronaut.http.HttpResponse; import io.micronaut.http.MediaType; -import io.micronaut.http.annotation.*; - +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; - -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.NegativeOrZero; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PositiveOrZero; @Controller class PersonController { @@ -539,11 +541,9 @@ public class MyBean {} openAPI.components.schemas["Person"].properties["total_goals"].type == "integer" !openAPI.components.schemas["Person"].properties["total_goals"].exclusiveMinimum openAPI.components.schemas["Person"].properties["total_goals"].description == "The total number of person's goals." - - cleanup: - System.clearProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY) } + @RestoreSystemProperties void "test render OpenApiView specification with LOWER_CAMEL_CASE property naming strategy - Issue #241"() { given: System.setProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY, "LOWER_CAMEL_CASE") @@ -555,16 +555,17 @@ package test; import io.micronaut.core.annotation.Introspected; import io.micronaut.http.HttpResponse; import io.micronaut.http.MediaType; -import io.micronaut.http.annotation.*; - +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; - -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.NegativeOrZero; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PositiveOrZero; @Controller class PersonController { @@ -679,11 +680,9 @@ public class MyBean {} openAPI.components.schemas["Person"].properties["totalGoals"].type == "integer" !openAPI.components.schemas["Person"].properties["totalGoals"].exclusiveMinimum openAPI.components.schemas["Person"].properties["totalGoals"].description == "The total number of person's goals." - - cleanup: - System.clearProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY) } + @RestoreSystemProperties void "test render OpenApiView specification with custom property naming strategy and required properties - Issue #240"() { given: System.setProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY, "SNAKE_CASE") @@ -695,16 +694,18 @@ package test; import io.micronaut.core.annotation.Introspected; import io.micronaut.http.HttpResponse; import io.micronaut.http.MediaType; -import io.micronaut.http.annotation.*; - +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; - -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NegativeOrZero; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PositiveOrZero; @Controller class PersonController { @@ -836,9 +837,6 @@ public class MyBean {} personSchema.required.contains("name") personSchema.required.contains("debt_value") personSchema.required.contains("total_goals") - - cleanup: - System.clearProperty(OpenApiConfigProperty.MICRONAUT_OPENAPI_PROPERTY_NAMING_STRATEGY) } void "test READ_ONLY accessMode correctly results in setting readOnly to true"() {