diff --git a/core/src/main/java/io/smallrye/openapi/internal/support/SimpleTypeTarget.java b/core/src/main/java/io/smallrye/openapi/internal/support/SimpleTypeTarget.java new file mode 100644 index 000000000..a69fe68ce --- /dev/null +++ b/core/src/main/java/io/smallrye/openapi/internal/support/SimpleTypeTarget.java @@ -0,0 +1,124 @@ +package io.smallrye.openapi.internal.support; + +import java.util.Collection; +import java.util.Collections; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.Declaration; +import org.jboss.jandex.DotName; +import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.MethodParameterInfo; +import org.jboss.jandex.RecordComponentInfo; +import org.jboss.jandex.Type; +import org.jboss.jandex.TypeTarget; + +/** + * Simple wrapper type that may be used to allow a Type to be accessed like + * an AnnotationTarget. + */ +public final class SimpleTypeTarget implements AnnotationTarget { + + public static final SimpleTypeTarget create(Type type) { + return new SimpleTypeTarget(type); + } + + private final Type type; + + private SimpleTypeTarget(Type type) { + this.type = type; + } + + @Override + public Kind kind() { + return Kind.TYPE; + } + + @Override + public boolean isDeclaration() { + return false; + } + + @Override + public Declaration asDeclaration() { + throw new UnsupportedOperationException(); + } + + @Override + public ClassInfo asClass() { + throw new UnsupportedOperationException(); + } + + @Override + public FieldInfo asField() { + throw new UnsupportedOperationException(); + } + + @Override + public MethodInfo asMethod() { + throw new UnsupportedOperationException(); + } + + @Override + public MethodParameterInfo asMethodParameter() { + throw new UnsupportedOperationException(); + } + + @Override + public TypeTarget asType() { + throw new UnsupportedOperationException("Not a TypeTarget"); + } + + @Override + public RecordComponentInfo asRecordComponent() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasAnnotation(DotName name) { + return type.hasAnnotation(name); + } + + @Override + public AnnotationInstance annotation(DotName name) { + return type.annotation(name); + } + + @Override + public Collection annotations(DotName name) { + return Collections.singletonList(type.annotation(name)); + } + + @Override + public Collection annotationsWithRepeatable(DotName name, IndexView index) { + return type.annotationsWithRepeatable(name, index); + } + + @Override + public Collection annotations() { + return type.annotations(); + } + + @Override + public boolean hasDeclaredAnnotation(DotName name) { + return type.hasAnnotation(name); + } + + @Override + public AnnotationInstance declaredAnnotation(DotName name) { + return type.annotation(name); + } + + @Override + public Collection declaredAnnotationsWithRepeatable(DotName name, IndexView index) { + return type.annotationsWithRepeatable(name, index); + } + + @Override + public Collection declaredAnnotations() { + return type.annotations(); + } +} diff --git a/core/src/main/java/io/smallrye/openapi/runtime/io/parameters/RequestBodyIO.java b/core/src/main/java/io/smallrye/openapi/runtime/io/parameters/RequestBodyIO.java index 925501d1e..d393f6609 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/io/parameters/RequestBodyIO.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/io/parameters/RequestBodyIO.java @@ -93,8 +93,7 @@ private RequestBody readRequestSchema(AnnotationInstance annotation) { MediaType type = OASFactory.createMediaType(); type.setSchema(SchemaFactory.typeToSchema(scannerContext(), value(annotation, PROP_VALUE), - null, - scannerContext().getExtensions())); + null)); content.addMediaType(mediaType, type); } diff --git a/core/src/main/java/io/smallrye/openapi/runtime/io/responses/APIResponseIO.java b/core/src/main/java/io/smallrye/openapi/runtime/io/responses/APIResponseIO.java index 4d3038f03..aed9aa2bf 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/io/responses/APIResponseIO.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/io/responses/APIResponseIO.java @@ -70,8 +70,7 @@ Map readResponseSchema(AnnotationInstance annotation) { Content content = OASFactory.createContent(); Schema responseSchema = SchemaFactory.typeToSchema(scannerContext(), responseType, - null, - scannerContext().getExtensions()); + null); for (String mediaType : mediaTypes) { content.addMediaType(mediaType, OASFactory.createMediaType().schema(responseSchema)); diff --git a/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaFactory.java b/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaFactory.java index c63ae315d..469b6f8ac 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaFactory.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaFactory.java @@ -35,6 +35,7 @@ import io.smallrye.openapi.runtime.scanner.AnnotationScannerExtension; import io.smallrye.openapi.runtime.scanner.OpenApiDataObjectScanner; import io.smallrye.openapi.runtime.scanner.SchemaRegistry; +import io.smallrye.openapi.runtime.scanner.dataobject.BeanValidationScanner; import io.smallrye.openapi.runtime.scanner.dataobject.EnumProcessor; import io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner; import io.smallrye.openapi.runtime.scanner.spi.AnnotationScannerContext; @@ -544,7 +545,7 @@ static Schema readClassSchema(final AnnotationScannerContext context, Type type, * @return Schema model */ public static Schema typeToSchema(AnnotationScannerContext context, Type type) { - return typeToSchema(context, type, null, context.getExtensions()); + return typeToSchema(context, type, null); } /** @@ -552,12 +553,10 @@ public static Schema typeToSchema(AnnotationScannerContext context, Type type) { * * @param context scanning context * @param type the implementation type of the item to scan - * @param extensions list of AnnotationScannerExtensions * @return Schema model */ public static Schema typeToSchema(final AnnotationScannerContext context, Type type, - AnnotationInstance schemaAnnotation, - List extensions) { + AnnotationInstance schemaAnnotation) { Schema schema = null; Schema fromAnnotation = null; @@ -574,11 +573,10 @@ public static Schema typeToSchema(final AnnotationScannerContext context, Type t if (TypeUtil.isWrappedType(type)) { // Recurse using the optional's type - schema = typeToSchema(context, TypeUtil.unwrapType(type), null, extensions); + schema = typeToSchema(context, TypeUtil.unwrapType(type), null); } else if (currentScanner.isPresent() && currentScanner.get().isWrapperType(type)) { // Recurse using the wrapped type - schema = typeToSchema(context, currentScanner.get().unwrapType(type), null, - extensions); + schema = typeToSchema(context, currentScanner.get().unwrapType(type), null); } else if (TypeUtil.isTerminalType(type)) { schema = OASFactory.createSchema(); TypeUtil.applyTypeAttributes(type, schema); @@ -592,19 +590,21 @@ public static Schema typeToSchema(final AnnotationScannerContext context, Type t if (dimensions > 1) { // Recurse using a new array type with dimensions decremented schema.setItems( - typeToSchema(context, ArrayType.create(componentType, dimensions - 1), null, extensions)); + typeToSchema(context, ArrayType.create(componentType, dimensions - 1), null)); } else { // Recurse using the type of the array elements - schema.setItems(typeToSchema(context, componentType, null, extensions)); + schema.setItems(typeToSchema(context, componentType, null)); } } else if (type.kind() == Type.Kind.CLASS) { schema = introspectClassToSchema(context, type.asClassType(), true); } else if (type.kind() == Type.Kind.PRIMITIVE) { schema = OpenApiDataObjectScanner.process(type.asPrimitiveType()); } else { - schema = otherTypeToSchema(context, type, extensions); + schema = otherTypeToSchema(context, type); } + BeanValidationScanner.applyConstraints(context, type, schema); + if (fromAnnotation != null) { // Generate `allOf` ? schema = MergeUtil.mergeObjects(schema, fromAnnotation); @@ -768,24 +768,22 @@ private static List readClassSchemas(final AnnotationScannerContext cont .collect(Collectors.toList()); } - private static Schema otherTypeToSchema(final AnnotationScannerContext context, Type type, - List extensions) { + private static Schema otherTypeToSchema(final AnnotationScannerContext context, Type type) { if (TypeUtil.isA(context, type, MutinyConstants.MULTI_TYPE)) { // Treat as an Array Schema schema = OASFactory.createSchema().addType(SchemaType.ARRAY); Type componentType = type.asParameterizedType().arguments().get(0); // Recurse using the type of the array elements - schema.setItems(typeToSchema(context, componentType, null, extensions)); + schema.setItems(typeToSchema(context, componentType)); return schema; } else { - Type asyncType = resolveAsyncType(context, type, extensions); + Type asyncType = resolveAsyncType(context, type); return schemaRegistration(context, asyncType, OpenApiDataObjectScanner.process(context, asyncType)); } } - static Type resolveAsyncType(final AnnotationScannerContext context, Type type, - List extensions) { + static Type resolveAsyncType(final AnnotationScannerContext context, Type type) { if (type.kind() == Type.Kind.PARAMETERIZED_TYPE) { ParameterizedType pType = type.asParameterizedType(); if (pType.arguments().size() == 1 && @@ -793,7 +791,7 @@ static Type resolveAsyncType(final AnnotationScannerContext context, Type type, return pType.arguments().get(0); } } - for (AnnotationScannerExtension extension : extensions) { + for (AnnotationScannerExtension extension : context.getExtensions()) { Type asyncType = extension.resolveAsyncType(type); if (asyncType != null) return asyncType; diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiAnnotationScanner.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiAnnotationScanner.java index cb0354672..4b9c0de6c 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiAnnotationScanner.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiAnnotationScanner.java @@ -313,7 +313,7 @@ private void processClassSchemas(final AnnotationScannerContext context) { .filter(this::annotatedClasses) .map(annotation -> Type.create(annotation.target().asClass().name(), Type.Kind.CLASS)) .sorted(Comparator.comparing(Type::name)) // Process annotation classes in predictable order - .forEach(type -> SchemaFactory.typeToSchema(context, type, null, context.getExtensions())); + .forEach(type -> SchemaFactory.typeToSchema(context, type, null)); } private boolean annotatedClasses(AnnotationInstance annotation) { diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationScanner.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationScanner.java index 97593f59d..caf2bec53 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationScanner.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationScanner.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import org.eclipse.microprofile.openapi.models.media.Schema; @@ -20,6 +21,7 @@ import io.smallrye.openapi.api.constants.JacksonConstants; import io.smallrye.openapi.api.constants.KotlinConstants; import io.smallrye.openapi.internal.models.media.SchemaSupport; +import io.smallrye.openapi.internal.support.SimpleTypeTarget; import io.smallrye.openapi.runtime.scanner.spi.AnnotationScannerContext; /** @@ -202,6 +204,49 @@ public void applyConstraints(AnnotationTarget target, } } + /** + * Like {@link #applyConstraints(AnnotationTarget, Schema, String, RequirementHandler)}, + * but for constraints on {@link Type}s. + * + * @param target + * the object from which to retrieve the constraint annotations + * @param schema + * the schema to which the constraints will be applied + * @param propertyKey + * the name of the property in parentSchema that refers to the + * schema + * @param handler + * the handler to be called when a + * bean validation @NotNull constraint is encountered. + */ + public void applyConstraints(Type target, + Schema schema, + String propertyKey, + RequirementHandler handler) { + applyConstraints(SimpleTypeTarget.create(target), schema, propertyKey, handler); + } + + /** + * Apply validation constraints found on the type to the schema, if bean + * validation scanning is enabled on the given context. + * + * @param context + * current scanning context + * @param type + * the object from which to retrieve the constraint annotations + * @param schema + * the schema to which the constraints will be applied + */ + public static void applyConstraints(AnnotationScannerContext context, Type type, Schema schema) { + Optional constraintScanner = context.getBeanValidationScanner(); + + if (schema != null && constraintScanner.isPresent()) { + constraintScanner.get().applyConstraints(type, schema, null, + (target, name) -> { + /* Nothing additional to do for @NotNull */ }); + } + } + private void applyStringConstraints(AnnotationTarget target, Schema schema, String propertyKey, diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeProcessor.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeProcessor.java index 3f4c0fcc7..e14dbcbac 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeProcessor.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeProcessor.java @@ -297,6 +297,8 @@ private Schema readGenericValueType(Type valueType) { valueSchema = resolveParameterizedType(valueType, valueSchema); } + BeanValidationScanner.applyConstraints(context, valueType, valueSchema); + return valueSchema; } diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeResolver.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeResolver.java index 2f2e939a4..5b4ddba90 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeResolver.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeResolver.java @@ -414,7 +414,22 @@ private static Type[] resolveArguments(ParameterizedType type, UnaryOperator arg.kind() == Type.Kind.WILDCARD_TYPE)) { - return ParameterizedType.create(type.name(), resolveArguments(type, this::resolve), null); + ParameterizedType.Builder builder = ParameterizedType.builder(type.name()); + + Type owner = type.owner(); + if (owner != null) { + builder.setOwner(resolve(owner)); + } + + for (AnnotationInstance anno : type.annotations()) { + builder.addAnnotation(anno); + } + + for (Type arg : resolveArguments(type, this::resolve)) { + builder.addArgument(arg); + } + + return builder.build(); } return getResolvedType((Type) type); diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AbstractParameterProcessor.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AbstractParameterProcessor.java index 37f09b6af..505140feb 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AbstractParameterProcessor.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AbstractParameterProcessor.java @@ -572,7 +572,7 @@ void mapParameterSchema(Parameter param, ParameterContext context) { // readSchema *may* replace the existing schema, so we must assign. schema = SchemaFactory.readSchema(scannerContext, OASFactory.createSchema(), schemaAnnotation, defaults); } else { - schema = SchemaFactory.typeToSchema(scannerContext, context.targetType, schemaAnnotation, extensions); + schema = SchemaFactory.typeToSchema(scannerContext, context.targetType, schemaAnnotation); } ModelUtil.setParameterSchema(param, schema); @@ -681,8 +681,7 @@ protected void setSchemaProperties(Schema schema, if (schemaAnnotationSupported) { schemaAnnotation = scannerContext.annotations().getAnnotation(paramTarget, SchemaConstant.DOTNAME_SCHEMA); } - Schema paramSchema = SchemaFactory.typeToSchema(scannerContext, paramType, - schemaAnnotation, extensions); + Schema paramSchema = SchemaFactory.typeToSchema(scannerContext, paramType, schemaAnnotation); if (paramSchema == null) { // hidden diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java index 270849ed3..675eda5b4 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java @@ -465,7 +465,7 @@ default void createResponseFromRestMethod(final AnnotationScannerContext context } else if (hasKotlinContinuation(method)) { schema = kotlinContinuationToSchema(context, method); } else { - schema = SchemaFactory.typeToSchema(context, returnType, null, context.getExtensions()); + schema = SchemaFactory.typeToSchema(context, returnType, null); } Content content = OASFactory.createContent(); @@ -475,6 +475,8 @@ default void createResponseFromRestMethod(final AnnotationScannerContext context produces = getDefaultProduces(context, method); } + BeanValidationScanner.applyConstraints(context, returnType, schema); + if (schema != null && SchemaSupport.getNullable(schema) == null && TypeUtil.isOptional(returnType)) { if (schema.getType() != null) { @@ -603,7 +605,7 @@ default Schema kotlinContinuationToSchema(AnnotationScannerContext context, Meth Type type = getKotlinContinuationArgument(context, method); AnnotationInstance schemaAnnotation = context.annotations().getMethodParameterAnnotation(method, type, SchemaConstant.DOTNAME_SCHEMA); - return SchemaFactory.typeToSchema(context, type, schemaAnnotation, context.getExtensions()); + return SchemaFactory.typeToSchema(context, type, schemaAnnotation); } /** @@ -800,8 +802,7 @@ default RequestBody processRequestBody(final AnnotationScannerContext context, AnnotationInstance schemaAnnotation = context.annotations().getMethodParameterAnnotation(method, requestBodyType, SchemaConstant.DOTNAME_SCHEMA); - Schema schema = SchemaFactory.typeToSchema(context, requestBodyType, schemaAnnotation, - context.getExtensions()); + Schema schema = SchemaFactory.typeToSchema(context, requestBodyType, schemaAnnotation); if (schema != null) { ModelUtil.setRequestBodySchema(requestBody, schema, getConsumes(context)); @@ -848,8 +849,7 @@ && getConsumes(context) != null) { AnnotationInstance schemaAnnotation = context.annotations().getMethodParameterAnnotation(method, requestBodyType, SchemaConstant.DOTNAME_SCHEMA); - schema = SchemaFactory.typeToSchema(context, requestBodyType, schemaAnnotation, - context.getExtensions()); + schema = SchemaFactory.typeToSchema(context, requestBodyType, schemaAnnotation); } if (requestBody == null) { diff --git a/core/src/main/java/io/smallrye/openapi/runtime/util/Annotations.java b/core/src/main/java/io/smallrye/openapi/runtime/util/Annotations.java index e579d8a05..57ef16b27 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/util/Annotations.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/util/Annotations.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -93,9 +92,7 @@ private static List getDeclaredAnnotations(AnnotationTarget return filter(target, target.asRecordComponent().annotations()); case TYPE: - return filter(target, Optional.ofNullable(target.asType().target()) - .map(Type::annotations) - .orElseGet(Collections::emptyList)); + return new ArrayList<>(target.annotations()); default: return Collections.emptyList(); diff --git a/core/src/test/java/io/smallrye/openapi/runtime/io/schema/SchemaFactoryTest.java b/core/src/test/java/io/smallrye/openapi/runtime/io/schema/SchemaFactoryTest.java index 91f14a9c7..8e1ac195a 100644 --- a/core/src/test/java/io/smallrye/openapi/runtime/io/schema/SchemaFactoryTest.java +++ b/core/src/test/java/io/smallrye/openapi/runtime/io/schema/SchemaFactoryTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Arrays; -import java.util.Collections; import java.util.concurrent.CompletableFuture; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; @@ -31,7 +30,7 @@ void testResolveAsyncType() { null); AnnotationScannerContext context = new AnnotationScannerContext(index, ClassLoaderUtil.getDefaultClassLoader(), emptyConfig()); - Type result = SchemaFactory.resolveAsyncType(context, target, Collections.emptyList()); + Type result = SchemaFactory.resolveAsyncType(context, target); assertEquals(STRING_TYPE, result); } @@ -41,7 +40,7 @@ void testWildcardSchemaIsEmpty() { AnnotationScannerContext context = new AnnotationScannerContext(index, ClassLoaderUtil.getDefaultClassLoader(), emptyConfig()); Type type = WildcardType.create(null, false); - Schema result = SchemaFactory.typeToSchema(context, type, null, Collections.emptyList()); + Schema result = SchemaFactory.typeToSchema(context, type, null); assertNull(result.getType()); } diff --git a/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationResourceTest.java b/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationResourceTest.java index 1f49c05b0..ca55e491c 100644 --- a/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationResourceTest.java +++ b/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/dataobject/BeanValidationResourceTest.java @@ -1,6 +1,9 @@ package io.smallrye.openapi.runtime.scanner.dataobject; import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import org.eclipse.microprofile.openapi.models.OpenAPI; import org.jboss.jandex.Index; @@ -64,4 +67,52 @@ void testInheritedBVConstraints(Index index) throws IOException, JSONException { printToConsole(result); assertJsonEquals("dataobject/schema.inherited-bv-constraints.json", result); } + + @Test + void testJaxRsResponseNestedTypeBVConstraints() throws IOException, JSONException { + @jakarta.ws.rs.Path("/") + @jakarta.ws.rs.Consumes(jakarta.ws.rs.core.MediaType.APPLICATION_JSON) + @jakarta.ws.rs.Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON) + class ValidationResource { + @jakarta.ws.rs.PUT + @jakarta.ws.rs.Path("single") + public void putSingle(@jakarta.validation.constraints.Size(min = 10) String body) { + } + + @jakarta.ws.rs.GET + @jakarta.ws.rs.Path("single") + @jakarta.validation.constraints.Size(min = 10) + public String getSingle() { + return "a"; + } + + @jakarta.ws.rs.PUT + @jakarta.ws.rs.Path("many") + public void putMany(List<@jakarta.validation.constraints.Size(min = 10) String> body) { + } + + @jakarta.ws.rs.GET + @jakarta.ws.rs.Path("many") + public List<@jakarta.validation.constraints.Size(min = 10) String> getMany() { + return Arrays.asList("a", "b", "c"); + } + + @jakarta.ws.rs.PUT + @jakarta.ws.rs.Path("nested") + public void putNested( + Map>> body) { + } + + @jakarta.ws.rs.GET + @jakarta.ws.rs.Path("nested") + public Map>> getNested() { + return null; + } + } + + OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), Index.of(ValidationResource.class)); + OpenAPI result = scanner.scan(); + printToConsole(result); + assertJsonEquals("dataobject/schema.type-target-constraints.json", result); + } } diff --git a/extension-jaxrs/src/test/resources/io/smallrye/openapi/runtime/scanner/dataobject/schema.type-target-constraints.json b/extension-jaxrs/src/test/resources/io/smallrye/openapi/runtime/scanner/dataobject/schema.type-target-constraints.json new file mode 100644 index 000000000..075d28f3c --- /dev/null +++ b/extension-jaxrs/src/test/resources/io/smallrye/openapi/runtime/scanner/dataobject/schema.type-target-constraints.json @@ -0,0 +1,136 @@ +{ + "openapi" : "3.1.0", + "paths" : { + "/many" : { + "put" : { + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string", + "minLength" : 10 + } + } + } + }, + "required" : true + }, + "responses" : { + "204" : { + "description" : "No Content" + } + } + }, + "get" : { + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "type" : "string", + "minLength" : 10 + } + } + } + } + } + } + } + }, + "/nested" : { + "put" : { + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string", + "minLength" : 10 + } + }, + "minProperties" : 1 + } + } + } + }, + "required" : true + }, + "responses" : { + "204" : { + "description" : "No Content" + } + } + }, + "get" : { + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string", + "minLength" : 10 + } + }, + "minProperties" : 1 + } + } + } + } + } + } + } + }, + "/single" : { + "put" : { + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "string", + "minLength" : 10 + } + } + }, + "required" : true + }, + "responses" : { + "204" : { + "description" : "No Content" + } + } + }, + "get" : { + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "string", + "minLength" : 10 + } + } + } + } + } + } + } + } +} diff --git a/pom.xml b/pom.xml index 168e2323f..ed05f8c34 100644 --- a/pom.xml +++ b/pom.xml @@ -39,10 +39,13 @@ core/src/main/java/io/smallrye/openapi/api/models/**/*.java, + core/src/main/java/io/smallrye/openapi/internal/support/SimpleTypeTarget.java, testsuite/data/**/*.java, tools/model-apt/**/*.java