Date: Mon, 5 Apr 2021 12:53:38 +0200
Subject: [PATCH 24/79] OpenAPI 3.1 - initial support
---
.../annotations/media/PatternProperties.java | 48 +++
.../annotations/media/PatternProperty.java | 66 ++++
.../annotations/media/SchemaProperties.java | 46 +++
.../oas/annotations/media/SchemaProperty.java | 64 ++++
.../v3/core/converter/ModelConverters.java | 9 +-
.../v3/core/jackson/CallbackSerializer.java | 5 +-
.../v3/core/jackson/ModelResolver.java | 160 ++++++++-
.../v3/core/jackson/Schema31Serializer.java | 38 ++
.../v3/core/jackson/mixin/Schema31Mixin.java | 37 ++
.../jackson/mixin/SchemaConverterMixin.java | 36 ++
.../v3/core/jackson/mixin/SchemaMixin.java | 14 +
.../core/util/ApiResponses31Deserializer.java | 9 +
.../core/util/ApiResponsesDeserializer.java | 15 +-
.../v3/core/util/DeserializationModule31.java | 30 ++
.../java/io/swagger/v3/core/util/Json31.java | 72 ++++
.../v3/core/util/Model31Deserializer.java | 22 ++
.../v3/core/util/ObjectMapperFactory.java | 148 ++++++--
.../core/util/OpenAPISchema2JsonSchema.java | 34 ++
.../v3/core/util/Parameter31Deserializer.java | 32 ++
.../v3/core/util/ParameterDeserializer.java | 25 +-
.../v3/core/util/Paths31Deserializer.java | 8 +
.../v3/core/util/PathsDeserializer.java | 16 +-
.../core/util/SecuritySchemeDeserializer.java | 4 +
.../java/io/swagger/v3/core/util/Yaml31.java | 62 ++++
.../OpenAPI3_1DeserializationTest.java | 45 +++
.../core/matchers/SerializationMatchers.java | 26 +-
.../v31/PatternAndSchemaPropertiesTest.java | 181 ++++++++++
.../resolving/v31/model/AnnotatedPet.java | 109 ++++++
.../AnnotatedPetSinglePatternProperty.java | 93 +++++
.../v3/core/resolving/v31/model/Category.java | 51 +++
.../resolving/v31/model/CustomGenerator.java | 32 ++
.../resolving/v31/model/ExtensionUser.java | 128 +++++++
.../core/resolving/v31/model/JacksonBean.java | 76 ++++
.../v31/model/ListOfStringsBeanParam.java | 17 +
.../v31/model/ModelWithJsonIdentity.java | 145 ++++++++
.../model/ModelWithJsonIdentityCyclic.java | 29 ++
.../resolving/v31/model/MultipleBaseBean.java | 11 +
.../resolving/v31/model/MultipleSub1Bean.java | 8 +
.../resolving/v31/model/MultipleSub2Bean.java | 8 +
.../resolving/v31/model/NotFoundModel.java | 30 ++
.../v3/core/resolving/v31/model/Pet.java | 76 ++++
.../v3/core/resolving/v31/model/Tag.java | 44 +++
.../v3/core/resolving/v31/model/User.java | 107 ++++++
.../OpenAPI3_1SerializationTest.java | 335 ++++++++++++++++++
.../specFiles/3.1.0/changelog-3.1.yaml | 137 +++++++
.../specFiles/3.1.0/petstore-3.1.yaml | 124 +++++++
.../integration/GenericOpenApiContext.java | 115 +++++-
.../IntegrationObjectMapperFactory.java | 4 +
.../oas/integration/SwaggerConfiguration.java | 24 ++
.../integration/api/OpenAPIConfiguration.java | 4 +
.../jaxrs2/integration/SortedOutputTest.java | 13 +
.../java/io/swagger/v3/oas/OpenAPI30.java | 23 ++
.../java/io/swagger/v3/oas/OpenAPI31.java | 23 ++
.../io/swagger/v3/oas/models/Components.java | 8 +
.../io/swagger/v3/oas/models/OpenAPI.java | 40 ++-
.../swagger/v3/oas/models/info/License.java | 35 +-
.../swagger/v3/oas/models/media/Schema.java | 126 ++++++-
.../oas/models/security/SecurityScheme.java | 3 +-
58 files changed, 3169 insertions(+), 61 deletions(-)
create mode 100644 modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperties.java
create mode 100644 modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperty.java
create mode 100644 modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperties.java
create mode 100644 modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperty.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/Schema31Serializer.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Schema31Mixin.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaConverterMixin.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponses31Deserializer.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/DeserializationModule31.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/Json31.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/Model31Deserializer.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPISchema2JsonSchema.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/Parameter31Deserializer.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/Paths31Deserializer.java
create mode 100644 modules/swagger-core/src/main/java/io/swagger/v3/core/util/Yaml31.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/OpenAPI3_1DeserializationTest.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/PatternAndSchemaPropertiesTest.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPet.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPetSinglePatternProperty.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Category.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/CustomGenerator.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ExtensionUser.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/JacksonBean.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ListOfStringsBeanParam.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentity.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentityCyclic.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleBaseBean.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub1Bean.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub2Bean.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/NotFoundModel.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Pet.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Tag.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/User.java
create mode 100644 modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java
create mode 100644 modules/swagger-core/src/test/resources/specFiles/3.1.0/changelog-3.1.yaml
create mode 100644 modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.yaml
create mode 100644 modules/swagger-models/src/main/java/io/swagger/v3/oas/OpenAPI30.java
create mode 100644 modules/swagger-models/src/main/java/io/swagger/v3/oas/OpenAPI31.java
diff --git a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperties.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperties.java
new file mode 100644
index 0000000000..d002806315
--- /dev/null
+++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperties.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2017 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.swagger.v3.oas.annotations.media;
+
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ * Container for repeatable {@link PatternProperty} annotation
+ *
+ * @see PatternProperty
+ */
+@Target({FIELD, METHOD, PARAMETER, TYPE, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface PatternProperties {
+ /**
+ * An array of PatternProperty annotations
+ *
+ * @return the array of the PatternProperty
+ **/
+ PatternProperty[] value() default {};
+
+}
diff --git a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperty.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperty.java
new file mode 100644
index 0000000000..c791ad02a4
--- /dev/null
+++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperty.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2021 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.swagger.v3.oas.annotations.media;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ * The annotation may be used in OpenAPI 3.1 schemas / JSON Schema.
+ *
+ * @see JSON Schema section 10.3.2.2
+ * @see Schema
+ *
+ * @since 2.1.8
+ **/
+@Target({FIELD, METHOD, PARAMETER, TYPE, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Repeatable(PatternProperties.class)
+public @interface PatternProperty {
+ /**
+ * The regex.
+ *
+ * @return the regex
+ **/
+ String regex() default "";
+
+ /**
+ * The schema to validate against for properties matching the regex.
+ *
+ * @return the schema
+ **/
+ Schema schema() default @Schema();
+
+ /**
+ * The schema of the array to validate against for properties matching the regex.
+ *
+ * @return the schema of the array
+ */
+ ArraySchema array() default @ArraySchema();
+
+}
diff --git a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperties.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperties.java
new file mode 100644
index 0000000000..62ff05b84e
--- /dev/null
+++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperties.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2017 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.swagger.v3.oas.annotations.media;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ * Container for repeatable {@link SchemaProperty} annotation
+ *
+ * @see SchemaProperty
+ */
+@Target({FIELD, METHOD, PARAMETER, TYPE, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface SchemaProperties {
+ /**
+ * An array of SchemaProperty annotations
+ *
+ * @return the array of the SchemaProperty
+ **/
+ SchemaProperty[] value() default {};
+
+}
diff --git a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperty.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperty.java
new file mode 100644
index 0000000000..33dd07617c
--- /dev/null
+++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/SchemaProperty.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2021 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.swagger.v3.oas.annotations.media;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ * The annotation may be used to define properties for an Object Schema
+ *
+ * @see Schema
+ *
+ * @since 2.1.8
+ **/
+@Target({FIELD, METHOD, PARAMETER, TYPE, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Repeatable(SchemaProperties.class)
+public @interface SchemaProperty {
+ /**
+ * The name.
+ *
+ * @return the name
+ **/
+ String name() default "";
+
+ /**
+ * The schema of the property.
+ *
+ * @return the schema
+ **/
+ Schema schema() default @Schema();
+
+ /**
+ * The schema of the array.
+ *
+ * @return the schema of the array
+ */
+ ArraySchema array() default @ArraySchema();
+
+}
diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java
index ec479d7505..0cbb130b64 100644
--- a/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java
+++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java
@@ -97,14 +97,7 @@ public ResolvedSchema readAllAsResolvedSchema(Type type) {
}
public ResolvedSchema readAllAsResolvedSchema(AnnotatedType type) {
if (shouldProcess(type.getType())) {
- ModelConverterContextImpl context = new ModelConverterContextImpl(
- converters);
-
- ResolvedSchema resolvedSchema = new ResolvedSchema();
- resolvedSchema.schema = context.resolve(type);
- resolvedSchema.referencedSchemas = context.getDefinedModels();
-
- return resolvedSchema;
+ return resolveAsResolvedSchema(type);
}
return null;
}
diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/CallbackSerializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/CallbackSerializer.java
index 374f658bd1..9d3dd5757c 100644
--- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/CallbackSerializer.java
+++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/CallbackSerializer.java
@@ -18,16 +18,19 @@ public void serialize(
Callback value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
+ // has extensions
if (value != null && value.getExtensions() != null && !value.getExtensions().isEmpty()) {
jgen.writeStartObject();
+ // not a ref
if (StringUtils.isBlank(value.get$ref())) {
if (!value.isEmpty()) {
+ // write map
for (Entry entry: value.entrySet()) {
jgen.writeObjectField(entry.getKey() , entry.getValue());
}
}
- } else { // handle ref schema serialization skipping all other props
+ } else { // handle ref schema serialization skipping all other props ...
jgen.writeStringField("$ref", value.get$ref());
}
for (String ext: value.getExtensions().keySet()) {
diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java
index 3884c6d418..9c10c21676 100644
--- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java
+++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java
@@ -33,12 +33,18 @@
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.core.util.Constants;
import io.swagger.v3.core.util.Json;
+import io.swagger.v3.core.util.Json31;
import io.swagger.v3.core.util.ObjectMapperFactory;
+import io.swagger.v3.core.util.OpenAPISchema2JsonSchema;
import io.swagger.v3.core.util.OptionalUtils;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.core.util.ReflectionUtils;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
+import io.swagger.v3.oas.annotations.media.PatternProperties;
+import io.swagger.v3.oas.annotations.media.PatternProperty;
+import io.swagger.v3.oas.annotations.media.SchemaProperties;
+import io.swagger.v3.oas.annotations.media.SchemaProperty;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.media.ArraySchema;
@@ -95,6 +101,7 @@
import static io.swagger.v3.core.util.RefUtils.constructRef;
public class ModelResolver extends AbstractModelConverter implements ModelConverter {
+
Logger LOGGER = LoggerFactory.getLogger(ModelResolver.class);
public static List NOT_NULL_ANNOTATIONS = Arrays.asList("NotNull", "NonNull", "NotBlank", "NotEmpty");
@@ -108,6 +115,11 @@ public class ModelResolver extends AbstractModelConverter implements ModelConver
*/
public static boolean enumsAsRef = System.getProperty(SET_PROPERTY_OF_ENUMS_AS_REF) != null;
+ /**
+ * @since 2.1.8
+ */
+ protected OpenAPISchema2JsonSchema jsonSchemaProcessor = new OpenAPISchema2JsonSchema();
+
public ModelResolver(ObjectMapper mapper) {
super(mapper);
}
@@ -192,11 +204,15 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
if (resolvedSchemaAnnotation != null &&
StringUtils.isNotEmpty(resolvedSchemaAnnotation.ref())) {
if (resolvedArrayAnnotation == null) {
+ Schema result = new Schema().$ref(resolvedSchemaAnnotation.ref()).name(name);
+ jsonSchemaProcessor.process(result);
return new Schema().$ref(resolvedSchemaAnnotation.ref()).name(name);
} else {
ArraySchema schema = new ArraySchema();
resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
- return schema.items(new Schema().$ref(resolvedSchemaAnnotation.ref()).name(name));
+ schema.items(new Schema().$ref(resolvedSchemaAnnotation.ref()).name(name));
+ jsonSchemaProcessor.process(schema);
+ return schema;
}
}
@@ -248,6 +264,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}
}
schema.setItems(innerSchema);
+ jsonSchemaProcessor.process(schema);
return schema;
} else {
Schema implSchema = context.resolve(aType);
@@ -259,6 +276,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
} else if (implSchema != null && implSchema.get$ref() != null) {
implSchema = new Schema().$ref(StringUtils.isNotEmpty(implSchema.get$ref()) ? implSchema.get$ref() : implSchema.getName());
}
+ if (implSchema != null) {
+ jsonSchemaProcessor.process(implSchema);
+ }
return implSchema;
}
}
@@ -306,6 +326,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}
model = new GeneratorWrapper().processJsonIdentity(annotatedType, context, _mapper, jsonIdentityInfo, jsonIdentityReference);
if (model != null) {
+ jsonSchemaProcessor.process(model);
return model;
}
}
@@ -336,14 +357,19 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
ArraySchema schema = new ArraySchema();
resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
schema.setItems(model);
+ jsonSchemaProcessor.process(schema);
return schema;
}
if (type.isEnumType() && shouldResolveEnumAsRef(resolvedSchemaAnnotation)) {
// Store off the ref and add the enum as a top-level model
+ jsonSchemaProcessor.process(model);
context.defineModel(name, model, annotatedType, null);
// Return the model as a ref only property
model = new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + name);
}
+ if (model != null) {
+ jsonSchemaProcessor.process(model);
+ }
return model;
}
@@ -362,6 +388,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
Schema resolvedModel = context.resolve(annotatedType);
if (resolvedModel != null) {
if (name != null && name.equals(resolvedModel.getName())) {
+ jsonSchemaProcessor.process(resolvedModel);
return resolvedModel;
}
}
@@ -379,7 +406,11 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
.propertyName(annotatedType.getPropertyName())
.ctxAnnotations(annotatedType.getCtxAnnotations())
.skipOverride(true);
- return context.resolve(aType);
+ Schema result = context.resolve(aType);
+ if (result != null) {
+ jsonSchemaProcessor.process(result);
+ }
+ return result;
}
List> composedSchemaReferencedClasses = getComposedSchemaReferencedClasses(type.getRawClass(), annotatedType.getCtxAnnotations(), resolvedSchemaAnnotation);
@@ -495,6 +526,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
AnnotatedType aType = OptionalUtils.unwrapOptional(annotatedType);
if (aType != null) {
model = context.resolve(aType);
+ if (model != null) {
+ jsonSchemaProcessor.process(model);
+ }
return model;
} else {
model = new Schema()
@@ -505,6 +539,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
if (!type.isContainerType() && StringUtils.isNotBlank(name)) {
// define the model here to support self/cyclic referencing of models
+ jsonSchemaProcessor.process(model);
context.defineModel(name, model, annotatedType, null);
}
@@ -731,6 +766,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
* available for modification by resolveSubtypes, when their parents are created.
*/
if (!type.isContainerType() && StringUtils.isNotBlank(name)) {
+ jsonSchemaProcessor.process(model);
context.defineModel(name, model, annotatedType, null);
}
@@ -766,6 +802,25 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}
}
+ Map patternProperties = resolvePatternProperties(type, annotatedType.getCtxAnnotations(), context);
+ if (model != null && patternProperties != null && !patternProperties.isEmpty()) {
+ if (model.getPatternProperties() == null) {
+ model.patternProperties(patternProperties);
+ } else {
+ model.getPatternProperties().putAll(patternProperties);
+ }
+ }
+
+
+ Map schemaProperties = resolveSchemaProperties(type, annotatedType.getCtxAnnotations(), context);
+ if (model != null && schemaProperties != null && !schemaProperties.isEmpty()) {
+ if (model.getProperties() == null) {
+ model.properties(schemaProperties);
+ } else {
+ model.getProperties().putAll(schemaProperties);
+ }
+ }
+
if (isComposedSchema) {
ComposedSchema composedSchema = (ComposedSchema) model;
@@ -842,6 +897,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
if (!type.isContainerType() && StringUtils.isNotBlank(name)) {
// define the model here to support self/cyclic referencing of models
+ jsonSchemaProcessor.process(model);
context.defineModel(name, model, annotatedType, null);
}
@@ -861,6 +917,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
ArraySchema schema = new ArraySchema();
schema.setItems(model);
resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
+ jsonSchemaProcessor.process(schema);
return schema;
} else {
if (model instanceof ArraySchema) {
@@ -1425,6 +1482,7 @@ private boolean resolveSubtypes(Schema model, BeanDescription bean, ModelConvert
// replace previous schema..
Class> currentType = subtype.getType();
if (StringUtils.isNotBlank(composedSchema.getName())) {
+ jsonSchemaProcessor.process(composedSchema);
context.defineModel(composedSchema.getName(), composedSchema, new AnnotatedType().type(currentType), null);
}
@@ -1522,6 +1580,104 @@ protected String resolveFormat(Annotated a, Annotation[] annotations, io.swagger
return null;
}
+ protected Map resolvePatternProperties(JavaType a, Annotation[] annotations, ModelConverterContext context) {
+
+ final Map propList = new LinkedHashMap<>();
+
+ PatternProperties props = a.getRawClass().getAnnotation(PatternProperties.class);
+ if (props != null && props.value().length > 0) {
+ for (PatternProperty prop: props.value()) {
+ propList.put(prop.regex(), prop);
+ }
+ }
+ PatternProperty singleProp = a.getRawClass().getAnnotation(PatternProperty.class);
+ if (singleProp != null) {
+ propList.put(singleProp.regex(), singleProp);
+ }
+ props = AnnotationsUtils.getAnnotation(PatternProperties.class, annotations);
+ if (props != null && props.value().length > 0) {
+ for (PatternProperty prop: props.value()) {
+ propList.put(prop.regex(), prop);
+ }
+ }
+ singleProp = AnnotationsUtils.getAnnotation(PatternProperty.class, annotations);
+ if (singleProp != null) {
+ propList.put(singleProp.regex(), singleProp);
+ }
+
+ if (propList.isEmpty()) {
+ return null;
+ }
+
+ Map patternProperties = new LinkedHashMap<>();
+
+ for (PatternProperty prop: propList.values()) {
+ String key = prop.regex();
+ if (StringUtils.isBlank(key)) {
+ continue;
+ }
+ Annotation[] propAnnotations = new Annotation[]{prop.schema(), prop.array()};
+ AnnotatedType propType = new AnnotatedType()
+ .type(String.class)
+ .ctxAnnotations(propAnnotations)
+ .resolveAsRef(true);
+ Schema resolvedPropSchema = context.resolve(propType);
+ if (resolvedPropSchema != null) {
+ patternProperties.put(key, resolvedPropSchema);
+ }
+ }
+ return patternProperties;
+ }
+
+ protected Map resolveSchemaProperties(JavaType a, Annotation[] annotations, ModelConverterContext context) {
+
+ final Map propList = new LinkedHashMap<>();
+
+ SchemaProperties props = a.getRawClass().getAnnotation(SchemaProperties.class);
+ if (props != null && props.value().length > 0) {
+ for (SchemaProperty prop: props.value()) {
+ propList.put(prop.name(), prop);
+ }
+ }
+ SchemaProperty singleProp = a.getRawClass().getAnnotation(SchemaProperty.class);
+ if (singleProp != null) {
+ propList.put(singleProp.name(), singleProp);
+ }
+ props = AnnotationsUtils.getAnnotation(SchemaProperties.class, annotations);
+ if (props != null && props.value().length > 0) {
+ for (SchemaProperty prop: props.value()) {
+ propList.put(prop.name(), prop);
+ }
+ }
+ singleProp = AnnotationsUtils.getAnnotation(SchemaProperty.class, annotations);
+ if (singleProp != null) {
+ propList.put(singleProp.name(), singleProp);
+ }
+
+ if (propList.isEmpty()) {
+ return null;
+ }
+
+ Map schemaProperties = new LinkedHashMap<>();
+
+ for (SchemaProperty prop: propList.values()) {
+ String key = prop.name();
+ if (StringUtils.isBlank(key)) {
+ continue;
+ }
+ Annotation[] propAnnotations = new Annotation[]{prop.schema(), prop.array()};
+ AnnotatedType propType = new AnnotatedType()
+ .type(String.class)
+ .ctxAnnotations(propAnnotations)
+ .resolveAsRef(true);
+ Schema resolvedPropSchema = context.resolve(propType);
+ if (resolvedPropSchema != null) {
+ schemaProperties.put(key, resolvedPropSchema);
+ }
+ }
+ return schemaProperties;
+ }
+
protected Object resolveDefaultValue(Annotated a, Annotation[] annotations, io.swagger.v3.oas.annotations.media.Schema schema) {
if (schema != null) {
if (!schema.defaultValue().isEmpty()) {
diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/Schema31Serializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/Schema31Serializer.java
new file mode 100644
index 0000000000..dcac551620
--- /dev/null
+++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/Schema31Serializer.java
@@ -0,0 +1,38 @@
+package io.swagger.v3.core.jackson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
+import io.swagger.v3.oas.models.media.Schema;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+
+public class Schema31Serializer extends JsonSerializer implements ResolvableSerializer {
+
+ private JsonSerializer