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

More changes to default content type #1615

Merged
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
8 changes: 8 additions & 0 deletions core/src/main/java/io/smallrye/openapi/api/OpenApiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ default Optional<String[]> getDefaultPrimitivesConsumes() {
return getConfigValue(OpenApiConstants.DEFAULT_CONSUMES_PRIMITIVES, String[].class, Optional::of, Optional::empty);
}

default Optional<String[]> getDefaultStreamingProduces() {
return getConfigValue(OpenApiConstants.DEFAULT_PRODUCES_STREAMING, String[].class, Optional::of, Optional::empty);
}

default Optional<String[]> getDefaultStreamingConsumes() {
return getConfigValue(OpenApiConstants.DEFAULT_CONSUMES_STREAMING, String[].class, Optional::of, Optional::empty);
}

default Optional<Boolean> allowNakedPathParameter() {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import org.jboss.jandex.DotName;
Expand All @@ -27,6 +28,7 @@ public class JDKConstants {
public static final DotName DOTNAME_OPTIONAL_INT = DotName.createSimple(OptionalInt.class.getName());
public static final DotName DOTNAME_OPTIONAL_LONG = DotName.createSimple(OptionalLong.class.getName());
public static final DotName COMPLETION_STAGE_NAME = DotName.createSimple(CompletionStage.class.getName());
public static final DotName COMPLETABLE_FUTURE_NAME = DotName.createSimple(CompletableFuture.class.getName());

public static final Set<DotName> DOTNAME_OPTIONALS = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList(DOTNAME_OPTIONAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public final class OpenApiConstants {
public static final String DEFAULT_CONSUMES = SMALLRYE_PREFIX + "defaultConsumes";
public static final String DEFAULT_PRODUCES_PRIMITIVES = SMALLRYE_PREFIX + "defaultPrimitivesProduces";
public static final String DEFAULT_CONSUMES_PRIMITIVES = SMALLRYE_PREFIX + "defaultPrimitivesConsumes";
public static final String DEFAULT_PRODUCES_STREAMING = SMALLRYE_PREFIX + "defaultStreamingProduces";
public static final String DEFAULT_CONSUMES_STREAMING = SMALLRYE_PREFIX + "defaultStreamingConsumes";

public static final String MAXIMUM_STATIC_FILE_SIZE = SMALLRYE_PREFIX + "maximumStaticFileSize";
public static final String AUTO_INHERITANCE = SMALLRYE_PREFIX + "auto-inheritance";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package io.smallrye.openapi.runtime.scanner.spi;

import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand All @@ -12,6 +18,7 @@

import io.smallrye.openapi.api.OpenApiConfig;
import io.smallrye.openapi.api.constants.OpenApiConstants;
import io.smallrye.openapi.runtime.scanner.ResourceParameters;
import io.smallrye.openapi.runtime.util.TypeUtil;

/**
Expand All @@ -22,6 +29,32 @@
public abstract class AbstractAnnotationScanner implements AnnotationScanner {
private static final String EMPTY = "";

private static final Set<DotName> PRIMITIVE_OBJECTS = new HashSet<>();
private static final Set<DotName> STREAM_OBJECTS = new HashSet<>();

static {
PRIMITIVE_OBJECTS.add(DotName.createSimple(String.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Integer.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Short.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Long.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Float.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Double.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Boolean.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(Character.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(BigDecimal.class));
PRIMITIVE_OBJECTS.add(DotName.createSimple(BigInteger.class));

STREAM_OBJECTS.add(DotName.createSimple(File.class));
STREAM_OBJECTS.add(DotName.createSimple(Path.class));
STREAM_OBJECTS.add(DotName.createSimple(InputStream.class));
STREAM_OBJECTS.add(DotName.createSimple(Reader.class));
STREAM_OBJECTS.add(DotName.createSimple(Byte.class));
STREAM_OBJECTS.add(DotName.createSimple(byte[].class));
STREAM_OBJECTS.add(DotName.createSimple("io.vertx.core.file.AsyncFile"));
STREAM_OBJECTS.add(DotName.createSimple("io.vertx.core.buffer.Buffer"));

}

protected String currentAppPath = EMPTY;
private String contextRoot = EMPTY;

Expand Down Expand Up @@ -104,20 +137,51 @@ private static boolean profileIncluded(OpenApiConfig config, Set<String> profile
return config.getScanProfiles().stream().anyMatch(profiles::contains);
}

public String[] getDefaultConsumes(AnnotationScannerContext context, MethodInfo methodInfo) {
return context.getConfig().getDefaultConsumes().orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
@Override
public String[] getDefaultConsumes(AnnotationScannerContext context, MethodInfo methodInfo,
final ResourceParameters params) {
Type requestBodyType = getRequestBodyParameterClassType(context, methodInfo, params);

if (requestBodyType != null) {
if (isStreaming(requestBodyType)) {
return context.getConfig().getDefaultStreamingConsumes()
.orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
} else if (isPrimimive(requestBodyType)) {
return context.getConfig().getDefaultPrimitivesConsumes()
.orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
}
return context.getConfig().getDefaultConsumes().orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
}
return new String[] {};
}

@Override
public String[] getDefaultProduces(AnnotationScannerContext context, MethodInfo methodInfo) {
if (isPrimimive(methodInfo.returnType())) {
if (isStreaming(methodInfo.returnType())) {
return context.getConfig().getDefaultStreamingProduces().orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
} else if (isPrimimive(methodInfo.returnType())) {
return context.getConfig().getDefaultPrimitivesProduces().orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
}
return context.getConfig().getDefaultProduces().orElseGet(OpenApiConstants.DEFAULT_MEDIA_TYPES);
}

private boolean isPrimimive(Type type) {
return type.kind().equals(Type.Kind.PRIMITIVE)
|| type.name().equals(DotName.createSimple(String.class))
|| (TypeUtil.isWrappedType(type) && isPrimimive(TypeUtil.unwrapType(type)));
if (type != null) {
return type.kind().equals(Type.Kind.PRIMITIVE)
|| PRIMITIVE_OBJECTS.contains(type.name())
|| (isWrapperType(type) && isPrimimive(unwrapType(type)))
|| (TypeUtil.isWrappedType(type) && isPrimimive(TypeUtil.unwrapType(type)));
}
return false;
}

private boolean isStreaming(Type type) {
if (type != null) {
return (type.kind().equals(Type.Kind.PRIMITIVE) && type.name().equals(DotName.createSimple(byte.class)))
|| STREAM_OBJECTS.contains(type.name())
|| (isWrapperType(type) && isStreaming(unwrapType(type)))
|| (TypeUtil.isWrappedType(type) && isStreaming(TypeUtil.unwrapType(type)));
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public boolean containsScannerAnnotations(List<AnnotationInstance> instances,
// Allow runtimes to set the context root path
public void setContextRoot(String path);

public String[] getDefaultConsumes(AnnotationScannerContext context, MethodInfo methodInfo);
public String[] getDefaultConsumes(AnnotationScannerContext context, MethodInfo methodInfo, ResourceParameters params);

public String[] getDefaultProduces(AnnotationScannerContext context, MethodInfo methodInfo);

Expand Down Expand Up @@ -990,7 +990,7 @@ && getConsumes(context) != null) {
}

if (schema != null) {
ModelUtil.setRequestBodySchema(requestBody, schema, getConsumes(context));
ModelUtil.setRequestBodySchema(requestBody, schema, getConsumesForRequestBody(context));
}

if (requestBody.getRequired() == null && TypeUtil.isOptional(requestBodyType)) {
Expand All @@ -1015,6 +1015,14 @@ default String[] getConsumes(final AnnotationScannerContext context) {
return currentConsumes;
}

default String[] getConsumesForRequestBody(final AnnotationScannerContext context) {
String[] currentConsumes = context.getCurrentConsumes();
if (currentConsumes == null || currentConsumes.length == 0) {
currentConsumes = context.getDefaultConsumes();
}
return currentConsumes;
}

/**
* Go through the method parameters looking for one that is not a Kotlin Continuation,
* is not annotated with a jax-rs/spring annotation, and is not a known path parameter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class AnnotationScannerContext {
private final Set<Type> jsonViews = new LinkedHashSet<>();
private String[] currentConsumes;
private String[] currentProduces;
private String[] defaultConsumes;
private String[] defaultProduces;
private Optional<AnnotationScanner> currentScanner = Optional.empty();
private final SchemaRegistry schemaRegistry;
private final JavaSecurityProcessor javaSecurityProcessor;
Expand Down Expand Up @@ -149,6 +151,22 @@ public void setCurrentProduces(String[] currentProduces) {
this.currentProduces = currentProduces;
}

public String[] getDefaultConsumes() {
return defaultConsumes;
}

public void setDefaultConsumes(String[] defaultConsumes) {
this.defaultConsumes = defaultConsumes;
}

public String[] getDefaultProduces() {
return defaultProduces;
}

public void setDefaultProduces(String[] defaultProduces) {
this.defaultProduces = defaultProduces;
}

public Optional<AnnotationScanner> getCurrentScanner() {
return currentScanner;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ public class TypeUtil {

wrapperTypes.addAll(JaxbConstants.JAXB_ELEMENT);
wrapperTypes.add(MutinyConstants.UNI_TYPE.name());
wrapperTypes.add(JDKConstants.COMPLETION_STAGE_NAME);
wrapperTypes.add(JDKConstants.COMPLETABLE_FUTURE_NAME);
}

private static void indexOptional(Indexer indexer, String className, ClassLoader contextLoader) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,11 +446,13 @@ private void processResourceMethod(final AnnotationScannerContext context,
JaxRsLogging.log.processingMethod(method.toString());

// Figure out the current @Produces and @Consumes (if any)
String[] defaultConsumes = getDefaultConsumes(context, method);
context.setCurrentConsumes(getMediaTypes(context, method, JaxRsConstants.CONSUMES, defaultConsumes));
String[] defaultConsumes = getDefaultConsumes(context, method, getResourceParameters(context, resourceClass, method));
context.setDefaultConsumes(defaultConsumes);
context.setCurrentConsumes(getMediaTypes(context, method, JaxRsConstants.CONSUMES, defaultConsumes).orElse(null));

String[] defaultProduces = getDefaultProduces(context, method);
context.setCurrentProduces(getMediaTypes(context, method, JaxRsConstants.PRODUCES, defaultProduces));
context.setDefaultProduces(defaultProduces);
context.setCurrentProduces(getMediaTypes(context, method, JaxRsConstants.PRODUCES, defaultProduces).orElse(null));

// Process any @Operation annotation
Optional<Operation> maybeOperation = processOperation(context, resourceClass, method);
Expand All @@ -463,10 +465,7 @@ private void processResourceMethod(final AnnotationScannerContext context,
processOperationTags(context, method, context.getOpenApi(), resourceTags, operation);

// Process @Parameter annotations.
Function<AnnotationInstance, Parameter> reader = t -> ParameterReader.readParameter(context, t);

ResourceParameters params = JaxRsParameterProcessor.process(context, currentAppPath, resourceClass, method,
reader, context.getExtensions());
ResourceParameters params = getResourceParameters(context, resourceClass, method);
List<Parameter> operationParams = params.getOperationParameters();
operation.setParameters(operationParams);
if (locatorPathParameters != null && operationParams != null) {
Expand Down Expand Up @@ -528,6 +527,13 @@ private void processResourceMethod(final AnnotationScannerContext context,
}
}

private ResourceParameters getResourceParameters(final AnnotationScannerContext context,
final ClassInfo resourceClass, final MethodInfo method) {
Function<AnnotationInstance, Parameter> reader = t -> ParameterReader.readParameter(context, t);
return JaxRsParameterProcessor.process(context, currentAppPath, resourceClass, method,
reader, context.getExtensions());
}

/**
* Remove from the list of locator parameters and parameter present in the list of operation parameters.
* Parameters are considered the same if they have the same value for name and {@code in}.
Expand All @@ -549,16 +555,15 @@ static List<Parameter> excludeOperationParameters(List<Parameter> locatorParams,
* not found, search for {@code annotationName} on {@code resourceMethod}'s containing class or any
* of its super-classes or interfaces.
*/
static String[] getMediaTypes(AnnotationScannerContext context, MethodInfo resourceMethod, Set<DotName> annotationName,
String[] defaultValue) {
static Optional<String[]> getMediaTypes(AnnotationScannerContext context, MethodInfo resourceMethod,
Set<DotName> annotationName, String[] defaultValue) {

return context.getAugmentedIndex().ancestry(resourceMethod).entrySet()
.stream()
.map(e -> getMediaTypeAnnotation(e.getKey(), e.getValue(), annotationName))
.filter(Objects::nonNull)
.map(annotation -> mediaTypeValue(annotation, defaultValue))
.findFirst()
.orElse(null);
.findFirst();
}

static AnnotationInstance getMediaTypeAnnotation(ClassInfo clazz, MethodInfo method, Set<DotName> annotationName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.MethodParameterInfo;
import org.jboss.jandex.Type;

import io.smallrye.openapi.api.constants.OpenApiConstants;
Expand Down Expand Up @@ -289,10 +290,12 @@ private void processControllerMethod(final AnnotationScannerContext context,
SpringLogging.log.processingMethod(method.toString());

// Figure out the current @Produces and @Consumes (if any)
String[] defaultConsumes = getDefaultConsumes(context, method);
String[] defaultConsumes = getDefaultConsumes(context, method, getResourceParameters(context, resourceClass, method));
context.setDefaultConsumes(defaultConsumes);
context.setCurrentConsumes(getMediaTypes(method, SpringConstants.MAPPING_CONSUMES, defaultConsumes).orElse(null));

String[] defaultProduces = getDefaultProduces(context, method);
context.setDefaultProduces(defaultProduces);
context.setCurrentProduces(getMediaTypes(method, SpringConstants.MAPPING_PRODUCES, defaultProduces).orElse(null));

// Process any @Operation annotation
Expand All @@ -307,10 +310,7 @@ private void processControllerMethod(final AnnotationScannerContext context,

// Process @Parameter annotations.
PathItem pathItem = new PathItemImpl();
Function<AnnotationInstance, Parameter> reader = t -> ParameterReader.readParameter(context, t);
ResourceParameters params = SpringParameterProcessor.process(context, currentAppPath, resourceClass,
method, reader,
context.getExtensions());
ResourceParameters params = getResourceParameters(context, resourceClass, method);
operation.setParameters(params.getOperationParameters());

pathItem.setParameters(ListUtil.mergeNullableLists(locatorPathParameters, params.getPathItemParameters()));
Expand Down Expand Up @@ -360,6 +360,15 @@ private void processControllerMethod(final AnnotationScannerContext context,
}
}

private ResourceParameters getResourceParameters(final AnnotationScannerContext context,
final ClassInfo resourceClass,
final MethodInfo method) {
Function<AnnotationInstance, Parameter> reader = t -> ParameterReader.readParameter(context, t);
return SpringParameterProcessor.process(context, currentAppPath, resourceClass,
method, reader,
context.getExtensions());
}

static Optional<String[]> getMediaTypes(MethodInfo resourceMethod, String property, String[] defaultValue) {
Set<DotName> annotationNames = new HashSet<>(SpringConstants.HTTP_METHODS);
annotationNames.add(SpringConstants.REQUEST_MAPPING);
Expand All @@ -383,10 +392,12 @@ static Optional<String[]> getMediaTypes(MethodInfo resourceMethod, String proper
if (annotationValue != null) {
return Optional.of(annotationValue.asStringArray());
}

return Optional.of(defaultValue);
}

return Optional.empty();
}

public boolean isRequestBody(MethodParameterInfo mip) {
return mip.annotations().isEmpty() || Annotations.hasAnnotation(mip, SpringConstants.REQUEST_BODY);
}
}
Loading