Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 4.9.2 #1079

Merged
merged 7 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ projectUrl=https://micronaut.io
githubSlug=micronaut-projects/micronaut-openapi
developers=Puneet Behl,Álvaro Sánchez-Mariscal,Iván López

org.gradle.caching=false
org.gradle.caching=true
kotlin.stdlib.default.dependency=false
org.gradle.jvmargs=-Xmx1g -Dfile.encoding=UTF-8
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@
import static io.micronaut.openapi.visitor.ElementUtils.isNullable;
import static io.micronaut.openapi.visitor.OpenApiApplicationVisitor.getSecurityProperties;
import static io.micronaut.openapi.visitor.OpenApiApplicationVisitor.isOpenApiEnabled;
import static io.micronaut.openapi.visitor.SchemaUtils.COMPONENTS_CALLBACKS_PREFIX;
import static io.micronaut.openapi.visitor.SchemaUtils.COMPONENTS_SCHEMAS_PREFIX;
import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_OBJECT;
import static io.micronaut.openapi.visitor.Utils.DEFAULT_MEDIA_TYPES;

Expand All @@ -122,8 +124,6 @@
*/
public abstract class AbstractOpenApiEndpointVisitor extends AbstractOpenApiVisitor {

public static final String COMPONENTS_CALLBACKS_PREFIX = "#/components/callbacks/";

protected static final String CONTEXT_CHILD_PATH = "internal.child.path";
protected static final String CONTEXT_CHILD_OP_ID_PREFIX = "internal.opId.prefix";
protected static final String CONTEXT_CHILD_OP_ID_SUFFIX = "internal.opId.suffix";
Expand Down Expand Up @@ -398,6 +398,8 @@ public void visitMethod(MethodElement element, VisitorContext context) {
List<MediaType> consumesMediaTypes = consumesMediaTypes(element);
Map<PathItem, io.swagger.v3.oas.models.Operation> swaggerOperations = readOperations(pathItemEntry.getKey(), httpMethod, pathItems, element, context);

boolean isRequestBodySchemaSet = false;

for (Map.Entry<PathItem, io.swagger.v3.oas.models.Operation> operationEntry : swaggerOperations.entrySet()) {
io.swagger.v3.oas.models.Operation swaggerOperation = operationEntry.getValue();
io.swagger.v3.oas.models.ExternalDocumentation externalDocs = readExternalDocs(element, context);
Expand Down Expand Up @@ -427,7 +429,12 @@ public void visitMethod(MethodElement element, VisitorContext context) {
readResponse(element, context, openAPI, swaggerOperation, javadocDescription);

if (permitsRequestBody) {
RequestBody requestBody = readSwaggerRequestBody(element, consumesMediaTypes, context);
Pair<RequestBody, Boolean> requestBodyPair = readSwaggerRequestBody(element, consumesMediaTypes, context);
RequestBody requestBody = null;
if (requestBodyPair != null) {
requestBody = requestBodyPair.getFirst();
isRequestBodySchemaSet = requestBodyPair.getSecond();
}
if (requestBody != null) {
RequestBody currentRequestBody = swaggerOperation.getRequestBody();
if (currentRequestBody != null) {
Expand All @@ -454,14 +461,15 @@ public void visitMethod(MethodElement element, VisitorContext context) {
List<TypedElement> extraBodyParameters = new ArrayList<>();
for (io.swagger.v3.oas.models.Operation operation : swaggerOperations.values()) {
processParameters(element, context, openAPI, operation, javadocDescription, permitsRequestBody, pathVariables, consumesMediaTypes, extraBodyParameters, httpMethod, matchTemplates, pathItems);
processExtraBodyParameters(context, httpMethod, openAPI, operation, javadocDescription, consumesMediaTypes, extraBodyParameters);
processExtraBodyParameters(context, httpMethod, openAPI, operation, javadocDescription, isRequestBodySchemaSet, consumesMediaTypes, extraBodyParameters);
}
}
}

private void processExtraBodyParameters(VisitorContext context, HttpMethod httpMethod, OpenAPI openAPI,
io.swagger.v3.oas.models.Operation swaggerOperation,
JavadocDescription javadocDescription,
boolean isRequestBodySchemaSet,
List<MediaType> consumesMediaTypes,
List<TypedElement> extraBodyParameters) {
RequestBody requestBody = swaggerOperation.getRequestBody();
Expand Down Expand Up @@ -491,31 +499,39 @@ private void processExtraBodyParameters(VisitorContext context, HttpMethod httpM
mediaType.setSchema(schema);
}
if (schema.get$ref() != null) {
ComposedSchema composedSchema = new ComposedSchema();
Schema extraBodyParametersSchema = new Schema();
// Composition of existing + a new schema where extra body parameters are going to be added
composedSchema.addAllOfItem(schema);
composedSchema.addAllOfItem(extraBodyParametersSchema);
schema = extraBodyParametersSchema;
mediaType.setSchema(composedSchema);
if (isRequestBodySchemaSet) {
schema = openAPI.getComponents().getSchemas().get(schema.get$ref().substring(COMPONENTS_SCHEMAS_PREFIX.length()));
} else {
ComposedSchema composedSchema = new ComposedSchema();
Schema extraBodyParametersSchema = new Schema();
// Composition of existing + a new schema where extra body parameters are going to be added
composedSchema.addAllOfItem(schema);
composedSchema.addAllOfItem(extraBodyParametersSchema);
schema = extraBodyParametersSchema;
mediaType.setSchema(composedSchema);
}
}
for (TypedElement parameter : extraBodyParameters) {
processBodyParameter(context, openAPI, javadocDescription, MediaType.of(mediaTypeName), schema, parameter);
if (!isRequestBodySchemaSet) {
processBodyParameter(context, openAPI, javadocDescription, MediaType.of(mediaTypeName), schema, parameter);
}
if (mediaTypeName.equals(MediaType.MULTIPART_FORM_DATA)) {
for (String prop : (Set<String>) schema.getProperties().keySet()) {
Map<String, Encoding> encodings = mediaType.getEncoding();
if (encodings == null) {
encodings = new HashMap<>();
mediaType.setEncoding(encodings);
}
// if content type doesn't set by annotation,
// we can set application/octet-stream for file upload classes
Encoding encoding = encodings.get(prop);
if (encoding == null && isFileUpload(parameter.getType())) {
encoding = new Encoding();
encodings.put(prop, encoding);

encoding.setContentType(MediaType.APPLICATION_OCTET_STREAM);
if (CollectionUtils.isNotEmpty(schema.getProperties())) {
for (String prop : (Set<String>) schema.getProperties().keySet()) {
Map<String, Encoding> encodings = mediaType.getEncoding();
if (encodings == null) {
encodings = new HashMap<>();
mediaType.setEncoding(encodings);
}
// if content type doesn't set by annotation,
// we can set application/octet-stream for file upload classes
Encoding encoding = encodings.get(prop);
if (encoding == null && isFileUpload(parameter.getType())) {
encoding = new Encoding();
encodings.put(prop, encoding);

encoding.setContentType(MediaType.APPLICATION_OCTET_STREAM);
}
}
}
}
Expand Down Expand Up @@ -651,9 +667,9 @@ private void processParameter(VisitorContext context, OpenAPI openAPI,
return;
}
if (permitsRequestBody && swaggerOperation.getRequestBody() == null) {
RequestBody requestBody = readSwaggerRequestBody(parameter, consumesMediaTypes, context);
if (requestBody != null) {
swaggerOperation.setRequestBody(requestBody);
Pair<RequestBody, Boolean> requestBodyPair = readSwaggerRequestBody(parameter, consumesMediaTypes, context);
if (requestBodyPair != null && requestBodyPair.getFirst() != null) {
swaggerOperation.setRequestBody(requestBodyPair.getFirst());
}
}

Expand Down Expand Up @@ -1678,15 +1694,25 @@ private void processResponses(io.swagger.v3.oas.models.Operation operation, List
}
}

private RequestBody readSwaggerRequestBody(Element element, List<MediaType> consumesMediaTypes, VisitorContext context) {
// boolean - is swagger schema has implementation
private Pair<RequestBody, Boolean> readSwaggerRequestBody(Element element, List<MediaType> consumesMediaTypes, VisitorContext context) {
AnnotationValue<io.swagger.v3.oas.annotations.parameters.RequestBody> requestBodyAnnValue =
element.findAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class).orElse(null);

if (requestBodyAnnValue == null) {
return null;
}

boolean hasSchemaImplementation = false;

AnnotationValue<io.swagger.v3.oas.annotations.media.Content> content = requestBodyAnnValue.getAnnotation("content", io.swagger.v3.oas.annotations.media.Content.class).orElse(null);
if (content != null) {
AnnotationValue<io.swagger.v3.oas.annotations.media.Schema> swaggerSchema = content.getAnnotation("schema", io.swagger.v3.oas.annotations.media.Schema.class).orElse(null);
if (swaggerSchema != null) {
hasSchemaImplementation = swaggerSchema.stringValue("implementation").orElse(null) != null;
}
}

RequestBody requestBody = toValue(requestBodyAnnValue.getValues(), context, RequestBody.class).orElse(null);
// if media type doesn't set in swagger annotation, check micronaut annotation
if (content != null
Expand All @@ -1701,7 +1727,7 @@ private RequestBody readSwaggerRequestBody(Element element, List<MediaType> cons
}
}

return requestBody;
return Pair.of(requestBody, hasSchemaImplementation);
}

private void readServers(MethodElement element, VisitorContext context, io.swagger.v3.oas.models.Operation swaggerOperation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@
import static io.micronaut.openapi.visitor.OpenApiApplicationVisitor.getConfigurationProperty;
import static io.micronaut.openapi.visitor.OpenApiApplicationVisitor.getExpandableProperties;
import static io.micronaut.openapi.visitor.OpenApiApplicationVisitor.resolvePlaceholders;
import static io.micronaut.openapi.visitor.SchemaUtils.EMPTY_SCHEMA;
import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_OBJECT;
import static io.micronaut.openapi.visitor.Utils.resolveComponents;
import static java.util.stream.Collectors.toMap;
Expand All @@ -148,7 +147,6 @@
abstract class AbstractOpenApiVisitor {

private static final Lock VISITED_ELEMENTS_LOCK = new ReentrantLock();
private static final ComposedSchema EMPTY_COMPOSED_SCHEMA = new ComposedSchema();

/**
* Stores relations between schema names and class names.
Expand Down Expand Up @@ -415,18 +413,17 @@ protected Map<CharSequence, Object> toValueMap(Map<CharSequence, Object> values,
Object[] a = (Object[]) value;
if (ArrayUtils.isNotEmpty(a)) {
Object first = a[0];
boolean areAnnotationValues = first instanceof AnnotationValue;
boolean areClassValues = first instanceof AnnotationClassValue;

if (areClassValues) {
// are class values
if ( first instanceof AnnotationClassValue) {
List<Class<?>> classes = new ArrayList<>(a.length);
for (Object o : a) {
AnnotationClassValue<?> acv = (AnnotationClassValue<?>) o;
acv.getType().ifPresent(classes::add);
}
newValues.put(key, classes);
} else if (areAnnotationValues) {
String annotationName = ((AnnotationValue<?>) first).getAnnotationName();
} else if (first instanceof AnnotationValue<?> annValue) {
String annotationName = annValue.getAnnotationName();
if (io.swagger.v3.oas.annotations.security.SecurityRequirement.class.getName().equals(annotationName)) {
List<SecurityRequirement> securityRequirements = new ArrayList<>(a.length);
for (Object o : a) {
Expand Down Expand Up @@ -835,8 +832,8 @@ protected Schema resolveSchema(OpenAPI openAPI, @Nullable Element definingElemen

Schema schema = null;

if (type instanceof EnumElement) {
schema = getSchemaDefinition(openAPI, context, type, typeArgs, definingElement, mediaTypes);
if (type instanceof EnumElement enumEl) {
schema = getSchemaDefinition(openAPI, context, enumEl, typeArgs, definingElement, mediaTypes);
} else {

boolean isPublisher = false;
Expand Down Expand Up @@ -972,7 +969,7 @@ protected Schema resolveSchema(OpenAPI openAPI, @Nullable Element definingElemen
if (schema != null) {

if (isSubstitudedType) {
processShemaAnn(schema, context, definingElement, schemaAnnotationValue);
processShemaAnn(schema, context, definingElement, type, schemaAnnotationValue);
}

if (definingElement != null && StringUtils.isEmpty(schema.getDescription())) {
Expand Down Expand Up @@ -1190,7 +1187,7 @@ protected Schema bindSchemaForElement(VisitorContext context, TypedElement eleme
Schema originalSchema = schemaToBind;

if (originalSchema.get$ref() != null) {
Schema schemaFromAnn = schemaFromAnnotation(context, element, schemaAnn);
Schema schemaFromAnn = schemaFromAnnotation(context, element, elementType, schemaAnn);
if (schemaFromAnn != null) {
schemaToBind = schemaFromAnn;
}
Expand Down Expand Up @@ -1257,7 +1254,7 @@ protected Schema bindSchemaForElement(VisitorContext context, TypedElement eleme
notOnlyRef = true;
}

boolean addSchemaToBind = !schemaToBind.equals(EMPTY_SCHEMA);
boolean addSchemaToBind = !SchemaUtils.isEmptySchema(schemaToBind);

if (addSchemaToBind) {
if (TYPE_OBJECT.equals(originalSchema.getType())) {
Expand All @@ -1266,7 +1263,7 @@ protected Schema bindSchemaForElement(VisitorContext context, TypedElement eleme
}
originalSchema.setType(null);
}
if (!originalSchema.equals(EMPTY_SCHEMA)) {
if (!SchemaUtils.isEmptySchema(originalSchema)) {
composedSchema.addAllOfItem(originalSchema);
}
} else if (isNullable && CollectionUtils.isEmpty(composedSchema.getAllOf())) {
Expand All @@ -1282,7 +1279,7 @@ protected Schema bindSchemaForElement(VisitorContext context, TypedElement eleme
composedSchema.addAllOfItem(schemaToBind);
}

if (!composedSchema.equals(EMPTY_COMPOSED_SCHEMA)
if (!SchemaUtils.isEmptySchema(composedSchema)
&& ((CollectionUtils.isNotEmpty(composedSchema.getAllOf()) && composedSchema.getAllOf().size() > 1)
|| CollectionUtils.isNotEmpty(composedSchema.getOneOf())
|| CollectionUtils.isNotEmpty(composedSchema.getAnyOf())
Expand Down Expand Up @@ -1462,18 +1459,18 @@ protected void processJavaxValidationAnnotations(Element element, ClassElement e
}
}

Schema schemaFromAnnotation(VisitorContext context, Element element, AnnotationValue<io.swagger.v3.oas.annotations.media.Schema> schemaAnn) {
Schema schemaFromAnnotation(VisitorContext context, Element element, ClassElement type, AnnotationValue<io.swagger.v3.oas.annotations.media.Schema> schemaAnn) {
if (schemaAnn == null) {
return null;
}

Schema schemaToBind = new Schema();
processShemaAnn(schemaToBind, context, element, schemaAnn);
processShemaAnn(schemaToBind, context, element, type, schemaAnn);

return schemaToBind;
}

void processShemaAnn(Schema schemaToBind, VisitorContext context, Element element, @NonNull AnnotationValue<io.swagger.v3.oas.annotations.media.Schema> schemaAnn) {
void processShemaAnn(Schema schemaToBind, VisitorContext context, Element element, ClassElement type, @NonNull AnnotationValue<io.swagger.v3.oas.annotations.media.Schema> schemaAnn) {

Map<CharSequence, Object> annValues = schemaAnn.getValues();
if (annValues.containsKey("description")) {
Expand Down Expand Up @@ -1536,7 +1533,7 @@ void processShemaAnn(Schema schemaToBind, VisitorContext context, Element elemen

String schemaDefaultValue = (String) annValues.get("defaultValue");
if (schemaDefaultValue != null) {
setDefaultValueObject(schemaToBind, schemaDefaultValue, element, schemaToBind.getType(), schemaToBind.getFormat(), false, context);
setDefaultValueObject(schemaToBind, schemaDefaultValue, type, schemaToBind.getType(), schemaToBind.getFormat(), false, context);
}
String schemaExample = (String) annValues.get("example");
if (StringUtils.isNotEmpty(schemaExample)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ public static SecurityRequirement mapToSecurityRequirement(AnnotationValue<io.sw
public static void setDefaultValueObject(Schema<?> schema, String defaultValue, @Nullable Element element, @Nullable String schemaType, @Nullable String schemaFormat, boolean isMicronautFormat, VisitorContext context) {
try {
Pair<String, String> typeAndFormat;
if (element instanceof EnumElement) {
typeAndFormat = ConvertUtils.checkEnumJsonValueType(context, (EnumElement) element, schemaType, schemaFormat);
if (element instanceof EnumElement enumEl) {
typeAndFormat = ConvertUtils.checkEnumJsonValueType(context, enumEl, schemaType, schemaFormat);
} else {
typeAndFormat = Pair.of(schemaType, schemaFormat);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;

import static io.micronaut.openapi.visitor.SchemaUtils.EMPTY_COMPOSED_SCHEMA;
import static io.micronaut.openapi.visitor.SchemaUtils.EMPTY_SCHEMA;
import static io.micronaut.openapi.visitor.SchemaUtils.EMPTY_SIMPLE_SCHEMA;
import static io.micronaut.openapi.visitor.SchemaUtils.TYPE_OBJECT;
import static io.swagger.v3.oas.models.Components.COMPONENTS_SCHEMAS_REF;
Expand Down Expand Up @@ -1451,7 +1449,7 @@ private Schema normalizeSchema(Schema schema) {
}
boolean isSameType = allOfSchema.getType() == null || allOfSchema.getType().equals(type);

if (schema.equals(EMPTY_SCHEMA) || schema.equals(EMPTY_COMPOSED_SCHEMA)
if (SchemaUtils.isEmptySchema(schema)
&& (serializedDefaultValue == null || serializedDefaultValue.equals(serializedAllOfDefaultValue))
&& (type == null || allOfSchema.getType() == null || allOfSchema.getType().equals(type))) {
normalizedSchema = allOfSchema;
Expand Down
Loading