diff --git a/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaConstant.java b/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaConstant.java index bf86b9580..3be863bb0 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaConstant.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaConstant.java @@ -107,11 +107,18 @@ public class SchemaConstant { public static final String PROP_PREFIX_ITEMS = "prefixItems"; public static final String PROP_CONTAINS = "contains"; public static final String PROP_PATTERN_PROPERTIES = "patternProperties"; + public static final String PROP_IF_SCHEMA = "ifSchema"; + public static final String PROP_THEN_SCHEMA = "thenSchema"; + public static final String PROP_ELSE_SCHEMA = "elseSchema"; + public static final String PROP_CONST_VALUE = "constValue"; + public static final String PROP_COMMENT_FIELD = "comment"; // Only in SchemaFactory ? public static final String PROP_REQUIRED_PROPERTIES = "requiredProperties"; public static final String PROP_PROPERTIES = "properties"; public static final String PROP_NOT = "not"; + public static final String PROP_REGEX = "regex"; + public static final String PROP_REQUIRES = "requires"; public static final String DIALECT_OAS31 = "https://spec.openapis.org/oas/3.1/dialect/base"; public static final String DIALECT_JSON_2020_12 = "https://json-schema.org/draft/2020-12/schema"; 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 250d3d783..1b7279fd9 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 @@ -1,5 +1,7 @@ package io.smallrye.openapi.runtime.io.schema; +import static java.util.Arrays.asList; + import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; @@ -154,6 +156,12 @@ public static Schema readSchema(final AnnotationScannerContext context, types -> readClassSchemas(context, types, false), defaults)); schema.setAllOf(SchemaFactory.> readAttr(context, annotation, SchemaConstant.PROP_ALL_OF, types -> readClassSchemas(context, types, true), defaults)); + schema.setIfSchema(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_IF_SCHEMA, + types -> readClassSchema(context, types, true), defaults)); + schema.setThenSchema(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_THEN_SCHEMA, + types -> readClassSchema(context, types, true), defaults)); + schema.setElseSchema(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_ELSE_SCHEMA, + types -> readClassSchema(context, types, true), defaults)); schema.setTitle(readAttr(context, annotation, SchemaConstant.PROP_TITLE, defaults)); schema.setMultipleOf(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_MULTIPLE_OF, BigDecimal::valueOf, defaults)); @@ -191,7 +199,23 @@ public static Schema readSchema(final AnnotationScannerContext context, final SchemaType type = readSchemaType(context, annotation, schema, defaults); SchemaImpl.setType(schema, type); - schema.setExamples(wrapInList(parseSchemaAttr(context, annotation, SchemaConstant.PROP_EXAMPLE, defaults, type))); + + Object example = parseSchemaAttr(context, annotation, SchemaConstant.PROP_EXAMPLE, defaults, type); + List examples = SchemaFactory.> readAttr(context, annotation, + SchemaConstant.PROP_EXAMPLES, + egs -> Arrays.stream(egs) + .map(e -> parseValue(context, e, type)) + .collect(Collectors.toCollection(ArrayList::new)), + defaults); + if (examples != null) { + if (example != null) { + examples.add(example); + } + schema.setExamples(examples); + } else { + schema.setExamples(wrapInList(example)); + } + schema.setDefaultValue( parseSchemaAttr(context, annotation, SchemaConstant.PROP_DEFAULT_VALUE, defaults, type)); schema.setDiscriminator(context.io().discriminatorIO().read(annotation)); @@ -199,6 +223,8 @@ public static Schema readSchema(final AnnotationScannerContext context, schema.setMinItems(readAttr(context, annotation, SchemaConstant.PROP_MIN_ITEMS, defaults)); schema.setUniqueItems(readAttr(context, annotation, SchemaConstant.PROP_UNIQUE_ITEMS, defaults)); schema.setExtensions(context.io().extensionIO().readExtensible(annotation)); + schema.setComment(readAttr(context, annotation, SchemaConstant.PROP_COMMENT_FIELD, defaults)); + schema.setConstValue(parseSchemaAttr(context, annotation, SchemaConstant.PROP_CONST_VALUE, defaults, type)); schema.setProperties(SchemaFactory.> readAttr(context, annotation, SchemaConstant.PROP_PROPERTIES, properties -> { @@ -215,17 +241,35 @@ public static Schema readSchema(final AnnotationScannerContext context, return propertySchemas; }, defaults)); - Type additionalProperties = readAttr(context, annotation, "additionalProperties", defaults); + schema.setAdditionalPropertiesSchema( + SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_ADDITIONAL_PROPERTIES, + types -> readClassSchema(context, types, true), defaults)); - if (additionalProperties != null) { - if (additionalProperties.name().equals(SchemaConstant.DOTNAME_TRUE_SCHEMA)) { - schema.setAdditionalPropertiesSchema(new SchemaImpl().booleanSchema(Boolean.TRUE)); - } else if (additionalProperties.name().equals(SchemaConstant.DOTNAME_FALSE_SCHEMA)) { - schema.setAdditionalPropertiesSchema(new SchemaImpl().booleanSchema(Boolean.FALSE)); - } else { - schema.setAdditionalPropertiesSchema(readClassSchema(context, additionalProperties, true)); - } - } + schema.setDependentRequired(SchemaFactory.>> readAttr(context, + annotation, SchemaConstant.PROP_DEPENDENT_REQUIRED, + annos -> readDependentRequired(context, annos), defaults)); + + schema.setDependentSchemas(SchemaFactory.> readAttr(context, annotation, + SchemaConstant.PROP_DEPENDENT_SCHEMAS, + annos -> readDependentSchemas(context, annos), defaults)); + + schema.setContentEncoding(readAttr(context, annotation, SchemaConstant.PROP_CONTENT_ENCODING, defaults)); + schema.setContentMediaType(readAttr(context, annotation, SchemaConstant.PROP_CONTENT_MEDIA_TYPE, defaults)); + schema.setContentSchema(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_CONTENT_SCHEMA, + types -> readClassSchema(context, types, true), defaults)); + + schema.setContains(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_CONTAINS, + types -> readClassSchema(context, types, true), defaults)); + schema.setMaxContains(readAttr(context, annotation, SchemaConstant.PROP_MAX_CONTAINS, defaults)); + schema.setMinContains(readAttr(context, annotation, SchemaConstant.PROP_MIN_CONTAINS, defaults)); + schema.setPrefixItems( + SchemaFactory.> readAttr(context, annotation, SchemaConstant.PROP_PREFIX_ITEMS, + types -> readClassSchemas(context, types, true), defaults)); + schema.setPropertyNames(SchemaFactory. readAttr(context, annotation, SchemaConstant.PROP_PROPERTY_NAMES, + types -> readClassSchema(context, types, true), defaults)); + schema.setPatternProperties(SchemaFactory.> readAttr(context, annotation, + SchemaConstant.PROP_PATTERN_PROPERTIES, + annos -> readPatternProperties(context, annos), defaults)); List enumeration = readAttr(context, annotation, SchemaConstant.PROP_ENUMERATION, (Object[] values) -> { List parsed = new ArrayList<>(values.length); @@ -467,7 +511,11 @@ static Schema readClassSchema(final AnnotationScannerContext context, Type type, return null; } Schema schema; - if (type.kind() == Type.Kind.ARRAY) { + if (type.name().equals(SchemaConstant.DOTNAME_TRUE_SCHEMA)) { + schema = new SchemaImpl().booleanSchema(true); + } else if (type.name().equals(SchemaConstant.DOTNAME_FALSE_SCHEMA)) { + schema = new SchemaImpl().booleanSchema(false); + } else if (type.kind() == Type.Kind.ARRAY) { schema = new SchemaImpl().addType(SchemaType.ARRAY); ArrayType array = type.asArrayType(); int dimensions = array.dimensions(); @@ -760,4 +808,56 @@ private static BigDecimal tolerantParseBigDecimal(String value) { return null; } } + + private static Map> readDependentRequired(AnnotationScannerContext context, + AnnotationInstance[] requireds) { + if (requireds == null || requireds.length == 0) { + return null; + } + + Map> result = new LinkedHashMap<>(); + for (AnnotationInstance required : requireds) { + String name = context.annotations().value(required, SchemaConstant.PROP_NAME); + String[] requires = context.annotations().value(required, SchemaConstant.PROP_REQUIRES); + result.put(name, new ArrayList<>(asList(requires))); + } + + return result; + } + + private static Map readDependentSchemas(AnnotationScannerContext context, + AnnotationInstance[] dependentSchemas) { + if (dependentSchemas == null || dependentSchemas.length == 0) { + return null; + } + + Map result = new LinkedHashMap<>(); + for (AnnotationInstance dependentSchema : dependentSchemas) { + String name = context.annotations().value(dependentSchema, SchemaConstant.PROP_NAME); + Type schemaClass = context.annotations().value(dependentSchema, SchemaConstant.PROP_SCHEMA); + Schema schema = readClassSchema(context, schemaClass, true); + if (schema != null) { + result.put(name, schema); + } + } + return result; + } + + private static Map readPatternProperties(AnnotationScannerContext context, + AnnotationInstance[] patternProperties) { + if (patternProperties == null || patternProperties.length == 0) { + return null; + } + + Map result = new LinkedHashMap<>(); + for (AnnotationInstance patternProperty : patternProperties) { + String regex = context.annotations().value(patternProperty, SchemaConstant.PROP_REGEX); + Type schemaClass = context.annotations().value(patternProperty, SchemaConstant.PROP_SCHEMA); + Schema schema = readClassSchema(context, schemaClass, true); + if (schema != null) { + result.put(regex, schema); + } + } + return result; + } }