Skip to content

Commit

Permalink
Fix empty oneOf, anyOf lists
Browse files Browse the repository at this point in the history
  • Loading branch information
altro3 committed Dec 12, 2023
1 parent adc7d7c commit 447d224
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -627,13 +627,13 @@ private void processParameterAnnotationInMethod(MethodElement element,
continue;
}
if (paramEl.isAnnotationPresent(PathVariable.class)) {
parameter.setIn("path");
parameter.setIn(ParameterIn.PATH.toString());
} else if (paramEl.isAnnotationPresent(QueryValue.class)) {
parameter.setIn("query");
parameter.setIn(ParameterIn.QUERY.toString());
} else if (paramEl.isAnnotationPresent(CookieValue.class)) {
parameter.setIn("cookie");
parameter.setIn(ParameterIn.COOKIE.toString());
} else if (paramEl.isAnnotationPresent(Header.class)) {
parameter.setIn("header");
parameter.setIn(ParameterIn.HEADER.toString());
} else {
UriMatchVariable pathVariable = pathVariables.get(parameter.getName());
// check if this parameter is optional path variable
Expand All @@ -645,14 +645,14 @@ private void processParameterAnnotationInMethod(MethodElement element,
}
}
if (pathVariable != null && !pathVariable.isOptional() && !pathVariable.isQuery() && !pathVariable.isExploded()) {
parameter.setIn("path");
parameter.setIn(ParameterIn.PATH.toString());
}

if (parameter.getIn() == null) {
if (httpMethod == HttpMethod.GET) {
// default to QueryValue -
// https://github.com/micronaut-projects/micronaut-openapi/issues/130
parameter.setIn("query");
parameter.setIn(ParameterIn.QUERY.toString());
}
}

Expand Down Expand Up @@ -899,11 +899,11 @@ private Parameter processMethodParameterAnnotation(VisitorContext context, io.sw
newParameter = new HeaderParameter();
newParameter.setName(headerName);
} else if (parameter.isAnnotationPresent(CookieValue.class)) {
String cookieName = parameter.getValue(CookieValue.class, String.class).orElse(parameterName);
String cookieName = parameter.stringValue(CookieValue.class).orElse(parameterName);
newParameter = new CookieParameter();
newParameter.setName(cookieName);
} else if (parameter.isAnnotationPresent(QueryValue.class)) {
String queryVar = parameter.getValue(QueryValue.class, String.class).orElse(parameterName);
String queryVar = parameter.stringValue(QueryValue.class).orElse(parameterName);
newParameter = new QueryParameter();
newParameter.setName(queryVar);
} else if (parameter.isAnnotationPresent(Part.class) && permitsRequestBody) {
Expand All @@ -915,8 +915,9 @@ private Parameter processMethodParameterAnnotation(VisitorContext context, io.sw
} else if (hasNoBindingAnnotationOrType(parameter)) {
AnnotationValue<io.swagger.v3.oas.annotations.Parameter> parameterAnnotation = parameter.getAnnotation(io.swagger.v3.oas.annotations.Parameter.class);
// Skip recognizing parameter if it's manually defined by "in"
var paramIn = parameterAnnotation != null ? parameterAnnotation.stringValue("in").orElse(null) : null;
if (parameterAnnotation == null || !parameterAnnotation.booleanValue("hidden").orElse(false)
&& parameterAnnotation.stringValue("in").isEmpty()) {
&& (paramIn == null || paramIn.equals(ParameterIn.DEFAULT.toString()))) {
if (permitsRequestBody) {
extraBodyParameters.add(parameter);
isBodyParameter = true;
Expand Down Expand Up @@ -1289,26 +1290,26 @@ private Map<PathItem, io.swagger.v3.oas.models.Operation> readOperations(String
swaggerParam.setName(paramName);
}
paramAnn.stringValue("description").ifPresent(swaggerParam::setDescription);
Optional<Boolean> required = paramAnn.booleanValue("required");
if (required.isPresent()) {
swaggerParam.setRequired(required.get() ? true : null);
var required = paramAnn.booleanValue("required").orElse(false);
if (required) {
swaggerParam.setRequired(true);
}
Optional<Boolean> deprecated = paramAnn.booleanValue("deprecated");
if (deprecated.isPresent()) {
swaggerParam.setDeprecated(deprecated.get() ? true : null);
var deprecated = paramAnn.booleanValue("deprecated").orElse(false);
if (deprecated) {
swaggerParam.setDeprecated(true);
}
Optional<Boolean> allowEmptyValue = paramAnn.booleanValue("allowEmptyValue");
if (allowEmptyValue.isPresent()) {
swaggerParam.setAllowEmptyValue(allowEmptyValue.get() ? true : null);
var allowEmptyValue = paramAnn.booleanValue("allowEmptyValue").orElse(false);
if (allowEmptyValue) {
swaggerParam.setAllowEmptyValue(true);
}
Optional<Boolean> allowReserved = paramAnn.booleanValue("allowReserved");
if (allowReserved.isPresent()) {
swaggerParam.setAllowReserved(allowReserved.get() ? true : null);
var allowReserved = paramAnn.booleanValue("allowReserved").orElse(false);
if (allowReserved) {
swaggerParam.setAllowReserved(true);
}
paramAnn.stringValue("example").ifPresent(swaggerParam::setExample);
Optional<ParameterStyle> style = paramAnn.get("style", ParameterStyle.class);
if (style.isPresent()) {
swaggerParam.setStyle(paramStyle(style.get()));
var style = paramAnn.get("style", ParameterStyle.class).orElse(ParameterStyle.DEFAULT);
if (style != ParameterStyle.DEFAULT) {
swaggerParam.setStyle(paramStyle(style));
}
paramAnn.stringValue("ref").ifPresent(swaggerParam::set$ref);
Optional<ParameterIn> in = paramAnn.get("in", ParameterIn.class);
Expand Down Expand Up @@ -1731,20 +1732,19 @@ private void readCallbacks(MethodElement element, VisitorContext context,
return;
}
for (AnnotationValue<Callback> callbackAnn : callbackAnnotations) {
final Optional<String> name = callbackAnn.stringValue("name");
if (name.isEmpty()) {
String callbackName = callbackAnn.stringValue("name").orElse(null);
if (StringUtils.isEmpty(callbackName)) {
continue;
}
String callbackName = name.get();
final Optional<String> ref = callbackAnn.stringValue("ref");
if (ref.isPresent()) {
String refCallback = ref.get().substring(COMPONENTS_CALLBACKS_PREFIX.length());
String ref = callbackAnn.stringValue("ref").orElse(null);
if (StringUtils.isNotEmpty(ref)) {
String refCallback = ref.substring(COMPONENTS_CALLBACKS_PREFIX.length());
processCallbackReference(context, swaggerOperation, callbackName, refCallback);
continue;
}
final Optional<String> expr = callbackAnn.stringValue("callbackUrlExpression");
if (expr.isPresent()) {
processUrlCallbackExpression(context, swaggerOperation, callbackAnn, callbackName, expr.get(), jsonViewClass);
String expr = callbackAnn.stringValue("callbackUrlExpression").orElse(null);
if (StringUtils.isNotEmpty(expr)) {
processUrlCallbackExpression(context, swaggerOperation, callbackAnn, callbackName, expr, jsonViewClass);
} else {
processCallbackReference(context, swaggerOperation, callbackName, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
Expand Down Expand Up @@ -134,6 +133,7 @@
import static io.micronaut.openapi.visitor.ConfigUtils.getExpandableProperties;
import static io.micronaut.openapi.visitor.ConfigUtils.getSchemaDecoration;
import static io.micronaut.openapi.visitor.ConfigUtils.isJsonViewDefaultInclusion;
import static io.micronaut.openapi.visitor.ContextUtils.ARGUMENT_BOOLEAN;
import static io.micronaut.openapi.visitor.ContextUtils.warn;
import static io.micronaut.openapi.visitor.ConvertUtils.parseJsonString;
import static io.micronaut.openapi.visitor.ConvertUtils.resolveExtensions;
Expand Down Expand Up @@ -1096,7 +1096,7 @@ protected void processSchemaProperty(VisitorContext context, TypedElement elemen
boolean isAutoRequiredMode = true;
boolean isRequiredDefaultValueSet = false;
if (schemaAnnotationValue != null) {
elementSchemaRequired = schemaAnnotationValue.get("required", Argument.of(Boolean.TYPE));
elementSchemaRequired = schemaAnnotationValue.get("required", ARGUMENT_BOOLEAN);
isRequiredDefaultValueSet = !schemaAnnotationValue.contains("required");
io.swagger.v3.oas.annotations.media.Schema.RequiredMode requiredMode = schemaAnnotationValue.enumValue("requiredMode", io.swagger.v3.oas.annotations.media.Schema.RequiredMode.class).orElse(null);
if (requiredMode == io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED) {
Expand Down Expand Up @@ -1808,9 +1808,9 @@ private void bindSchemaIfNecessary(VisitorContext context, AnnotationValue<?> av
final Optional<String> impl = av.stringValue("implementation");
final Optional<String> not = av.stringValue("not");
final Optional<String> schema = av.stringValue("schema");
final Optional<String[]> anyOf = av.get("anyOf", Argument.of(String[].class));
final Optional<String[]> oneOf = av.get("oneOf", Argument.of(String[].class));
final Optional<String[]> allOf = av.get("allOf", Argument.of(String[].class));
var anyOfList = av.stringValues("anyOf");
var oneOfList = av.stringValues("oneOf");
var allOfList = av.stringValues("allOf");
// remap keys.
Object o = valueMap.remove("defaultValue");
if (o != null) {
Expand All @@ -1832,9 +1832,15 @@ private void bindSchemaIfNecessary(VisitorContext context, AnnotationValue<?> av
schemaToValueMap(schemaMap, schemaNot);
valueMap.put("not", schemaMap);
}
anyOf.ifPresent(anyOfList -> bindSchemaForComposite(context, valueMap, anyOfList, "anyOf", jsonViewClass));
oneOf.ifPresent(oneOfList -> bindSchemaForComposite(context, valueMap, oneOfList, "oneOf", jsonViewClass));
allOf.ifPresent(allOfList -> bindSchemaForComposite(context, valueMap, allOfList, "allOf", jsonViewClass));
if (anyOfList.length > 0) {
bindSchemaForComposite(context, valueMap, anyOfList, "anyOf", jsonViewClass);
}
if (oneOfList.length > 0) {
bindSchemaForComposite(context, valueMap, oneOfList, "oneOf", jsonViewClass);
}
if (allOfList.length > 0) {
bindSchemaForComposite(context, valueMap, allOfList, "allOf", jsonViewClass);
}
}
if (DiscriminatorMapping.class.getName().equals(av.getAnnotationName()) && schema.isPresent()) {
final String className = schema.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public final class ContextUtils {
public static final Argument<Map<String, ConfigUtils.SchemaDecorator>> ARGUMENT_SCHEMA_DECORATORS_MAP = new GenericArgument<>() { };
public static final Argument<Map<String, ConfigUtils.CustomSchema>> ARGUMENT_CUSTOM_SCHEMA_MAP = new GenericArgument<>() { };
public static final Argument<Map<String, GroupProperties>> ARGUMENT_GROUP_PROPERTIES_MAP = new GenericArgument<>() { };
public static final Argument<Boolean> ARGUMENT_BOOLEAN = Argument.of(Boolean.TYPE);

private ContextUtils() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.security.SecurityScheme;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -87,8 +86,8 @@
import static io.micronaut.openapi.visitor.ConfigUtils.getEnv;
import static io.micronaut.openapi.visitor.ConfigUtils.getExpandableProperties;
import static io.micronaut.openapi.visitor.ConfigUtils.getGroupProperties;
import static io.micronaut.openapi.visitor.ConfigUtils.isSpecGenerationEnabled;
import static io.micronaut.openapi.visitor.ConfigUtils.isOpenApiEnabled;
import static io.micronaut.openapi.visitor.ConfigUtils.isSpecGenerationEnabled;
import static io.micronaut.openapi.visitor.ConfigUtils.readOpenApiConfigFile;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_OPENAPI_ENDPOINT_CLASS_TAGS;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_OPENAPI_ENDPOINT_SECURITY_REQUIREMENTS;
Expand All @@ -112,6 +111,7 @@
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_VIEWS_SPEC;
import static io.micronaut.openapi.visitor.SchemaUtils.EMPTY_SIMPLE_SCHEMA;
import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_OBJECT;
import static io.micronaut.openapi.visitor.SchemaUtils.copyOpenApi;
import static io.micronaut.openapi.visitor.SchemaUtils.getOperationOnPathItem;
import static io.micronaut.openapi.visitor.SchemaUtils.setOperationOnPathItem;
import static io.swagger.v3.oas.models.Components.COMPONENTS_SCHEMAS_REF;
Expand Down Expand Up @@ -176,8 +176,9 @@ public void visitClass(ClassElement element, VisitorContext context) {

OpenAPI existing = ContextUtils.get(Utils.ATTR_OPENAPI, OpenAPI.class, context);
if (existing != null) {
Optional.ofNullable(openApi.getInfo())
.ifPresent(existing::setInfo);
if (openApi.getInfo() != null) {
existing.setInfo(openApi.getInfo());
}
copyOpenApi(existing, openApi);
} else {
ContextUtils.put(Utils.ATTR_OPENAPI, openApi, context);
Expand Down Expand Up @@ -227,39 +228,6 @@ private void mergeAdditionalSwaggerFiles(ClassElement element, VisitorContext co
}
}

/**
* Copy information from one {@link OpenAPI} object to another.
*
* @param to The {@link OpenAPI} object to copy to
* @param from The {@link OpenAPI} object to copy from
*/
private void copyOpenApi(OpenAPI to, OpenAPI from) {
if (to != null && from != null) {
Optional.ofNullable(from.getTags()).ifPresent(tags -> tags.forEach(to::addTagsItem));
Optional.ofNullable(from.getServers()).ifPresent(servers -> servers.forEach(to::addServersItem));
Optional.ofNullable(from.getSecurity()).ifPresent(securityRequirements -> securityRequirements.forEach(to::addSecurityItem));
Optional.ofNullable(from.getPaths()).ifPresent(paths -> paths.forEach(to::path));
Optional.ofNullable(from.getComponents()).ifPresent(components -> {
Map<String, Schema> schemas = components.getSchemas();

if (CollectionUtils.isNotEmpty(schemas)) {
schemas.forEach((k, v) -> {
if (v.getName() == null) {
v.setName(k);
}
});
schemas.forEach(to::schema);
}
Map<String, SecurityScheme> securitySchemes = components.getSecuritySchemes();
if (securitySchemes != null && !securitySchemes.isEmpty()) {
securitySchemes.forEach(to::schemaRequirement);
}
});
Optional.ofNullable(from.getExternalDocs()).ifPresent(to::externalDocs);
Optional.ofNullable(from.getExtensions()).ifPresent(extensions -> extensions.forEach(to::addExtension));
}
}

private OpenAPI readOpenApi(ClassElement element, VisitorContext context) {
return element.findAnnotation(OpenAPIDefinition.class).flatMap(o -> {
Optional<OpenAPI> result = toValue(o.getValues(), context, OpenAPI.class, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package io.micronaut.openapi.visitor;

import java.util.List;
import java.util.Optional;

import javax.annotation.processing.SupportedOptions;

Expand All @@ -33,8 +32,8 @@
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;

import static io.micronaut.openapi.visitor.ConfigUtils.isSpecGenerationEnabled;
import static io.micronaut.openapi.visitor.ConfigUtils.isOpenApiEnabled;
import static io.micronaut.openapi.visitor.ConfigUtils.isSpecGenerationEnabled;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ENABLED;

/**
Expand All @@ -55,12 +54,12 @@ public void visitClass(ClassElement element, VisitorContext context) {
if (ArrayUtils.isNotEmpty(classes)) {
List<AnnotationValue<Tag>> tags = includeAnnotation.getAnnotations("tags", Tag.class);
List<AnnotationValue<SecurityRequirement>> security = includeAnnotation.getAnnotations("security", SecurityRequirement.class);
Optional<String> customUri = includeAnnotation.stringValue("uri");
String customUri = includeAnnotation.stringValue("uri").orElse(null);
List<String> groups = List.of(includeAnnotation.stringValues("groups"));
List<String> groupsExcluded = List.of(includeAnnotation.stringValues("groupsExcluded"));

OpenApiGroupInfoVisitor groupVisitor = new OpenApiGroupInfoVisitor(groups, groupsExcluded);
OpenApiControllerVisitor controllerVisitor = new OpenApiControllerVisitor(tags, security, customUri.orElse(null));
OpenApiControllerVisitor controllerVisitor = new OpenApiControllerVisitor(tags, security, customUri);
OpenApiEndpointVisitor endpointVisitor = new OpenApiEndpointVisitor(true, tags.isEmpty() ? null : tags, security.isEmpty() ? null : security);
for (String className : classes) {
var classEl = ContextUtils.getClassElement(className, context);
Expand Down
Loading

0 comments on commit 447d224

Please sign in to comment.