diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index b20a55a7a9..8f9ff40168 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -20,7 +20,7 @@ public class MavenWrapperDownloader { - private static final String WRAPPER_VERSION = "0.5.3"; + private static final String WRAPPER_VERSION = "0.5.6"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 84472b532a..6cbe900624 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.6.jar diff --git a/README.md b/README.md index 5e2c27e118..bd0eae4810 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ You need the following installed and available in your $PATH: * Jackson 2.4.5 or greater -### To build from source (currently 2.1.14-SNAPSHOT) +### To build from source (currently 2.2.0-SNAPSHOT) ``` # first time building locally mvn -N diff --git a/modules/swagger-annotations/pom.xml b/modules/swagger-annotations/pom.xml index b7a51bdf7a..e145110fe9 100644 --- a/modules/swagger-annotations/pom.xml +++ b/modules/swagger-annotations/pom.xml @@ -3,7 +3,7 @@ io.swagger.core.v3 swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Content.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Content.java index c4468e26b1..583f53149b 100644 --- a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Content.java +++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Content.java @@ -62,6 +62,22 @@ **/ Schema schema() default @Schema(); + /** + * The schema properties defined for schema provided in @Schema + * + * @since 2.2.0 + * @return the schema properties + */ + SchemaProperty[] schemaProperties() default {}; + + /** + * The schema properties defined for schema provided in @Schema + * + * @since 2.2.0 + * @return the schema properties + */ + Schema additionalPropertiesSchema() default @Schema(); + /** * The schema of the array that defines the type used for the content. * 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..4b93a3cb12 --- /dev/null +++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperties.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 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..10ee8f1adf --- /dev/null +++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/PatternProperty.java @@ -0,0 +1,65 @@ +/** + * 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 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/Schema.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Schema.java index 244d3fe92c..3e6c989020 100644 --- a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Schema.java +++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/media/Schema.java @@ -23,7 +23,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.nio.file.AccessMode; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.TYPE; @@ -329,10 +328,29 @@ */ Extension[] extensions() default {}; + /** + * Allows to specify the additionalProperties value + * + * AdditionalPropertiesValue.TRUE: set to TRUE + * AdditionalPropertiesValue.FALSE: set to FALSE + * AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION: resolve from @Content.additionalPropertiesSchema + * + * @since 2.2.0 + * @return the accessMode for this schema (property) + * + */ + AdditionalPropertiesValue additionalProperties() default AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION; + enum AccessMode { AUTO, READ_ONLY, WRITE_ONLY, READ_WRITE; } + + enum AdditionalPropertiesValue { + TRUE, + FALSE, + USE_ADDITIONAL_PROPERTIES_ANNOTATION; + } } 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-annotations/src/main/java/io/swagger/v3/oas/annotations/responses/ApiResponse.java b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/responses/ApiResponse.java index 9a9ed97ba4..5a5af16036 100644 --- a/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/responses/ApiResponse.java +++ b/modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/responses/ApiResponse.java @@ -96,4 +96,11 @@ **/ String ref() default ""; + /** + * Set to true to resolve the response schema from method return type + * + * @since 2.2.0 + **/ + boolean useReturnTypeSchema() default false; + } diff --git a/modules/swagger-core/pom.xml b/modules/swagger-core/pom.xml index f7de61f09f..1b67144d0a 100644 --- a/modules/swagger-core/pom.xml +++ b/modules/swagger-core/pom.xml @@ -3,7 +3,7 @@ io.swagger.core.v3 swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 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..3137167397 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 @@ -39,6 +39,10 @@ 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 +99,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"); @@ -766,6 +771,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; @@ -1522,6 +1546,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..a582b5ce96 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/Schema31Serializer.java @@ -0,0 +1,41 @@ +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 java.io.IOException; + +public class Schema31Serializer extends JsonSerializer implements ResolvableSerializer { + + private JsonSerializer defaultSerializer; + + public Schema31Serializer(JsonSerializer serializer) { + defaultSerializer = serializer; + } + + @Override + public void resolve(SerializerProvider serializerProvider) throws JsonMappingException { + if (defaultSerializer instanceof ResolvableSerializer) { + ((ResolvableSerializer) defaultSerializer).resolve(serializerProvider); + } + } + + @Override + public void serialize( + Schema value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + + if (value.getExampleSetFlag() && value.getExample() == null) { + jgen.writeStartObject(); + defaultSerializer.unwrappingSerializer(null).serialize(value, jgen, provider); + jgen.writeNullField("example"); + jgen.writeEndObject(); + } else { + defaultSerializer.serialize(value, jgen, provider); + } + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Components31Mixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Components31Mixin.java new file mode 100644 index 0000000000..71945ebeec --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Components31Mixin.java @@ -0,0 +1,22 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.core.jackson.CallbackSerializer; +import io.swagger.v3.oas.models.callbacks.Callback; + +import java.util.Map; + +public abstract class Components31Mixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonSerialize(contentUsing = CallbackSerializer.class) + public abstract Map getCallbacks(); + +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/ComponentsMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/ComponentsMixin.java index 14b25b3c79..ec6423126b 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/ComponentsMixin.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/ComponentsMixin.java @@ -2,8 +2,10 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.swagger.v3.core.jackson.CallbackSerializer; +import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.callbacks.Callback; import java.util.Map; @@ -19,4 +21,7 @@ public abstract class ComponentsMixin { @JsonSerialize(contentUsing = CallbackSerializer.class) public abstract Map getCallbacks(); + @JsonIgnore + public abstract Map getPathItems(); + } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DateSchemaMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DateSchemaMixin.java index 86cf5ed899..ba044ad74d 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DateSchemaMixin.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DateSchemaMixin.java @@ -1,9 +1,18 @@ package io.swagger.v3.core.jackson.mixin; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Map; public abstract class DateSchemaMixin { @JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") public abstract Object getExample(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + + @JsonIgnore + public abstract Map getJsonSchema(); } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Discriminator31Mixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Discriminator31Mixin.java new file mode 100644 index 0000000000..249452630f --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Discriminator31Mixin.java @@ -0,0 +1,15 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +import java.util.Map; + +public abstract class Discriminator31Mixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DiscriminatorMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DiscriminatorMixin.java new file mode 100644 index 0000000000..d633a651f5 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/DiscriminatorMixin.java @@ -0,0 +1,11 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Map; + +public abstract class DiscriminatorMixin { + + @JsonIgnore + public abstract Map getExtensions(); +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Info31Mixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Info31Mixin.java new file mode 100644 index 0000000000..bd2265b22d --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Info31Mixin.java @@ -0,0 +1,19 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Map; + +public abstract class Info31Mixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonIgnore + public abstract String getSummary(); +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/InfoMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/InfoMixin.java new file mode 100644 index 0000000000..0eb289d552 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/InfoMixin.java @@ -0,0 +1,19 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Map; + +public abstract class InfoMixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonIgnore + public abstract String getSummary(); +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/LicenseMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/LicenseMixin.java new file mode 100644 index 0000000000..4b205a908e --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/LicenseMixin.java @@ -0,0 +1,19 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Map; + +public abstract class LicenseMixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonIgnore + public abstract String getIdentifier(); +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPI31Mixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPI31Mixin.java new file mode 100644 index 0000000000..96add18b36 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPI31Mixin.java @@ -0,0 +1,22 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.core.jackson.PathsSerializer; +import io.swagger.v3.oas.models.Paths; + +import java.util.Map; + +public abstract class OpenAPI31Mixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonSerialize(using = PathsSerializer.class) + public abstract Paths getPaths(); + +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPIMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPIMixin.java index e43b1b2152..b2296e8b39 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPIMixin.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/OpenAPIMixin.java @@ -2,8 +2,10 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.swagger.v3.core.jackson.PathsSerializer; +import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; import java.util.Map; @@ -17,4 +19,7 @@ public abstract class OpenAPIMixin { @JsonSerialize(using = PathsSerializer.class) public abstract Paths getPaths(); + + @JsonIgnore + public abstract Map getWebhooks(); } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Schema31Mixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Schema31Mixin.java new file mode 100644 index 0000000000..02170fc62e --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/Schema31Mixin.java @@ -0,0 +1,77 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Map; +import java.util.Set; + +public abstract class Schema31Mixin { + + //@JsonValue + @JsonIgnore + public abstract Map getJsonSchema(); + + @JsonIgnore + public abstract Boolean getNullable(); + + @JsonIgnore + public abstract Boolean getExclusiveMinimum(); + + @JsonIgnore + public abstract Boolean getExclusiveMaximum(); + + @JsonProperty("exclusiveMinimum") + public abstract BigDecimal getExclusiveMinimumValue(); + + @JsonProperty("exclusiveMaximum") + public abstract BigDecimal getExclusiveMaximumValue(); + + @JsonIgnore + public abstract String getType(); + + @JsonProperty("type") + @JsonSerialize(using = TypeSerializer.class) + public abstract Set getTypes(); + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonIgnore + public abstract boolean getExampleSetFlag(); + + @JsonInclude(value = JsonInclude.Include.NON_NULL, content = JsonInclude.Include.ALWAYS) + public abstract Object getExample(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + + public static class TypeSerializer extends JsonSerializer> { + + @Override + public void serialize(Set types, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + if (types != null && types.size() == 1) { + jsonGenerator.writeString((String)types.toArray()[0]); + } else if (types != null && types.size() > 1){ + jsonGenerator.writeStartArray(); + for (String t: types) { + jsonGenerator.writeString(t); + } + jsonGenerator.writeEndArray(); + } + } + } + +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaConverterMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaConverterMixin.java new file mode 100644 index 0000000000..b1b38ff4b6 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaConverterMixin.java @@ -0,0 +1,112 @@ +package io.swagger.v3.core.jackson.mixin; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.models.media.Schema; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class SchemaConverterMixin { + + @JsonIgnore + public abstract Map getJsonSchema(); + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonIgnore + public abstract boolean getExampleSetFlag(); + + @JsonInclude(value = JsonInclude.Include.NON_NULL, content = JsonInclude.Include.ALWAYS) + public abstract Object getExample(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMinimumValue(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMaximumValue(); + + @JsonIgnore + public abstract Schema getContains(); + + @JsonIgnore + public abstract String get$id(); + + @JsonIgnore + public abstract String get$anchor(); + + @JsonIgnore + public abstract String get$schema(); + + @JsonIgnore + public abstract Set getTypes(); + + @JsonIgnore + public abstract Map getPatternProperties(); + + @JsonIgnore + public abstract List getPrefixItems(); + + @JsonIgnore + public abstract String getContentEncoding(); + + @JsonIgnore + public abstract String getContentMediaType(); + + @JsonIgnore + public abstract Schema getContentSchema(); + + @JsonIgnore + public abstract Schema getPropertyNames(); + + @JsonIgnore + public abstract Object getUnevaluatedProperties(); + + @JsonIgnore + public abstract Integer getMaxContains(); + + @JsonIgnore + public abstract Integer getMinContains(); + + @JsonIgnore + public abstract Schema getAdditionalItems(); + + @JsonIgnore + public abstract Schema getUnevaluatedItems(); + + @JsonIgnore + public abstract Schema getIf(); + + @JsonIgnore + public abstract Schema getElse(); + + @JsonIgnore + public abstract Schema getThen(); + + @JsonIgnore + public abstract Map getDependentSchemas(); + + @JsonIgnore + public abstract Map> getDependentRequired(); + + @JsonIgnore + public abstract String get$comment(); + + @JsonIgnore + public abstract List getExamples(); + + @JsonIgnore + public abstract Object getConst(); + +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaMixin.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaMixin.java index f1bfb5c578..7c4637eddd 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaMixin.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/mixin/SchemaMixin.java @@ -4,8 +4,12 @@ import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.models.media.Schema; +import java.math.BigDecimal; +import java.util.List; import java.util.Map; +import java.util.Set; public abstract class SchemaMixin { @@ -20,4 +24,88 @@ public abstract class SchemaMixin { @JsonInclude(value = JsonInclude.Include.NON_NULL, content = JsonInclude.Include.ALWAYS) public abstract Object getExample(); + + @JsonIgnore + public abstract Map getJsonSchema(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMinimumValue(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMaximumValue(); + + @JsonIgnore + public abstract Schema getContains(); + + @JsonIgnore + public abstract String get$id(); + + @JsonIgnore + public abstract String get$anchor(); + + @JsonIgnore + public abstract String get$schema(); + + @JsonIgnore + public abstract Set getTypes(); + + @JsonIgnore + public abstract Map getPatternProperties(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + + @JsonIgnore + public abstract List getPrefixItems(); + + @JsonIgnore + public abstract String getContentEncoding(); + + @JsonIgnore + public abstract String getContentMediaType(); + + @JsonIgnore + public abstract Schema getContentSchema(); + + @JsonIgnore + public abstract Schema getPropertyNames(); + + @JsonIgnore + public abstract Object getUnevaluatedProperties(); + + @JsonIgnore + public abstract Integer getMaxContains(); + + @JsonIgnore + public abstract Integer getMinContains(); + + @JsonIgnore + public abstract Schema getAdditionalItems(); + + @JsonIgnore + public abstract Schema getUnevaluatedItems(); + + @JsonIgnore + public abstract Schema getIf(); + + @JsonIgnore + public abstract Schema getElse(); + + @JsonIgnore + public abstract Schema getThen(); + + @JsonIgnore + public abstract Map getDependentSchemas(); + + @JsonIgnore + public abstract Map> getDependentRequired(); + + @JsonIgnore + public abstract String get$comment(); + + @JsonIgnore + public abstract List getExamples(); + + @JsonIgnore + public abstract Object getConst(); } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java index 10ecfe84a1..677ba11dfe 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java @@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.links.LinkParameter; import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; import io.swagger.v3.oas.annotations.media.ExampleObject; +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.examples.Example; @@ -97,6 +98,7 @@ public static boolean hasSchemaAnnotation(io.swagger.v3.oas.annotations.media.Sc && schema.extensions().length == 0 && !schema.hidden() && !schema.enumAsRef() + && schema.additionalProperties().equals(io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION) ) { return false; } @@ -284,6 +286,10 @@ else if (thisSchema == null || thatSchema == null) { if (!Arrays.equals(thisSchema.extensions(), thatSchema.extensions())) { return false; } + if (!thisSchema.additionalProperties().equals(thatSchema.additionalProperties())) { + return false; + } + return true; } @@ -539,7 +545,11 @@ public static Optional getSchemaFromAnnotation(io.swagger.v3.oas.annotat ((ComposedSchema) schemaObject).addAllOfItem(allOfSchemaObject); } } - + if (schema.additionalProperties().equals(io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.TRUE)) { + schemaObject.additionalProperties(true); + } else if (schema.additionalProperties().equals(io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.FALSE)) { + schemaObject.additionalProperties(false); + } return Optional.of(schemaObject); } @@ -1044,6 +1054,46 @@ public static Optional getContent(io.swagger.v3.oas.annotations.media.C MediaType mediaType = new MediaType(); if (components != null) { getSchema(annotationContent, components, jsonViewAnnotation).ifPresent(mediaType::setSchema); + if (annotationContent.schemaProperties().length > 0) { + if (mediaType.getSchema() == null) { + mediaType.schema(new Schema().type("object")); + } + Schema oSchema = mediaType.getSchema(); + for (SchemaProperty sp: annotationContent.schemaProperties()) { + Class schemaImplementation = sp.schema().implementation(); + boolean isArray = false; + if (schemaImplementation == Void.class) { + schemaImplementation = sp.array().schema().implementation(); + if (schemaImplementation != Void.class) { + isArray = true; + } + } + getSchema(sp.schema(), sp.array(), isArray, schemaImplementation, components, jsonViewAnnotation) + .ifPresent(s -> { + if ("array".equals(oSchema.getType())) { + oSchema.getItems().addProperty(sp.name(), s); + } else { + oSchema.addProperty(sp.name(), s); + } + }); + + } + } + if ( + hasSchemaAnnotation(annotationContent.additionalPropertiesSchema()) && + mediaType.getSchema() != null && + !Boolean.TRUE.equals(mediaType.getSchema().getAdditionalProperties()) && + !Boolean.FALSE.equals(mediaType.getSchema().getAdditionalProperties())) { + getSchemaFromAnnotation(annotationContent.additionalPropertiesSchema(), components, jsonViewAnnotation) + .ifPresent(s -> { + if ("array".equals(mediaType.getSchema().getType())) { + mediaType.getSchema().getItems().additionalProperties(s); + } else { + mediaType.getSchema().additionalProperties(s); + } + } + ); + } } else { mediaType.setSchema(schema); } @@ -1726,6 +1776,14 @@ public Extension[] extensions() { public Class annotationType() { return io.swagger.v3.oas.annotations.media.Schema.class; } + + @Override + public AdditionalPropertiesValue additionalProperties() { + if (!master.additionalProperties().equals(AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION) || patch.additionalProperties().equals(AdditionalPropertiesValue.USE_ADDITIONAL_PROPERTIES_ANNOTATION)) { + return master.additionalProperties(); + } + return patch.additionalProperties(); + } }; return (io.swagger.v3.oas.annotations.media.Schema)schema; diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponses31Deserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponses31Deserializer.java new file mode 100644 index 0000000000..c1534dce31 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponses31Deserializer.java @@ -0,0 +1,9 @@ +package io.swagger.v3.core.util; + +public class ApiResponses31Deserializer extends ApiResponsesDeserializer { + + public ApiResponses31Deserializer() { + this.openapi31 = true; + } + +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponsesDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponsesDeserializer.java index 1fe6a2ef51..cbf21a1a9e 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponsesDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ApiResponsesDeserializer.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; @@ -14,21 +15,31 @@ import java.util.Map; public class ApiResponsesDeserializer extends JsonDeserializer { + + protected boolean openapi31; + @Override public ApiResponses deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + + final ObjectMapper mapper; + if (openapi31) { + mapper = Json31.mapper(); + } else { + mapper = Json.mapper(); + } ApiResponses result = new ApiResponses(); JsonNode node = jp.getCodec().readTree(jp); - ObjectNode objectNode = (ObjectNode)node; + ObjectNode objectNode = (ObjectNode) node; Map extensions = new LinkedHashMap<>(); for (Iterator it = objectNode.fieldNames(); it.hasNext(); ) { String childName = it.next(); JsonNode child = objectNode.get(childName); - // if name start with `x-` consider it an extesion + // if name start with `x-` consider it an extension if (childName.startsWith("x-")) { - extensions.put(childName, Json.mapper().convertValue(child, Object.class)); + extensions.put(childName, mapper.convertValue(child, Object.class)); } else { - result.put(childName, Json.mapper().convertValue(child, ApiResponse.class)); + result.put(childName, mapper.convertValue(child, ApiResponse.class)); } } if (!extensions.isEmpty()) { diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Callback31Deserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Callback31Deserializer.java new file mode 100644 index 0000000000..aae9fd1bf1 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Callback31Deserializer.java @@ -0,0 +1,21 @@ +package io.swagger.v3.core.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.oas.models.PathItem; + +import javax.security.auth.callback.Callback; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public class Callback31Deserializer extends CallbackDeserializer { + + public Callback31Deserializer() { + openapi31 = true; + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/CallbackDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/CallbackDeserializer.java index f5a8668f20..1fc2c3ec92 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/CallbackDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/CallbackDeserializer.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.callbacks.Callback; @@ -14,9 +15,19 @@ import java.util.Map; public class CallbackDeserializer extends JsonDeserializer { + + protected boolean openapi31; + @Override public Callback deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + + final ObjectMapper mapper; + if (openapi31) { + mapper = Json31.mapper(); + } else { + mapper = Json.mapper(); + } Callback result = new Callback(); JsonNode node = jp.getCodec().readTree(jp); ObjectNode objectNode = (ObjectNode)node; @@ -26,11 +37,11 @@ public Callback deserialize(JsonParser jp, DeserializationContext ctxt) JsonNode child = objectNode.get(childName); // if name start with `x-` consider it an extension if (childName.startsWith("x-")) { - extensions.put(childName, Json.mapper().convertValue(child, Object.class)); + extensions.put(childName, mapper.convertValue(child, Object.class)); } else if (childName.equals("$ref")) { result.$ref(child.asText()); } else { - result.put(childName, Json.mapper().convertValue(child, PathItem.class)); + result.put(childName, mapper.convertValue(child, PathItem.class)); } } if (!extensions.isEmpty()) { diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/DeserializationModule31.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/DeserializationModule31.java new file mode 100644 index 0000000000..b9003784e4 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/DeserializationModule31.java @@ -0,0 +1,30 @@ +package io.swagger.v3.core.util; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.callbacks.Callback; +import io.swagger.v3.oas.models.headers.Header; +import io.swagger.v3.oas.models.media.Encoding; +import io.swagger.v3.oas.models.media.EncodingProperty; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityScheme; + +public class DeserializationModule31 extends SimpleModule { + + public DeserializationModule31() { + + this.addDeserializer(Schema.class, new Model31Deserializer()); + this.addDeserializer(Parameter.class, new Parameter31Deserializer()); + this.addDeserializer(Header.StyleEnum.class, new HeaderStyleEnumDeserializer()); + this.addDeserializer(Encoding.StyleEnum.class, new EncodingStyleEnumDeserializer()); + this.addDeserializer(EncodingProperty.StyleEnum.class, new EncodingPropertyStyleEnumDeserializer()); + + this.addDeserializer(SecurityScheme.class, new SecurityScheme31Deserializer()); + + this.addDeserializer(ApiResponses.class, new ApiResponses31Deserializer()); + this.addDeserializer(Paths.class, new Paths31Deserializer()); + this.addDeserializer(Callback.class, new Callback31Deserializer()); + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Json31.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Json31.java new file mode 100644 index 0000000000..16a883c090 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Json31.java @@ -0,0 +1,82 @@ +package io.swagger.v3.core.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import io.swagger.v3.oas.models.media.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class Json31 { + + private static ObjectMapper mapper; + private static ObjectMapper converterMapper; + + static Logger LOGGER = LoggerFactory.getLogger(Json31.class); + + public static ObjectMapper mapper() { + if (mapper == null) { + mapper = ObjectMapperFactory.createJson31(); + } + return mapper; + } + + public static ObjectMapper converterMapper() { + if (converterMapper == null) { + converterMapper = ObjectMapperFactory.createJsonConverter(); + } + return converterMapper; + } + + public static ObjectWriter pretty() { + return mapper().writer(new DefaultPrettyPrinter()); + } + + public static String pretty(Object o) { + try { + return pretty().writeValueAsString(o); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static void prettyPrint(Object o) { + try { + System.out.println(pretty().writeValueAsString(o).replace("\r", "")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static Map jsonSchemaAsMap(String jsonSchema) { + try { + return mapper().readValue(jsonSchema, Map.class); + } catch (JsonProcessingException e) { + LOGGER.error("Exception converting jsonSchema to Map", e); + return null; + } + } + + public static Map jsonSchemaAsMap(Schema schema) { + try { + return mapper().readValue(mapper().writeValueAsString(schema), Map.class); + } catch (JsonProcessingException e) { + LOGGER.error("Exception converting jsonSchema to Map", e); + return null; + } + } + + public static Map jsonSchemaAsMap(JsonNode schema) { + try { + return mapper().readValue(mapper().writeValueAsString(schema), Map.class); + } catch (JsonProcessingException e) { + LOGGER.error("Exception converting jsonSchema to Map", e); + return null; + } + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Model31Deserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Model31Deserializer.java new file mode 100644 index 0000000000..67cfa66799 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Model31Deserializer.java @@ -0,0 +1,6 @@ +package io.swagger.v3.core.util; + +public class Model31Deserializer extends ModelDeserializer { + + public Model31Deserializer() {this.openapi31 = true;} +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java index 9c8941a936..7037a32bfe 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import io.swagger.v3.oas.models.media.ArraySchema; @@ -13,6 +14,7 @@ import io.swagger.v3.oas.models.media.DateTimeSchema; import io.swagger.v3.oas.models.media.EmailSchema; import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.JsonSchema; import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.NumberSchema; import io.swagger.v3.oas.models.media.ObjectSchema; @@ -23,61 +25,70 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; public class ModelDeserializer extends JsonDeserializer { + + protected boolean openapi31 = false; @Override public Schema deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { JsonNode node = jp.getCodec().readTree(jp); - JsonNode allOf = node.get("allOf"); - JsonNode anyOf = node.get("anyOf"); - JsonNode oneOf = node.get("oneOf"); Schema schema = null; - if (allOf != null || anyOf != null || oneOf != null) { + if (openapi31) { + schema = deserializeJsonSchema(node); + return schema; + } - return Json.mapper().convertValue(node, ComposedSchema.class); - } else { + List composed = Arrays.asList("allOf", "anyOf", "oneOf"); + for (String field: composed) { + if (node.get(field) != null) { + return Json.mapper().convertValue(node, ComposedSchema.class); + } + } + + JsonNode type = node.get("type"); + String format = node.get("format") == null ? "" : node.get("format").textValue(); - JsonNode type = node.get("type"); - String format = node.get("format") == null ? "" : node.get("format").textValue(); - - if (type != null && "array".equals(((TextNode) type).textValue())) { - schema = Json.mapper().convertValue(node, ArraySchema.class); - } else if (type != null) { - if (type.textValue().equals("integer")) { - schema = Json.mapper().convertValue(node, IntegerSchema.class); - if (StringUtils.isBlank(format)) { - schema.setFormat(null); - } - } else if (type.textValue().equals("number")) { - schema = Json.mapper().convertValue(node, NumberSchema.class); - } else if (type.textValue().equals("boolean")) { - schema = Json.mapper().convertValue(node, BooleanSchema.class); - } else if (type.textValue().equals("string")) { - if ("date".equals(format)) { - schema = Json.mapper().convertValue(node, DateSchema.class); - } else if ("date-time".equals(format)) { - schema = Json.mapper().convertValue(node, DateTimeSchema.class); - } else if ("email".equals(format)) { - schema = Json.mapper().convertValue(node, EmailSchema.class); - } else if ("password".equals(format)) { - schema = Json.mapper().convertValue(node, PasswordSchema.class); - } else if ("uuid".equals(format)) { - schema = Json.mapper().convertValue(node, UUIDSchema.class); - } else { - schema = Json.mapper().convertValue(node, StringSchema.class); - } - } else if (type.textValue().equals("object")) { - schema = deserializeObjectSchema(node); + if (type != null && "array".equals(((TextNode) type).textValue())) { + schema = Json.mapper().convertValue(node, ArraySchema.class); + } else if (type != null) { + if (type.textValue().equals("integer")) { + schema = Json.mapper().convertValue(node, IntegerSchema.class); + if (StringUtils.isBlank(format)) { + schema.setFormat(null); } - } else if (node.get("$ref") != null) { - schema = new Schema().$ref(node.get("$ref").asText()); - } else { // assume object + } else if (type.textValue().equals("number")) { + schema = Json.mapper().convertValue(node, NumberSchema.class); + } else if (type.textValue().equals("boolean")) { + schema = Json.mapper().convertValue(node, BooleanSchema.class); + } else if (type.textValue().equals("string")) { + if ("date".equals(format)) { + schema = Json.mapper().convertValue(node, DateSchema.class); + } else if ("date-time".equals(format)) { + schema = Json.mapper().convertValue(node, DateTimeSchema.class); + } else if ("email".equals(format)) { + schema = Json.mapper().convertValue(node, EmailSchema.class); + } else if ("password".equals(format)) { + schema = Json.mapper().convertValue(node, PasswordSchema.class); + } else if ("uuid".equals(format)) { + schema = Json.mapper().convertValue(node, UUIDSchema.class); + } else { + schema = Json.mapper().convertValue(node, StringSchema.class); + } + } else if (type.textValue().equals("object")) { schema = deserializeObjectSchema(node); } + } else if (node.get("$ref") != null) { + schema = new Schema().$ref(node.get("$ref").asText()); + } else { // assume object + schema = deserializeObjectSchema(node); } return schema; @@ -107,6 +118,47 @@ private Schema deserializeObjectSchema(JsonNode node) { } else { schema = Json.mapper().convertValue(node, ObjectSchema.class); } + if (schema != null) { + schema.jsonSchema(Json31.jsonSchemaAsMap(node)); + } + return schema; + } + + private Schema deserializeJsonSchema(JsonNode node) { + JsonNode additionalProperties = node.get("additionalProperties"); + JsonNode type = node.get("type"); + Schema schema = null; + + if (type != null || additionalProperties != null) { + if (type != null) { + ((ObjectNode)node).remove("type"); + } + if (additionalProperties != null) { + ((ObjectNode)node).remove("additionalProperties"); + } + schema = Json31.mapper().convertValue(node, JsonSchema.class); + if (type instanceof TextNode) { + schema.types(new LinkedHashSet<>(Arrays.asList(type.textValue()))); + } else if (type instanceof ArrayNode){ + Set types = new LinkedHashSet<>(); + ((ArrayNode)type).elements().forEachRemaining( n -> { + types.add(n.textValue()); + }); + schema.types(types); + } + if (additionalProperties != null) { + try { + Schema innerSchema = Json31.mapper().convertValue(additionalProperties, JsonSchema.class); + schema.setAdditionalProperties(innerSchema); + } catch (Exception e) { + Boolean additionalPropsBoolean = Json31.mapper().convertValue(additionalProperties, Boolean.class); + schema.setAdditionalProperties(additionalPropsBoolean); + } + } + + } else { + schema = Json31.mapper().convertValue(node, JsonSchema.class); + } return schema; } } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java index 561c8f70fd..a1d2937223 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ObjectMapperFactory.java @@ -14,15 +14,24 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.swagger.v3.core.jackson.Schema31Serializer; import io.swagger.v3.core.jackson.MediaTypeSerializer; import io.swagger.v3.core.jackson.SchemaSerializer; +import io.swagger.v3.core.jackson.mixin.Components31Mixin; import io.swagger.v3.core.jackson.mixin.ComponentsMixin; import io.swagger.v3.core.jackson.mixin.DateSchemaMixin; +import io.swagger.v3.core.jackson.mixin.Discriminator31Mixin; +import io.swagger.v3.core.jackson.mixin.DiscriminatorMixin; import io.swagger.v3.core.jackson.mixin.ExampleMixin; import io.swagger.v3.core.jackson.mixin.ExtensionsMixin; +import io.swagger.v3.core.jackson.mixin.InfoMixin; +import io.swagger.v3.core.jackson.mixin.LicenseMixin; import io.swagger.v3.core.jackson.mixin.MediaTypeMixin; +import io.swagger.v3.core.jackson.mixin.OpenAPI31Mixin; import io.swagger.v3.core.jackson.mixin.OpenAPIMixin; import io.swagger.v3.core.jackson.mixin.OperationMixin; +import io.swagger.v3.core.jackson.mixin.Schema31Mixin; +import io.swagger.v3.core.jackson.mixin.SchemaConverterMixin; import io.swagger.v3.core.jackson.mixin.SchemaMixin; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.ExternalDocumentation; @@ -39,6 +48,7 @@ import io.swagger.v3.oas.models.links.Link; import io.swagger.v3.oas.models.links.LinkParameter; import io.swagger.v3.oas.models.media.DateSchema; +import io.swagger.v3.oas.models.media.Discriminator; import io.swagger.v3.oas.models.media.Encoding; import io.swagger.v3.oas.models.media.EncodingProperty; import io.swagger.v3.oas.models.media.MediaType; @@ -63,41 +73,147 @@ public class ObjectMapperFactory { protected static ObjectMapper createJson() { - return create(null); + return create(null, false); } - protected static ObjectMapper createYaml() { + protected static ObjectMapper createYaml(boolean openapi31) { YAMLFactory factory = new YAMLFactory(); factory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER); factory.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES); factory.enable(YAMLGenerator.Feature.SPLIT_LINES); factory.enable(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS); - return create(factory); + return create(factory, openapi31); + } + + protected static ObjectMapper createYaml() { + return createYaml(false); + } + + protected static ObjectMapper createJson31() { + return create(null, true); + } + + + protected static ObjectMapper createYaml31() { + return createYaml(true); } - private static ObjectMapper create(JsonFactory jsonFactory) { + private static ObjectMapper create(JsonFactory jsonFactory, boolean openapi31) { ObjectMapper mapper = jsonFactory == null ? new ObjectMapper() : new ObjectMapper(jsonFactory); - // handle ref schema serialization skipping all other props - mapper.registerModule(new SimpleModule() { - @Override - public void setupModule(SetupContext context) { - super.setupModule(context); - context.addBeanSerializerModifier(new BeanSerializerModifier() { - @Override - public JsonSerializer modifySerializer( - SerializationConfig config, BeanDescription desc, JsonSerializer serializer) { - if (Schema.class.isAssignableFrom(desc.getBeanClass())) { - return new SchemaSerializer((JsonSerializer) serializer); - } else if (MediaType.class.isAssignableFrom(desc.getBeanClass())) { - return new MediaTypeSerializer((JsonSerializer) serializer); + if (!openapi31) { + // handle ref schema serialization skipping all other props + mapper.registerModule(new SimpleModule() { + @Override + public void setupModule(SetupContext context) { + super.setupModule(context); + context.addBeanSerializerModifier(new BeanSerializerModifier() { + @Override + public JsonSerializer modifySerializer( + SerializationConfig config, BeanDescription desc, JsonSerializer serializer) { + if (Schema.class.isAssignableFrom(desc.getBeanClass())) { + return new SchemaSerializer((JsonSerializer) serializer); + } else if (MediaType.class.isAssignableFrom(desc.getBeanClass())) { + return new MediaTypeSerializer((JsonSerializer) serializer); + } + return serializer; + } + }); + } + }); + } else { + mapper.registerModule(new SimpleModule() { + @Override + public void setupModule(SetupContext context) { + super.setupModule(context); + context.addBeanSerializerModifier(new BeanSerializerModifier() { + @Override + public JsonSerializer modifySerializer( + SerializationConfig config, BeanDescription desc, JsonSerializer serializer) { + if (Schema.class.isAssignableFrom(desc.getBeanClass())) { + return new Schema31Serializer((JsonSerializer) serializer); + } else if (MediaType.class.isAssignableFrom(desc.getBeanClass())) { + return new MediaTypeSerializer((JsonSerializer) serializer); + } + return serializer; } - return serializer; - } - }); - } - }); + }); + } + }); + } + + if (!openapi31) { + Module deserializerModule = new DeserializationModule(); + mapper.registerModule(deserializerModule); + } else { + Module deserializerModule = new DeserializationModule31(); + mapper.registerModule(deserializerModule); + } + mapper.registerModule(new JavaTimeModule()); + + Map, Class> sourceMixins = new LinkedHashMap<>(); + + sourceMixins.put(ApiResponses.class, ExtensionsMixin.class); + sourceMixins.put(Contact.class, ExtensionsMixin.class); + sourceMixins.put(Encoding.class, ExtensionsMixin.class); + sourceMixins.put(EncodingProperty.class, ExtensionsMixin.class); + sourceMixins.put(Example.class, ExampleMixin.class); + sourceMixins.put(ExternalDocumentation.class, ExtensionsMixin.class); + sourceMixins.put(Link.class, ExtensionsMixin.class); + sourceMixins.put(LinkParameter.class, ExtensionsMixin.class); + sourceMixins.put(MediaType.class, MediaTypeMixin.class); + sourceMixins.put(OAuthFlow.class, ExtensionsMixin.class); + sourceMixins.put(OAuthFlows.class, ExtensionsMixin.class); + sourceMixins.put(Operation.class, OperationMixin.class); + sourceMixins.put(PathItem.class, ExtensionsMixin.class); + sourceMixins.put(Paths.class, ExtensionsMixin.class); + sourceMixins.put(Scopes.class, ExtensionsMixin.class); + sourceMixins.put(Server.class, ExtensionsMixin.class); + sourceMixins.put(ServerVariable.class, ExtensionsMixin.class); + sourceMixins.put(ServerVariables.class, ExtensionsMixin.class); + sourceMixins.put(Tag.class, ExtensionsMixin.class); + sourceMixins.put(XML.class, ExtensionsMixin.class); + sourceMixins.put(ApiResponse.class, ExtensionsMixin.class); + sourceMixins.put(Parameter.class, ExtensionsMixin.class); + sourceMixins.put(RequestBody.class, ExtensionsMixin.class); + sourceMixins.put(Header.class, ExtensionsMixin.class); + sourceMixins.put(SecurityScheme.class, ExtensionsMixin.class); + sourceMixins.put(Callback.class, ExtensionsMixin.class); + + + if (!openapi31) { + sourceMixins.put(Schema.class, SchemaMixin.class); + sourceMixins.put(DateSchema.class, DateSchemaMixin.class); + sourceMixins.put(Components.class, ComponentsMixin.class); + sourceMixins.put(Info.class, InfoMixin.class); + sourceMixins.put(License.class, LicenseMixin.class); + sourceMixins.put(OpenAPI.class, OpenAPIMixin.class); + sourceMixins.put(Discriminator.class, DiscriminatorMixin.class); + } else { + sourceMixins.put(Info.class, ExtensionsMixin.class); + sourceMixins.put(Schema.class, Schema31Mixin.class); + sourceMixins.put(Components.class, Components31Mixin.class); + sourceMixins.put(OpenAPI.class, OpenAPI31Mixin.class); + sourceMixins.put(DateSchema.class, DateSchemaMixin.class); + sourceMixins.put(Discriminator.class, Discriminator31Mixin.class); + } + mapper.setMixIns(sourceMixins); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); + mapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + return mapper; + } + + protected static ObjectMapper createJsonConverter() { + + ObjectMapper mapper = new ObjectMapper(); + Module deserializerModule = new DeserializationModule(); mapper.registerModule(deserializerModule); @@ -135,9 +251,8 @@ public JsonSerializer modifySerializer( sourceMixins.put(ServerVariables.class, ExtensionsMixin.class); sourceMixins.put(Tag.class, ExtensionsMixin.class); sourceMixins.put(XML.class, ExtensionsMixin.class); - sourceMixins.put(Schema.class, SchemaMixin.class); - sourceMixins.put(DateSchema.class, DateSchemaMixin.class); + sourceMixins.put(Schema.class, SchemaConverterMixin.class); mapper.setMixIns(sourceMixins); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); @@ -150,6 +265,7 @@ public JsonSerializer modifySerializer( return mapper; } + public static ObjectMapper buildStrictGenericObjectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPISchema2JsonSchema.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPISchema2JsonSchema.java new file mode 100644 index 0000000000..1d7405d7ec --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/OpenAPISchema2JsonSchema.java @@ -0,0 +1,48 @@ +package io.swagger.v3.core.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.oas.models.SpecVersion; +import io.swagger.v3.oas.models.media.Schema; +import java.util.LinkedHashSet; +import java.util.Map; + +public class OpenAPISchema2JsonSchema { + + protected final ObjectMapper converterMapper = Json31.converterMapper(); + + public void process(Schema schema) { + schema.specVersion(SpecVersion.V31); + Map jsonSchema = converterMapper.convertValue(schema, Map.class); + + // handle nullable + if (schema.getType() != null || Boolean.TRUE.equals(schema.getNullable())) { + schema.types(new LinkedHashSet<>()); + } + if (schema.getType() != null) { + schema.getTypes().add(schema.getType()); + } + schema.type(null); + if (Boolean.TRUE.equals(schema.getNullable())) { + schema.nullable(null); + schema.getTypes().add("null"); + } + + // TODO handle other differences + + schema.jsonSchema(jsonSchema); + + // TODO handle all nested schemas + if (schema.getAdditionalProperties() instanceof Schema) { + process((Schema) schema.getAdditionalProperties()); + } + if (schema.getAllOf() != null) { + schema.getAllOf().forEach(this::process); + } + if (schema.getProperties() != null) { + schema.getProperties().values().forEach(this::process); + } + if (schema.getItems() != null) { + process(schema.getItems()); + } + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Parameter31Deserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Parameter31Deserializer.java new file mode 100644 index 0000000000..fc9d50a001 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Parameter31Deserializer.java @@ -0,0 +1,8 @@ +package io.swagger.v3.core.util; + +public class Parameter31Deserializer extends ParameterDeserializer { + + public Parameter31Deserializer() { + this.openapi31 = true; + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterDeserializer.java index 0b7a7dc9c6..14918e8858 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterDeserializer.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import io.swagger.v3.oas.models.parameters.CookieParameter; import io.swagger.v3.oas.models.parameters.HeaderParameter; @@ -15,6 +16,9 @@ import java.io.IOException; public class ParameterDeserializer extends JsonDeserializer { + + protected boolean openapi31; + @Override public Parameter deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { @@ -23,22 +27,34 @@ public Parameter deserialize(JsonParser jp, DeserializationContext ctxt) JsonNode node = jp.getCodec().readTree(jp); JsonNode sub = node.get("$ref"); JsonNode inNode = node.get("in"); + JsonNode desc = node.get("description"); if (sub != null) { result = new Parameter().$ref(sub.asText()); + if (desc != null && openapi31) { + result.description(desc.asText()); + } + } else if (inNode != null) { String in = inNode.asText(); ObjectReader reader = null; + ObjectMapper mapper = null; + if (openapi31) { + mapper = Json31.mapper(); + } else { + mapper = Json.mapper(); + } + if ("query".equals(in)) { - reader = Json.mapper().readerFor(QueryParameter.class); + reader = mapper.readerFor(QueryParameter.class); } else if ("header".equals(in)) { - reader = Json.mapper().readerFor(HeaderParameter.class); + reader = mapper.readerFor(HeaderParameter.class); } else if ("path".equals(in)) { - reader = Json.mapper().readerFor(PathParameter.class); + reader = mapper.readerFor(PathParameter.class); } else if ("cookie".equals(in)) { - reader = Json.mapper().readerFor(CookieParameter.class); + reader = mapper.readerFor(CookieParameter.class); } if (reader != null) { result = reader.with(DeserializationFeature.READ_ENUMS_USING_TO_STRING).readValue(node); diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Paths31Deserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Paths31Deserializer.java new file mode 100644 index 0000000000..5463b59e82 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Paths31Deserializer.java @@ -0,0 +1,8 @@ +package io.swagger.v3.core.util; + +public class Paths31Deserializer extends PathsDeserializer { + + public Paths31Deserializer() { + this.openapi31 = true; + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PathsDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PathsDeserializer.java index 7b8e70b3fd..f1738165e7 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PathsDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PathsDeserializer.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; @@ -14,9 +15,20 @@ import java.util.Map; public class PathsDeserializer extends JsonDeserializer { + + protected boolean openapi31; + @Override public Paths deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + + final ObjectMapper mapper; + if (openapi31) { + mapper = Json31.mapper(); + } else { + mapper = Json.mapper(); + } + Paths result = new Paths(); JsonNode node = jp.getCodec().readTree(jp); ObjectNode objectNode = (ObjectNode)node; @@ -26,9 +38,9 @@ public Paths deserialize(JsonParser jp, DeserializationContext ctxt) JsonNode child = objectNode.get(childName); // if name start with `x-` consider it an extesion if (childName.startsWith("x-")) { - extensions.put(childName, Json.mapper().convertValue(child, Object.class)); + extensions.put(childName, mapper.convertValue(child, Object.class)); } else { - result.put(childName, Json.mapper().convertValue(child, PathItem.class)); + result.put(childName, mapper.convertValue(child, PathItem.class)); } } if (!extensions.isEmpty()) { diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecurityScheme31Deserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecurityScheme31Deserializer.java new file mode 100644 index 0000000000..9a641f28e8 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecurityScheme31Deserializer.java @@ -0,0 +1,8 @@ +package io.swagger.v3.core.util; + +public class SecurityScheme31Deserializer extends SecuritySchemeDeserializer { + + public SecurityScheme31Deserializer() { + openapi31 = true; + } +} diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecuritySchemeDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecuritySchemeDeserializer.java index d773882adc..7921af0fce 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecuritySchemeDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/SecuritySchemeDeserializer.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.models.security.OAuthFlows; import io.swagger.v3.oas.models.security.SecurityScheme; @@ -12,9 +13,18 @@ import java.util.Arrays; public class SecuritySchemeDeserializer extends JsonDeserializer { + + protected boolean openapi31; + @Override public SecurityScheme deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + ObjectMapper mapper = null; + if (openapi31) { + mapper = Json31.mapper(); + } else { + mapper = Json.mapper(); + } SecurityScheme result = null; JsonNode node = jp.getCodec().readTree(jp); @@ -47,7 +57,10 @@ public SecurityScheme deserialize(JsonParser jp, DeserializationContext ctxt) } else if ("oauth2".equals(type)) { result .type(SecurityScheme.Type.OAUTH2) - .flows(Json.mapper().convertValue(node.get("flows"), OAuthFlows.class)); + .flows(mapper.convertValue(node.get("flows"), OAuthFlows.class)); + } else if ("mutualTLS".equals(type)) { + result + .type(SecurityScheme.Type.MUTUALTLS); } } diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Yaml31.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Yaml31.java new file mode 100644 index 0000000000..ba16433896 --- /dev/null +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Yaml31.java @@ -0,0 +1,62 @@ +package io.swagger.v3.core.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import io.swagger.v3.oas.models.media.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class Yaml31 { + static ObjectMapper mapper; + + static Logger LOGGER = LoggerFactory.getLogger(Yaml31.class); + + public static ObjectMapper mapper() { + if (mapper == null) { + mapper = ObjectMapperFactory.createYaml31(); + } + return mapper; + } + + public static ObjectWriter pretty() { + return mapper().writer(new DefaultPrettyPrinter()); + } + + public static String pretty(Object o) { + try { + return pretty().writeValueAsString(o); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static void prettyPrint(Object o) { + try { + System.out.println(pretty().writeValueAsString(o)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static Map jsonSchemaAsMap(String jsonSchema) { + try { + return mapper().readValue(jsonSchema, Map.class); + } catch (JsonProcessingException e) { + LOGGER.error("Exception converting jsonSchema to Map", e); + return null; + } + } + + public static Map jsonSchemaAsMap(Schema schema) { + try { + return mapper().readValue(mapper().writeValueAsString(schema), Map.class); + } catch (JsonProcessingException e) { + LOGGER.error("Exception converting jsonSchema to Map", e); + return null; + } + }} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/SwaggerSerializerTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/SwaggerSerializerTest.java index 40045ddc71..0a73bcfd03 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/SwaggerSerializerTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/converting/SwaggerSerializerTest.java @@ -102,10 +102,10 @@ public void convertSpec() throws IOException { final ApiResponse errorResponse = new ApiResponse() .description("error response") - .link("myLink", new Link() + .addLink("myLink", new Link() .description("a link") .operationId("theLinkedOperationId") - .parameters("userId", "gah") + .addParameter("userId", "gah") ) .content(new Content() .addMediaType("application/json", new MediaType() diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/OpenAPI3_1DeserializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/OpenAPI3_1DeserializationTest.java new file mode 100644 index 0000000000..9a0a299f40 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/OpenAPI3_1DeserializationTest.java @@ -0,0 +1,225 @@ +package io.swagger.v3.core.deserialization; + +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.core.util.ResourceUtils; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Yaml31; +import io.swagger.v3.oas.models.OpenAPI; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; + +public class OpenAPI3_1DeserializationTest { + + @Test + public void deserializePetstore3_1() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1.yaml"); + final OpenAPI swagger = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(swagger); + assertEquals(swagger.getInfo().getLicense().getIdentifier(), "test"); + } + + @Test + public void deserializePetstore3_1More() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1_more.yaml"); + final OpenAPI swagger = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(swagger); + assertEquals(swagger.getInfo().getLicense().getIdentifier(), "test"); + } + + + @Test + public void deserializePetstore3_0() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/petstore-3.0.yaml"); + final OpenAPI swagger = Yaml.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(swagger); + assertEquals(swagger.getInfo().getLicense().getIdentifier(), null); + } + + @Test + public void deserializeChangelog3_1() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/changelog-3.1.yaml"); + final OpenAPI swagger = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(swagger); + assertEquals(swagger.getInfo().getLicense().getIdentifier(), "test"); + Yaml31.prettyPrint(swagger); + SerializationMatchers.assertEqualsToYaml31(swagger, jsonString); + } + + @Test + public void testDeserializationOnOAS31() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1_sample.yaml"); + OpenAPI openAPI = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(openAPI); + + assertEquals(openAPI.getInfo().getTitle(), "Swagger Petstore"); + assertEquals(openAPI.getInfo().getVersion(), "1.0.0"); + assertEquals(openAPI.getInfo().getSummary(), "petstore sample for OAS 3.1.0"); + assertEquals(openAPI.getInfo().getLicense().getName(), "MIT"); + assertEquals(openAPI.getInfo().getLicense().getIdentifier(), "test"); + + assertNotNull(openAPI.getWebhooks()); + assertFalse(openAPI.getWebhooks().isEmpty()); + assertNotNull(openAPI.getWebhooks().get("newPet")); + assertNotNull(openAPI.getWebhooks().get("newPet").getPost()); + + assertNotNull(openAPI.getComponents().getPathItems()); + assertNotNull(openAPI.getComponents().getPathItems().get("/pet")); + assertEquals(openAPI.getComponents().getPathItems().get("/pet").getDescription(), "get a pet"); + assertNotNull(openAPI.getComponents().getPathItems().get("/pet").getGet()); + assertEquals(openAPI.getComponents().getPathItems().get("/pet").getGet().getOperationId(), "getPet"); + + assertNotNull(openAPI.getComponents().getSchemas()); + assertNotNull(openAPI.getComponents().getSchemas().get("Pet")); + assertNotNull(openAPI.getComponents().getSchemas().get("Pet").getDiscriminator()); + assertNotNull(openAPI.getComponents().getSchemas().get("Pet").getDiscriminator().getExtensions()); + assertEquals(openAPI.getComponents().getSchemas().get("Pet").getDiscriminator().getExtensions().get("x-test-extension"), "extended"); + + assertNotNull(openAPI.getComponents().getResponses()); + assertNotNull(openAPI.getComponents().getResponses().get("201")); + assertEquals(openAPI.getComponents().getResponses().get("201").getDescription(), "api response description"); + + assertNotNull(openAPI.getComponents().getParameters()); + assertNotNull(openAPI.getComponents().getParameters().get("param")); + assertEquals(openAPI.getComponents().getParameters().get("param").getIn(), "query"); + assertEquals(openAPI.getComponents().getParameters().get("param").getName(), "param0"); + assertEquals(openAPI.getComponents().getParameters().get("param").getDescription(), "parameter description"); + + assertNotNull(openAPI.getComponents().getExamples()); + assertNotNull(openAPI.getComponents().getExamples().get("example")); + assertEquals(openAPI.getComponents().getExamples().get("example").getDescription(), "example description"); + assertEquals(openAPI.getComponents().getExamples().get("example").getSummary(), "example summary"); + assertEquals(openAPI.getComponents().getExamples().get("example").getValue(), "This is an example"); + + assertNotNull(openAPI.getComponents().getRequestBodies()); + assertNotNull(openAPI.getComponents().getRequestBodies().get("body")); + + assertNotNull(openAPI.getComponents().getHeaders()); + assertNotNull(openAPI.getComponents().getHeaders().get("test-head")); + assertEquals(openAPI.getComponents().getHeaders().get("test-head").getDescription(), "test header description"); + + assertNotNull(openAPI.getComponents().getSecuritySchemes()); + assertNotNull(openAPI.getComponents().getSecuritySchemes().get("basic")); + assertEquals(openAPI.getComponents().getSecuritySchemes().get("basic").getDescription(), "security description"); + assertEquals(openAPI.getComponents().getSecuritySchemes().get("basic").getType().toString(), "http"); + + assertNotNull(openAPI.getComponents().getLinks()); + assertNotNull(openAPI.getComponents().getLinks().get("Link")); + assertEquals(openAPI.getComponents().getLinks().get("Link").getOperationRef(), "#/paths/~12.0~1repositories~1{username}/get"); + + assertNotNull(openAPI.getComponents().getCallbacks()); + assertNotNull(openAPI.getComponents().getCallbacks().get("TestCallback")); + + openAPI = Yaml.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(openAPI); + + assertNull(openAPI.getInfo().getSummary()); + assertNull(openAPI.getWebhooks()); + assertNull(openAPI.getComponents().getPathItems()); + assertNull(openAPI.getComponents().getSchemas().get("Pet").getDiscriminator().getExtensions()); + + } + + @Test + public void testDeserializationOnOAS30() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1_sample.yaml"); + OpenAPI openAPI = Yaml.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(openAPI); + + assertEquals(openAPI.getInfo().getTitle(), "Swagger Petstore"); + assertEquals(openAPI.getInfo().getVersion(), "1.0.0"); + assertNull(openAPI.getInfo().getSummary()); + assertEquals(openAPI.getInfo().getLicense().getName(), "MIT"); + assertNull(openAPI.getInfo().getLicense().getIdentifier()); + + assertNull(openAPI.getWebhooks()); + + assertNull(openAPI.getComponents().getPathItems()); + + assertNotNull(openAPI.getComponents().getSchemas()); + assertNotNull(openAPI.getComponents().getSchemas().get("Pet")); + assertNotNull(openAPI.getComponents().getSchemas().get("Pet").getDiscriminator()); + assertNull(openAPI.getComponents().getSchemas().get("Pet").getDiscriminator().getExtensions()); + + assertNotNull(openAPI.getComponents().getResponses()); + assertNotNull(openAPI.getComponents().getResponses().get("201")); + assertEquals(openAPI.getComponents().getResponses().get("201").getDescription(), "api response description"); + + assertNotNull(openAPI.getComponents().getParameters()); + assertNotNull(openAPI.getComponents().getParameters().get("param")); + assertEquals(openAPI.getComponents().getParameters().get("param").getIn(), "query"); + assertEquals(openAPI.getComponents().getParameters().get("param").getName(), "param0"); + assertEquals(openAPI.getComponents().getParameters().get("param").getDescription(), "parameter description"); + + assertNotNull(openAPI.getComponents().getExamples()); + assertNotNull(openAPI.getComponents().getExamples().get("example")); + assertEquals(openAPI.getComponents().getExamples().get("example").getDescription(), "example description"); + assertEquals(openAPI.getComponents().getExamples().get("example").getSummary(), "example summary"); + assertEquals(openAPI.getComponents().getExamples().get("example").getValue(), "This is an example"); + + assertNotNull(openAPI.getComponents().getRequestBodies()); + assertNotNull(openAPI.getComponents().getRequestBodies().get("body")); + + assertNotNull(openAPI.getComponents().getHeaders()); + assertNotNull(openAPI.getComponents().getHeaders().get("test-head")); + assertEquals(openAPI.getComponents().getHeaders().get("test-head").getDescription(), "test header description"); + + assertNotNull(openAPI.getComponents().getSecuritySchemes()); + assertNotNull(openAPI.getComponents().getSecuritySchemes().get("basic")); + assertEquals(openAPI.getComponents().getSecuritySchemes().get("basic").getDescription(), "security description"); + assertEquals(openAPI.getComponents().getSecuritySchemes().get("basic").getType().toString(), "http"); + + assertNotNull(openAPI.getComponents().getLinks()); + assertNotNull(openAPI.getComponents().getLinks().get("Link")); + assertEquals(openAPI.getComponents().getLinks().get("Link").getOperationRef(), "#/paths/~12.0~1repositories~1{username}/get"); + + assertNotNull(openAPI.getComponents().getCallbacks()); + assertNotNull(openAPI.getComponents().getCallbacks().get("TestCallback")); + + openAPI = Yaml.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(openAPI); + } + + @Test + public void testRefDeserializationOnOAS31() throws IOException { + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1_refs_siblings.yaml"); + OpenAPI openAPI = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + + + assertEquals(openAPI.getPaths().get("/ref_pet").get$ref(), "#/components/pathItems/pet"); + assertEquals(openAPI.getPaths().get("/ref_pet").getDescription(), "ref pathItem description"); + assertEquals(openAPI.getPaths().get("/ref_pet").getSummary(), "ref pathItem summary"); + + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(0).get$ref(), "#/components/parameters/testParameter"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(0).getDescription(), "ref parameter description"); + + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(1).getName(), "randomParam"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(1).getIn(), "query"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(1).getExamples().get("refExample").get$ref(), "#/components/examples/testExample"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(1).getExamples().get("refExample").getDescription(), "ref example description"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getParameters().get(1).getExamples().get("refExample").getSummary(), "ref example summary"); + + assertEquals(openAPI.getPaths().get("/pets").getPost().getCallbacks().get("callIt").get$ref(), "#/components/callbacks/TestCallback"); + + assertEquals(openAPI.getPaths().get("/pets").getPost().getRequestBody().get$ref(), "#/components/requestBodies/body"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getRequestBody().getDescription(), "ref request body description"); + + assertEquals(openAPI.getPaths().get("/pets").getPost().getResponses().get("201").get$ref(), "#/components/responses/okResponse"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getResponses().get("201").getDescription(), "ref response description"); + + assertEquals(openAPI.getPaths().get("/pets").getPost().getResponses().get("default").getHeaders().get("head").get$ref(), "#/components/headers/head"); + assertEquals(openAPI.getPaths().get("/pets").getPost().getResponses().get("default").getHeaders().get("head").getDescription(), "ref header description"); + + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/SchemaDeserializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/SchemaDeserializationTest.java new file mode 100644 index 0000000000..6f506b3cf5 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/deserialization/SchemaDeserializationTest.java @@ -0,0 +1,31 @@ +package io.swagger.v3.core.deserialization; + +import io.swagger.v3.core.util.ResourceUtils; +import io.swagger.v3.core.util.Yaml31; +import io.swagger.v3.oas.models.OpenAPI; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +public class SchemaDeserializationTest { + + @Test + public void deserializeRefSchema3_1() throws IOException { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1_refs_siblings.yaml"); + final OpenAPI openAPI = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(openAPI); + assertNotNull(openAPI.getComponents()); + assertNotNull(openAPI.getComponents().getSchemas()); + assertNotNull(openAPI.getComponents().getSchemas().get("AnotherPet")); + assertEquals(openAPI.getComponents().getSchemas().get("AnotherPet").get$ref(), "#/components/schemas/Pet"); + assertEquals(openAPI.getComponents().getSchemas().get("AnotherPet").getTitle(), "Another Pet"); + assertEquals(openAPI.getComponents().getSchemas().get("AnotherPet").getDescription(), "Another Pet for petstore referencing Pet schema"); + assertNotNull(openAPI.getComponents().getSchemas().get("AnotherPet").getProperties()); + assertNotNull(openAPI.getComponents().getSchemas().get("AnotherPet").getProperties().get("category")); + assertNotNull(openAPI.getComponents().getSchemas().get("AnotherPet").getProperties().get("photoUrl")); + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/matchers/SerializationMatchers.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/matchers/SerializationMatchers.java index 1ccb2f27b9..75771ba588 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/matchers/SerializationMatchers.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/matchers/SerializationMatchers.java @@ -1,11 +1,14 @@ package io.swagger.v3.core.matchers; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.NumericNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Json31; import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Yaml31; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +28,14 @@ public static void assertEqualsToJson(Object objectToSerialize, String jsonStr) apply(objectToSerialize, jsonStr, Json.mapper()); } + public static void assertEqualsToYaml31(Object objectToSerialize, String yamlStr) { + apply31(objectToSerialize, yamlStr, Yaml31.mapper()); + } + + public static void assertEqualsToJson31(Object objectToSerialize, String jsonStr) { + apply31(objectToSerialize, jsonStr, Json31.mapper()); + } + private static void apply(Object objectToSerialize, String str, ObjectMapper mapper) { final ObjectNode lhs = mapper.convertValue(objectToSerialize, ObjectNode.class); ObjectNode rhs = null; @@ -39,6 +50,20 @@ private static void apply(Object objectToSerialize, String str, ObjectMapper map } } + private static void apply31(Object objectToSerialize, String str, ObjectMapper mapper) { + final ObjectNode lhs = mapper.convertValue(objectToSerialize, ObjectNode.class); + ObjectNode rhs = null; + try { + rhs = mapper.readValue(str, ObjectNode.class); + } catch (IOException e) { + LOGGER.error("Failed to read value", e); + } + if (!lhs.equals(new ObjectNodeComparator(), rhs)) { + assertEquals(Yaml31.pretty(lhs), Yaml31.pretty(rhs)); + //fail(String.format("Serialized object:\n%s\ndoes not equal to expected serialized string:\n%s", lhs, rhs)); + } + } + static final class ObjectNodeComparator implements Comparator { @Override public int compare(JsonNode o1, JsonNode o2) { @@ -57,4 +82,4 @@ public int compare(JsonNode o1, JsonNode o2) { return comp; } } -} \ No newline at end of file +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/PatternAndSchemaPropertiesTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/PatternAndSchemaPropertiesTest.java new file mode 100644 index 0000000000..09a7195a94 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/PatternAndSchemaPropertiesTest.java @@ -0,0 +1,184 @@ +package io.swagger.v3.core.resolving.v31; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverterContextImpl; +import io.swagger.v3.core.jackson.ModelResolver; +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.core.resolving.SwaggerTestBase; +import io.swagger.v3.core.resolving.v31.model.AnnotatedPet; +import io.swagger.v3.core.resolving.v31.model.AnnotatedPetSinglePatternProperty; +import io.swagger.v3.core.util.OpenAPISchema2JsonSchema; +import io.swagger.v3.oas.models.media.Schema; +import org.testng.annotations.Test; + + +import static org.testng.Assert.assertEquals; + +public class PatternAndSchemaPropertiesTest extends SwaggerTestBase { + + @Test + public void testPatternAndSchemaProperties() throws Exception { + + final ModelResolver modelResolver = new ModelResolver(mapper()); + + ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + + Schema model = context + .resolve(new AnnotatedType(AnnotatedPet.class)); + + assertEquals(((Schema)model.getPatternProperties().get("what.*ever")).getFormat(), "int32"); + assertEquals(((Schema)model.getPatternProperties().get("it.*takes")).get$ref(), "#/components/schemas/Category"); + + assertEquals(((Schema)model.getProperties().get("anotherCategory")).get$ref(), "#/components/schemas/Category"); + assertEquals(((Schema)model.getProperties().get("anotherInteger")).getFormat(), "int32"); + + SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "AnnotatedPet:\n" + + " type: object\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " category:\n" + + " $ref: '#/components/schemas/Category'\n" + + " name:\n" + + " type: string\n" + + " photoUrls:\n" + + " type: array\n" + + " xml:\n" + + " wrapped: true\n" + + " items:\n" + + " type: string\n" + + " xml:\n" + + " name: photoUrl\n" + + " tags:\n" + + " type: array\n" + + " xml:\n" + + " wrapped: true\n" + + " items:\n" + + " $ref: '#/components/schemas/Tag'\n" + + " status:\n" + + " type: string\n" + + " description: pet status in the store\n" + + " enum:\n" + + " - available\n" + + " - pending\n" + + " - sold\n" + + " anotherCategory:\n" + + " $ref: '#/components/schemas/Category'\n" + + " anotherInteger:\n" + + " maximum: 10\n" + + " type: integer\n" + + " description: prop schema 1\n" + + " format: int32\n" + + " description: Annotated Pet\n" + + " nullable: true\n" + + "Category:\n" + + " type: object\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " name:\n" + + " type: string\n" + + " description: prop schema 2\n" + + " xml:\n" + + " name: Category\n" + + "Tag:\n" + + " type: object\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " name:\n" + + " type: string\n" + + " xml:\n" + + " name: Tag"); + context.getDefinedModels().values().forEach(s -> new OpenAPISchema2JsonSchema().process(s)); + + SerializationMatchers.assertEqualsToYaml31(context.getDefinedModels(), "AnnotatedPet:\n" + + " type:\n" + + " - object\n" + + " - \"null\"\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " category:\n" + + " $ref: '#/components/schemas/Category'\n" + + " name:\n" + + " type: string\n" + + " photoUrls:\n" + + " type: array\n" + + " xml:\n" + + " wrapped: true\n" + + " items:\n" + + " type: string\n" + + " xml:\n" + + " name: photoUrl\n" + + " tags:\n" + + " type: array\n" + + " xml:\n" + + " wrapped: true\n" + + " items:\n" + + " $ref: '#/components/schemas/Tag'\n" + + " status:\n" + + " type: string\n" + + " description: pet status in the store\n" + + " enum:\n" + + " - available\n" + + " - pending\n" + + " - sold\n" + + " anotherCategory:\n" + + " $ref: '#/components/schemas/Category'\n" + + " anotherInteger:\n" + + " maximum: 10\n" + + " type: integer\n" + + " description: prop schema 1\n" + + " format: int32\n" + + " patternProperties:\n" + + " what.*ever:\n" + + " maximum: 10\n" + + " type: integer\n" + + " description: prop schema 1\n" + + " format: int32\n" + + " it.*takes:\n" + + " $ref: '#/components/schemas/Category'\n" + + " description: Annotated Pet\n" + + "Category:\n" + + " type: object\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " name:\n" + + " type: string\n" + + " description: prop schema 2\n" + + " xml:\n" + + " name: Category\n" + + "Tag:\n" + + " type: object\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " name:\n" + + " type: string\n" + + " xml:\n" + + " name: Tag\n"); + } + + @Test + public void testSinglePatternAndSchemaProperties() throws Exception { + + final ModelResolver modelResolver = new ModelResolver(mapper()); + + ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + + Schema model = context + .resolve(new AnnotatedType(AnnotatedPetSinglePatternProperty.class)); + + assertEquals(((Schema)model.getPatternProperties().get("what.*ever")).getFormat(), "int32"); + assertEquals(((Schema)model.getProperties().get("anotherCategory")).get$ref(), "#/components/schemas/Category"); + } + +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPet.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPet.java new file mode 100644 index 0000000000..ac6f4e7e8f --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPet.java @@ -0,0 +1,109 @@ +package io.swagger.v3.core.resolving.v31.model; + +import io.swagger.v3.oas.annotations.media.PatternProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import java.util.ArrayList; +import java.util.List; + +@Schema(description = "Annotated Pet", nullable = true) +@PatternProperty( + regex = "what.*ever", + schema = @Schema( + type = "integer", + description = "prop schema 1", + format = "int32", + maximum = "10" + ) +) +@PatternProperty( + regex = "it.*takes", + schema = @Schema( + implementation = Category.class, + description = "prop schema 2" + ) +) +@SchemaProperty( + name = "anotherCategory", + schema = @Schema( + implementation = Category.class, + description = "prop schema 2" + ) +) +@SchemaProperty( + name = "anotherInteger", + schema = @Schema( + type = "integer", + description = "prop schema 1", + format = "int32", + maximum = "10" + ) +) +public class AnnotatedPet { + private long id; + private Category category; + private String name; + private List photoUrls = new ArrayList(); + private List tags = new ArrayList(); + private String status; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "category") + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + @XmlElement(name = "name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @XmlElementWrapper(name = "photoUrls") + @XmlElement(name = "photoUrl") + public List getPhotoUrls() { + return photoUrls; + } + + public void setPhotoUrls(List photoUrls) { + this.photoUrls = photoUrls; + } + + @XmlElementWrapper(name = "tags") + @XmlElement(name = "tag") + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + @XmlElement(name = "status") + @Schema(description = "pet status in the store", allowableValues = {"available", "pending", "sold"}) + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPetSinglePatternProperty.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPetSinglePatternProperty.java new file mode 100644 index 0000000000..8e47b5586c --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/AnnotatedPetSinglePatternProperty.java @@ -0,0 +1,93 @@ +package io.swagger.v3.core.resolving.v31.model; + +import io.swagger.v3.oas.annotations.media.PatternProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import java.util.ArrayList; +import java.util.List; + +@Schema(description = "Annotated Pet", nullable = true) +@PatternProperty( + regex = "what.*ever", + schema = @Schema( + type = "integer", + description = "prop schema 1", + format = "int32", + maximum = "10" + ) +) +@SchemaProperty( + name = "anotherCategory", + schema = @Schema( + implementation = Category.class, + description = "prop schema 2" + ) +) +public class AnnotatedPetSinglePatternProperty { + private long id; + private Category category; + private String name; + private List photoUrls = new ArrayList(); + private List tags = new ArrayList(); + private String status; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "category") + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + @XmlElement(name = "name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @XmlElementWrapper(name = "photoUrls") + @XmlElement(name = "photoUrl") + public List getPhotoUrls() { + return photoUrls; + } + + public void setPhotoUrls(List photoUrls) { + this.photoUrls = photoUrls; + } + + @XmlElementWrapper(name = "tags") + @XmlElement(name = "tag") + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + @XmlElement(name = "status") + @Schema(description = "pet status in the store", allowableValues = {"available", "pending", "sold"}) + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Category.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Category.java new file mode 100644 index 0000000000..df3c2a5ac0 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Category.java @@ -0,0 +1,51 @@ +/** + * Copyright 2016 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.core.resolving.v31.model; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "Category") +public class Category { + private long id; + private String name; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Category() { + } + + public Category(String name) { + this.name = name; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/CustomGenerator.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/CustomGenerator.java new file mode 100644 index 0000000000..240cc539de --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/CustomGenerator.java @@ -0,0 +1,32 @@ +package io.swagger.v3.core.resolving.v31.model; + +import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +public class CustomGenerator extends ObjectIdGenerators.PropertyGenerator { + private static final long serialVersionUID = 1L; + + protected CustomGenerator(Class scope) { + super(scope); + } + + @Override + public ObjectIdGenerator forScope(Class scope) { + return null; + } + + @Override + public ObjectIdGenerator newForSerialization(Object context) { + return null; + } + + @Override + public IdKey key(Object key) { + return null; + } + + @Override + public Object generateId(Object forPojo) { + return null; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ExtensionUser.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ExtensionUser.java new file mode 100644 index 0000000000..24647d7e82 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ExtensionUser.java @@ -0,0 +1,128 @@ +/** + * Copyright 2016 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.core.resolving.v31.model; + +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "User") +@Schema( + description = "User", + extensions = { + @Extension(name = "x-user", properties = { + @ExtensionProperty(name = "name", value = "Josh")}), + @Extension(name = "user-extensions", properties = { + @ExtensionProperty(name = "lastName", value = "Hart"), + @ExtensionProperty(name = "address", value = "House")}) + } +) +public class ExtensionUser { + private long id; + private String username; + private String firstName; + private String lastName; + private String email; + private String password; + private String phone; + private int userStatus; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "firstName") + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + @XmlElement(name = "username") + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @XmlElement(name = "lastName") + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @XmlElement(name = "email") + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @XmlElement(name = "password") + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @XmlElement(name = "phone") + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @XmlElement(name = "userStatus") + @Schema( + description = "User Status", + extensions = { + @Extension(name = "x-userStatus", properties = { + @ExtensionProperty(name = "name", value = "Josh")}), + @Extension(name = "userStatus-extensions", properties = { + @ExtensionProperty(name = "lastName", value = "Hart"), + @ExtensionProperty(name = "address", value = "House")}) + } + ) + public int getUserStatus() { + return userStatus; + } + + public void setUserStatus(int userStatus) { + this.userStatus = userStatus; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/JacksonBean.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/JacksonBean.java new file mode 100644 index 0000000000..fe8b341124 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/JacksonBean.java @@ -0,0 +1,76 @@ +package io.swagger.v3.core.resolving.v31.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.annotation.JsonValue; + +public class JacksonBean { + + private String id; + private String ignored; + private StringValueBean bean; + private NotFoundModel model; + private NotFoundModel model2; + + @JsonIgnore + public String getIgnored() { + return ignored; + } + + public void setIgnored(String ignored) { + this.ignored = ignored; + } + + public void setId(String id) { + this.id = id; + } + + public void setModel(NotFoundModel model) { + this.model = model; + } + + public StringValueBean getBean() { + return bean; + } + + public void setBean(StringValueBean bean) { + this.bean = bean; + } + + @JsonProperty("identity") + public String getId() { + return id; + } + + @JsonUnwrapped + public NotFoundModel getModel() { + return model; + } + + @JsonUnwrapped(prefix = "pre", suffix = "suf") + public NotFoundModel getModel2() { + return model2; + } + + public void setModel2(NotFoundModel model2) { + this.model2 = model2; + } + + public static class StringValueBean { + + private final String value; + + @JsonCreator + public StringValueBean(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + } + +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ListOfStringsBeanParam.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ListOfStringsBeanParam.java new file mode 100644 index 0000000000..a5a8eb5fc5 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ListOfStringsBeanParam.java @@ -0,0 +1,17 @@ +package io.swagger.v3.core.resolving.v31.model; + +import javax.ws.rs.QueryParam; +import java.util.List; + +public class ListOfStringsBeanParam { + @QueryParam(value = "listOfStrings") + private List list; + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentity.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentity.java new file mode 100644 index 0000000000..ff00051313 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentity.java @@ -0,0 +1,145 @@ +package io.swagger.v3.core.resolving.v31.model; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonIdentityReference; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +public class ModelWithJsonIdentity { + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name") + @JsonProperty("PropertyGeneratorAsId") + public SourceDefinition1 testPropertyGeneratorAsId; + + @JsonIdentityReference(alwaysAsId = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name") + @JsonProperty("PropertyGeneratorAsProperty") + public SourceDefinition1 testPropertyGeneratorAsProperty; + + public class SourceDefinition1 { + public String driver; + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "driverId") + @JsonProperty("ChangedPropertyName") + public SourceDefinition2 testChangedPropertyName; + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "driverId") + @JsonProperty("ChangedPropertyName2") + public SourceDefinition2 testChangedPropertyName2; + + static public class SourceDefinition2 { + @JsonProperty("driverId") + public String driver; + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class) + @JsonProperty("SourceWithoutPropertyAsId") + public SourceDefinition3 testWithoutPropertyAsId; + + @JsonIdentityReference(alwaysAsId = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class) + @JsonProperty("SourceWithoutPropertyAsProperty") + public SourceDefinition3 testWithoutPropertyAsProperty; + + public class SourceDefinition3 { + @JsonProperty("driverId") + public String driver; + public String name; + + @JsonProperty("@id") + public String id; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "testName1") + @JsonProperty("IntSequenceGeneratorAsId") + public SourceDefinition4 testIntSequenceGeneratorAsId; + + @JsonIdentityReference(alwaysAsId = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "testName2") + @JsonProperty("IntSequenceGeneratorAsProperty") + public SourceDefinition4 testIntSequenceGeneratorAsProperty; + + public class SourceDefinition4 { + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) + @JsonProperty("IntSequenceWithoutPropertyAsId") + public SourceDefinition5 testIntSequenceWithoutPropertyAsId; + + @JsonIdentityReference(alwaysAsId = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) + @JsonProperty("IntSequenceWithoutPropertyAsProperty") + public SourceDefinition5 testIntSequenceWithoutPropertyAsProperty; + + public class SourceDefinition5 { + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class, property = "UUID1") + @JsonProperty("UUIDGeneratorAsId") + public SourceDefinition6 testUUIDGeneratorAsId; + + @JsonIdentityReference(alwaysAsId = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class, property = "UUID2") + @JsonProperty("UUIDGeneratorAsProperty") + public SourceDefinition6 testUUIDGeneratorAsProperty; + + public class SourceDefinition6 { + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class) + @JsonProperty("UUIDGeneratorWithoutPropertyAsId") + public SourceDefinition7 testUUIDGeneratorWithoutPropertyAsId; + + @JsonIdentityReference(alwaysAsId = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class) + @JsonProperty("UUIDGeneratorWithoutPropertyAsProperty") + public SourceDefinition7 testUUIDGeneratorWithoutPropertyAsProperty; + + public class SourceDefinition7 { + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = ObjectIdGenerators.None.class, property = "testGeneratorsNone") + @JsonProperty("GeneratorsNone") + public SourceDefinition8 testGeneratorsNone; + + public class SourceDefinition8 { + @JsonProperty("driverId") + public String driver; + public String name; + } + + @JsonIdentityReference(alwaysAsId = true) + @JsonIdentityInfo(generator = CustomGenerator.class, property = "name") + @JsonProperty("CustomGenerator") + public SourceDefinition9 testCustomGenerator; + + public class SourceDefinition9 { + public String driver; + public String name; + } + + @JsonIdentityInfo(generator = CustomGenerator.class, property = "name") + @JsonProperty("WithoutJsonIdentityReference") + public SourceDefinition10 testWithoutJsonIdentityReference; + + public class SourceDefinition10 { + public String driver; + public String name; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentityCyclic.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentityCyclic.java new file mode 100644 index 0000000000..381c2716f7 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/ModelWithJsonIdentityCyclic.java @@ -0,0 +1,29 @@ +package io.swagger.v3.core.resolving.v31.model; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonIdentityReference; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +import java.util.List; + +public class ModelWithJsonIdentityCyclic { + + public Long id; + + public List sourceDefinitions; + + @JsonIdentityInfo( + generator = ObjectIdGenerators.PropertyGenerator.class, + property = "name") + public static class SourceDefinition { + public String driver; + public String name; + + @JsonIdentityReference(alwaysAsId=true) + @JsonIdentityInfo( + generator = ObjectIdGenerators.PropertyGenerator.class, + property = "id") + public ModelWithJsonIdentityCyclic model; + } + +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleBaseBean.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleBaseBean.java new file mode 100644 index 0000000000..8d79b293e5 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleBaseBean.java @@ -0,0 +1,11 @@ +package io.swagger.v3.core.resolving.v31.model; + +@io.swagger.v3.oas.annotations.media.Schema( + description = "MultipleBaseBean", + subTypes = { MultipleSub1Bean.class, MultipleSub2Bean.class } +) +public class MultipleBaseBean { + public String beanType; + public int a; + public String b; +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub1Bean.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub1Bean.java new file mode 100644 index 0000000000..7637d22609 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub1Bean.java @@ -0,0 +1,8 @@ +package io.swagger.v3.core.resolving.v31.model; + +@io.swagger.v3.oas.annotations.media.Schema( + description = "MultipleSub1Bean" +) +public class MultipleSub1Bean extends MultipleBaseBean { + public int c; +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub2Bean.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub2Bean.java new file mode 100644 index 0000000000..044320bde9 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/MultipleSub2Bean.java @@ -0,0 +1,8 @@ +package io.swagger.v3.core.resolving.v31.model; + +@io.swagger.v3.oas.annotations.media.Schema( + description = "MultipleSub2Bean" +) +public class MultipleSub2Bean extends MultipleBaseBean { + public int d; +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/NotFoundModel.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/NotFoundModel.java new file mode 100644 index 0000000000..27619e95f4 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/NotFoundModel.java @@ -0,0 +1,30 @@ +package io.swagger.v3.core.resolving.v31.model; + +public class NotFoundModel { + int code; + String message; + + public NotFoundModel() { + } + + public NotFoundModel(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Pet.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Pet.java new file mode 100644 index 0000000000..1554a00ddb --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Pet.java @@ -0,0 +1,76 @@ +package io.swagger.v3.core.resolving.v31.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; + +@XmlRootElement(name = "Pet") +public class Pet { + private long id; + private Category category; + private String name; + private List photoUrls = new ArrayList(); + private List tags = new ArrayList(); + private String status; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "category") + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + @XmlElement(name = "name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @XmlElementWrapper(name = "photoUrls") + @XmlElement(name = "photoUrl") + public List getPhotoUrls() { + return photoUrls; + } + + public void setPhotoUrls(List photoUrls) { + this.photoUrls = photoUrls; + } + + @XmlElementWrapper(name = "tags") + @XmlElement(name = "tag") + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + @XmlElement(name = "status") + @Schema(description = "pet status in the store", allowableValues = {"available", "pending", "sold"}) + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Tag.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Tag.java new file mode 100644 index 0000000000..1d24a79c67 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/Tag.java @@ -0,0 +1,44 @@ +/** + * Copyright 2016 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.core.resolving.v31.model; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "Tag") +public class Tag { + private long id; + private String name; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/User.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/User.java new file mode 100644 index 0000000000..27ce93f0d6 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/v31/model/User.java @@ -0,0 +1,107 @@ +/** + * Copyright 2016 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.core.resolving.v31.model; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "User") +public class User { + private long id; + private String username; + private String firstName; + private String lastName; + private String email; + private String password; + private String phone; + private int userStatus; + + @XmlElement(name = "id") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @XmlElement(name = "firstName") + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + @XmlElement(name = "username") + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @XmlElement(name = "lastName") + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @XmlElement(name = "email") + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @XmlElement(name = "password") + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @XmlElement(name = "phone") + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @XmlElement(name = "userStatus") + @Schema(description = "User Status") //, allowableValues = {"1","2","3"}) + public int getUserStatus() { + return userStatus; + } + + public void setUserStatus(int userStatus) { + this.userStatus = userStatus; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java new file mode 100644 index 0000000000..ddd08549a3 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/OpenAPI3_1SerializationTest.java @@ -0,0 +1,1361 @@ +package io.swagger.v3.core.serialization; + +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.core.util.ResourceUtils; +import io.swagger.v3.core.util.Yaml31; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.callbacks.Callback; +import io.swagger.v3.oas.models.examples.Example; +import io.swagger.v3.oas.models.headers.Header; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.links.Link; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.Discriminator; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +public class OpenAPI3_1SerializationTest { + + @Test + public void testSerializePetstore() throws Exception { + + final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/petstore-3.1.yaml"); + final OpenAPI swagger = Yaml31.mapper().readValue(jsonString, OpenAPI.class); + assertNotNull(swagger); + assertEquals(swagger.getInfo().getLicense().getIdentifier(), "test"); + SerializationMatchers.assertEqualsToYaml31(swagger, "openapi: 3.1.0\n" + + "info:\n" + + " title: Swagger Petstore\n" + + " license:\n" + + " name: MIT\n" + + " identifier: test\n" + + " version: 1.0.0\n" + + "servers:\n" + + "- url: http://petstore.swagger.io/v1\n" + + "paths:\n" + + " /pets:\n" + + " get:\n" + + " tags:\n" + + " - pets\n" + + " summary: List all pets\n" + + " operationId: listPets\n" + + " parameters:\n" + + " - name: limit\n" + + " in: query\n" + + " description: How many items to return at one time (max 100)\n" + + " required: false\n" + + " schema:\n" + + " type: integer\n" + + " format: int32\n" + + " responses:\n" + + " \"200\":\n" + + " description: An paged array of pets\n" + + " headers:\n" + + " x-next:\n" + + " description: A link to the next page of responses\n" + + " schema:\n" + + " type: string\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/Pets'\n" + + " default:\n" + + " description: unexpected error\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/Error'\n" + + " post:\n" + + " tags:\n" + + " - pets\n" + + " summary: Create a pet\n" + + " operationId: createPets\n" + + " responses:\n" + + " \"201\":\n" + + " description: Null response\n" + + " default:\n" + + " description: unexpected error\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/Error'\n" + + " /pets/{petId}:\n" + + " get:\n" + + " tags:\n" + + " - pets\n" + + " summary: Info for a specific pet\n" + + " operationId: showPetById\n" + + " parameters:\n" + + " - name: petId\n" + + " in: path\n" + + " description: The id of the pet to retrieve\n" + + " required: true\n" + + " schema:\n" + + " type: string\n" + + " responses:\n" + + " \"200\":\n" + + " description: Expected response to a valid request\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/Pets'\n" + + " default:\n" + + " description: unexpected error\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/Error'\n" + + "components:\n" + + " schemas:\n" + + " Pet:\n" + + " required:\n" + + " - id\n" + + " - name\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " format: int64\n" + + " name:\n" + + " type:\n" + + " - string\n" + + " - integer\n" + + " tag:\n" + + " type: string\n" + + " Pets:\n" + + " type: array\n" + + " items:\n" + + " $ref: '#/components/schemas/Pet'\n" + + " Error:\n" + + " required:\n" + + " - code\n" + + " - message\n" + + " properties:\n" + + " code:\n" + + " type: integer\n" + + " format: int32\n" + + " message:\n" + + " type: string\n" + + "webhooks:\n" + + " newPet:\n" + + " post:\n" + + " requestBody:\n" + + " description: Information about a new pet in the system\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/Pet'\n" + + " responses:\n" + + " \"200\":\n" + + " description: Return a 200 status to indicate that the data was received\n" + + " successfully"); + SerializationMatchers.assertEqualsToJson31(swagger, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"info\" : {\n" + + " \"title\" : \"Swagger Petstore\",\n" + + " \"license\" : {\n" + + " \"name\" : \"MIT\",\n" + + " \"identifier\" : \"test\"\n" + + " },\n" + + " \"version\" : \"1.0.0\"\n" + + " },\n" + + " \"servers\" : [ {\n" + + " \"url\" : \"http://petstore.swagger.io/v1\"\n" + + " } ],\n" + + " \"paths\" : {\n" + + " \"/pets\" : {\n" + + " \"get\" : {\n" + + " \"tags\" : [ \"pets\" ],\n" + + " \"summary\" : \"List all pets\",\n" + + " \"operationId\" : \"listPets\",\n" + + " \"parameters\" : [ {\n" + + " \"name\" : \"limit\",\n" + + " \"in\" : \"query\",\n" + + " \"description\" : \"How many items to return at one time (max 100)\",\n" + + " \"required\" : false,\n" + + " \"schema\" : {\n" + + " \"type\" : \"integer\",\n" + + " \"format\" : \"int32\"\n" + + " }\n" + + " } ],\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"An paged array of pets\",\n" + + " \"headers\" : {\n" + + " \"x-next\" : {\n" + + " \"description\" : \"A link to the next page of responses\",\n" + + " \"schema\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"$ref\" : \"#/components/schemas/Pets\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"default\" : {\n" + + " \"description\" : \"unexpected error\",\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"$ref\" : \"#/components/schemas/Error\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"post\" : {\n" + + " \"tags\" : [ \"pets\" ],\n" + + " \"summary\" : \"Create a pet\",\n" + + " \"operationId\" : \"createPets\",\n" + + " \"responses\" : {\n" + + " \"201\" : {\n" + + " \"description\" : \"Null response\"\n" + + " },\n" + + " \"default\" : {\n" + + " \"description\" : \"unexpected error\",\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"$ref\" : \"#/components/schemas/Error\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"/pets/{petId}\" : {\n" + + " \"get\" : {\n" + + " \"tags\" : [ \"pets\" ],\n" + + " \"summary\" : \"Info for a specific pet\",\n" + + " \"operationId\" : \"showPetById\",\n" + + " \"parameters\" : [ {\n" + + " \"name\" : \"petId\",\n" + + " \"in\" : \"path\",\n" + + " \"description\" : \"The id of the pet to retrieve\",\n" + + " \"required\" : true,\n" + + " \"schema\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " } ],\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"Expected response to a valid request\",\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"$ref\" : \"#/components/schemas/Pets\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"default\" : {\n" + + " \"description\" : \"unexpected error\",\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"$ref\" : \"#/components/schemas/Error\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"schemas\" : {\n" + + " \"Pet\" : {\n" + + " \"required\" : [ \"id\", \"name\" ],\n" + + " \"properties\" : {\n" + + " \"id\" : {\n" + + " \"type\" : \"integer\",\n" + + " \"format\" : \"int64\"\n" + + " },\n" + + " \"name\" : {\n" + + " \"type\" : [\"string\", \"integer\"]\n" + + " },\n" + + " \"tag\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"Pets\" : {\n" + + " \"type\" : \"array\",\n" + + " \"items\" : {\n" + + " \"$ref\" : \"#/components/schemas/Pet\"\n" + + " }\n" + + " },\n" + + " \"Error\" : {\n" + + " \"required\" : [ \"code\", \"message\" ],\n" + + " \"properties\" : {\n" + + " \"code\" : {\n" + + " \"type\" : \"integer\",\n" + + " \"format\" : \"int32\"\n" + + " },\n" + + " \"message\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"webhooks\" : {\n" + + " \"newPet\" : {\n" + + " \"post\" : {\n" + + " \"requestBody\" : {\n" + + " \"description\" : \"Information about a new pet in the system\",\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"$ref\" : \"#/components/schemas/Pet\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"Return a 200 status to indicate that the data was received successfully\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + + } + + @Test + public void testInfoSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .info(new Info() + .title("Title test") + .description("This is a description for test") + .summary("Test Summary") + .version("1.0.0") + .termsOfService("https://test.term.of.services") + .contact(new Contact() + .name("Test Contact") + .url("https://test.contact.url") + .email("test@email.com")) + .license(new License() + .name("test license") + .url("https://test.license.com") + .identifier("swagger"))); + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "info:\n" + + " title: Title test\n" + + " description: This is a description for test\n" + + " summary: Test Summary\n" + + " termsOfService: https://test.term.of.services\n" + + " contact:\n" + + " name: Test Contact\n" + + " url: https://test.contact.url\n" + + " email: test@email.com\n" + + " license:\n" + + " name: test license\n" + + " url: https://test.license.com\n" + + " identifier: swagger\n" + + " version: 1.0.0"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"info\" : {\n" + + " \"title\" : \"Title test\",\n" + + " \"description\" : \"This is a description for test\",\n" + + " \"summary\" : \"Test Summary\",\n" + + " \"termsOfService\" : \"https://test.term.of.services\",\n" + + " \"contact\" : {\n" + + " \"name\" : \"Test Contact\",\n" + + " \"url\" : \"https://test.contact.url\",\n" + + " \"email\" : \"test@email.com\"\n" + + " },\n" + + " \"license\" : {\n" + + " \"name\" : \"test license\",\n" + + " \"url\" : \"https://test.license.com\",\n" + + " \"identifier\" : \"swagger\"\n" + + " },\n" + + " \"version\" : \"1.0.0\"\n" + + " }\n" + + "}"); + + openAPI.setOpenapi("3.0.3"); + SerializationMatchers.assertEqualsToYaml(openAPI, "openapi: 3.0.3\n" + + "info:\n" + + " title: Title test\n" + + " description: This is a description for test\n" + + " termsOfService: https://test.term.of.services\n" + + " contact:\n" + + " name: Test Contact\n" + + " url: https://test.contact.url\n" + + " email: test@email.com\n" + + " license:\n" + + " name: test license\n" + + " url: https://test.license.com\n" + + " version: 1.0.0"); + + SerializationMatchers.assertEqualsToJson(openAPI, "{\n" + + " \"openapi\" : \"3.0.3\",\n" + + " \"info\" : {\n" + + " \"title\" : \"Title test\",\n" + + " \"description\" : \"This is a description for test\",\n" + + " \"termsOfService\" : \"https://test.term.of.services\",\n" + + " \"contact\" : {\n" + + " \"name\" : \"Test Contact\",\n" + + " \"url\" : \"https://test.contact.url\",\n" + + " \"email\" : \"test@email.com\"\n" + + " },\n" + + " \"license\" : {\n" + + " \"name\" : \"test license\",\n" + + " \"url\" : \"https://test.license.com\"\n" + + " },\n" + + " \"version\" : \"1.0.0\"\n" + + " }\n" + + "}"); + } + + @Test + public void testWebHooksSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .addWebhooks("hook", new PathItem() + .description("test path hook") + .get(new Operation() + .operationId("testHookOperation") + .responses(new ApiResponses() + .addApiResponse("200", new ApiResponse().description("test response description"))))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "webhooks:\n" + + " hook:\n" + + " description: test path hook\n" + + " get:\n" + + " operationId: testHookOperation\n" + + " responses:\n" + + " \"200\":\n" + + " description: test response description"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"webhooks\" : {\n" + + " \"hook\" : {\n" + + " \"description\" : \"test path hook\",\n" + + " \"get\" : {\n" + + " \"operationId\" : \"testHookOperation\",\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"test response description\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + openAPI.setOpenapi("3.0.3"); + SerializationMatchers.assertEqualsToYaml(openAPI, "openapi: 3.0.3"); + SerializationMatchers.assertEqualsToJson(openAPI, "{\n" + + " \"openapi\" : \"3.0.3\"\n}"); + + } + + @Test + public void testComponentPathItemsSerialization() { + Schema schema = new StringSchema(); + schema.addType(schema.getType()); + OpenAPI openAPI = new OpenAPI().openapi("3.1.0").components(new Components() + .addSchemas("stringTest", schema) + .addPathItem("/pathTest", new PathItem() + .description("test path item") + .get(new Operation() + .operationId("testPathItem") + .responses(new ApiResponses() + .addApiResponse("200", new ApiResponse().description("response description"))))) + .addResponses("201", new ApiResponse() + .description("api response description")) + .addParameters("param", new Parameter() + .in("query") + .description("parameter description") + .schema(schema)) + .addExamples("example", new Example() + .summary("example summary") + .value("This is an example/") + .description("example description")) + .addRequestBodies("body", new RequestBody() + .content(new Content() + .addMediaType("application/json", new MediaType() + .schema(new ObjectSchema())))) + .addHeaders("test-head", new Header() + .description("test header description")) + .addSecuritySchemes("basic", new SecurityScheme() + .in(SecurityScheme.In.HEADER) + .scheme("http") + .description("ref security description")) + .addLinks("Link", new Link() + .operationRef("#/paths/~12.0~1repositories~1{username}/get")) + .addCallbacks("TestCallback", new Callback().addPathItem("{$request.query.queryUrl}", new PathItem() + .description("test path item") + .post(new Operation() + .operationId("testPathItem"))))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "components:\n" + + " schemas:\n" + + " stringTest:\n" + + " type: string\n" + + " responses:\n" + + " \"201\":\n" + + " description: api response description\n" + + " parameters:\n" + + " param:\n" + + " in: query\n" + + " description: parameter description\n" + + " schema:\n" + + " type: string\n" + + " examples:\n" + + " example:\n" + + " summary: example summary\n" + + " description: example description\n" + + " value: This is an example/\n" + + " requestBodies:\n" + + " body:\n" + + " content:\n" + + " application/json:\n" + + " schema: {}\n" + + " headers:\n" + + " test-head:\n" + + " description: test header description\n" + + " securitySchemes:\n" + + " basic:\n" + + " description: ref security description\n" + + " in: header\n" + + " scheme: http\n" + + " links:\n" + + " Link:\n" + + " operationRef: \"#/paths/~12.0~1repositories~1{username}/get\"\n" + + " callbacks:\n" + + " TestCallback:\n" + + " '{$request.query.queryUrl}':\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem\n" + + " pathItems:\n" + + " /pathTest:\n" + + " description: test path item\n" + + " get:\n" + + " operationId: testPathItem\n" + + " responses:\n" + + " \"200\":\n" + + " description: response description"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"components\" : {\n" + + " \"schemas\" : {\n" + + " \"stringTest\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " },\n" + + " \"responses\" : {\n" + + " \"201\" : {\n" + + " \"description\" : \"api response description\"\n" + + " }\n" + + " },\n" + + " \"parameters\" : {\n" + + " \"param\" : {\n" + + " \"in\" : \"query\",\n" + + " \"description\" : \"parameter description\",\n" + + " \"schema\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"examples\" : {\n" + + " \"example\" : {\n" + + " \"summary\" : \"example summary\",\n" + + " \"description\" : \"example description\",\n" + + " \"value\" : \"This is an example/\"\n" + + " }\n" + + " },\n" + + " \"requestBodies\" : {\n" + + " \"body\" : {\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : { }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"headers\" : {\n" + + " \"test-head\" : {\n" + + " \"description\" : \"test header description\"\n" + + " }\n" + + " },\n" + + " \"securitySchemes\" : {\n" + + " \"basic\" : {\n" + + " \"description\" : \"ref security description\",\n" + + " \"in\" : \"header\",\n" + + " \"scheme\" : \"http\"\n" + + " }\n" + + " },\n" + + " \"links\" : {\n" + + " \"Link\" : {\n" + + " \"operationRef\" : \"#/paths/~12.0~1repositories~1{username}/get\"\n" + + " }\n" + + " },\n" + + " \"callbacks\" : {\n" + + " \"TestCallback\" : {\n" + + " \"{$request.query.queryUrl}\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"pathItems\" : {\n" + + " \"/pathTest\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"get\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"response description\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + + openAPI.openapi("3.0.3"); + SerializationMatchers.assertEqualsToYaml(openAPI, "openapi: 3.0.3\n" + + "components:\n" + + " schemas:\n" + + " stringTest:\n" + + " type: string\n" + + " responses:\n" + + " \"201\":\n" + + " description: api response description\n" + + " parameters:\n" + + " param:\n" + + " in: query\n" + + " description: parameter description\n" + + " schema:\n" + + " type: string\n" + + " examples:\n" + + " example:\n" + + " summary: example summary\n" + + " description: example description\n" + + " value: This is an example/\n" + + " requestBodies:\n" + + " body:\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " type: object\n" + + " headers:\n" + + " test-head:\n" + + " description: test header description\n" + + " securitySchemes:\n" + + " basic:\n" + + " description: ref security description\n" + + " in: header\n" + + " scheme: http\n" + + " links:\n" + + " Link:\n" + + " operationRef: \"#/paths/~12.0~1repositories~1{username}/get\"\n" + + " callbacks:\n" + + " TestCallback:\n" + + " '{$request.query.queryUrl}':\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem"); + + SerializationMatchers.assertEqualsToJson(openAPI, "{\n" + + " \"openapi\" : \"3.0.3\",\n" + + " \"components\" : {\n" + + " \"schemas\" : {\n" + + " \"stringTest\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " },\n" + + " \"responses\" : {\n" + + " \"201\" : {\n" + + " \"description\" : \"api response description\"\n" + + " }\n" + + " },\n" + + " \"parameters\" : {\n" + + " \"param\" : {\n" + + " \"in\" : \"query\",\n" + + " \"description\" : \"parameter description\",\n" + + " \"schema\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"examples\" : {\n" + + " \"example\" : {\n" + + " \"summary\" : \"example summary\",\n" + + " \"description\" : \"example description\",\n" + + " \"value\" : \"This is an example/\"\n" + + " }\n" + + " },\n" + + " \"requestBodies\" : {\n" + + " \"body\" : {\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : {\n" + + " \"type\" : \"object\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"headers\" : {\n" + + " \"test-head\" : {\n" + + " \"description\" : \"test header description\"\n" + + " }\n" + + " },\n" + + " \"securitySchemes\" : {\n" + + " \"basic\" : {\n" + + " \"description\" : \"ref security description\",\n" + + " \"in\" : \"header\",\n" + + " \"scheme\" : \"http\"\n" + + " }\n" + + " },\n" + + " \"links\" : {\n" + + " \"Link\" : {\n" + + " \"operationRef\" : \"#/paths/~12.0~1repositories~1{username}/get\"\n" + + " }\n" + + " },\n" + + " \"callbacks\" : {\n" + + " \"TestCallback\" : {\n" + + " \"{$request.query.queryUrl}\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testDiscriminatorSerialization() { + Schema propertySchema1 = new StringSchema(); + propertySchema1.addType(propertySchema1.getType()); + + Schema propertySchema2 = new StringSchema(); + propertySchema2.addType(propertySchema2.getType()); + + Discriminator discriminator = new Discriminator().propertyName("type"); + discriminator.addExtension("x-otherName", "discriminationType"); + + Schema schema = new ObjectSchema() + .addProperties("name", propertySchema1) + .addProperties("type", propertySchema1) + .discriminator(discriminator); + + schema.addType(schema.getType()); + OpenAPI openAPI = new OpenAPI().openapi("3.1.0").components(new Components() + .addSchemas("pet", schema)); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "components:\n" + + " schemas:\n" + + " pet:\n" + + " properties:\n" + + " name:\n" + + " type: string\n" + + " type:\n" + + " type: string\n" + + " discriminator:\n" + + " propertyName: type\n" + + " x-otherName: discriminationType\n" + + " type: object"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"components\" : {\n" + + " \"schemas\" : {\n" + + " \"pet\" : {\n" + + " \"properties\" : {\n" + + " \"name\" : {\n" + + " \"type\" : \"string\"\n" + + " },\n" + + " \"type\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " },\n" + + " \"discriminator\" : {\n" + + " \"propertyName\" : \"type\",\n" + + " \"x-otherName\" : \"discriminationType\"\n" + + " },\n" + + " \"type\" : \"object\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + + openAPI.openapi("3.0.3"); + + SerializationMatchers.assertEqualsToYaml(openAPI, "openapi: 3.0.3\n" + + "components:\n" + + " schemas:\n" + + " pet:\n" + + " properties:\n" + + " name:\n" + + " type: string\n" + + " type:\n" + + " type: string\n" + + " discriminator:\n" + + " propertyName: type\n" + + " type: object"); + + SerializationMatchers.assertEqualsToJson(openAPI, "{\n" + + " \"openapi\" : \"3.0.3\",\n" + + " \"components\" : {\n" + + " \"schemas\" : {\n" + + " \"pet\" : {\n" + + " \"properties\" : {\n" + + " \"name\" : {\n" + + " \"type\" : \"string\"\n" + + " },\n" + + " \"type\" : {\n" + + " \"type\" : \"string\"\n" + + " }\n" + + " },\n" + + " \"discriminator\" : {\n" + + " \"propertyName\" : \"type\"\n" + + " },\n" + + " \"type\" : \"object\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testPathItemsRefSerialization() { + OpenAPI openAPI = new OpenAPI().openapi("3.1.0") + .path("/pathTest", new PathItem() + .$ref("#/components/pathItems/pathTest") + .description("This is a ref path item") + .summary("ref path item") + ) + .components(new Components() + .addPathItem("pathTest", new PathItem() + .description("test path item") + .get(new Operation() + .operationId("testPathItem") + .responses(new ApiResponses() + .addApiResponse("200", new ApiResponse().description("response description")))))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /pathTest:\n" + + " $ref: '#/components/pathItems/pathTest'\n" + + " description: This is a ref path item\n" + + " summary: ref path item\n" + + "components:\n" + + " pathItems:\n" + + " pathTest:\n" + + " description: test path item\n" + + " get:\n" + + " operationId: testPathItem\n" + + " responses:\n" + + " \"200\":\n" + + " description: response description"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/pathTest\" : {\n" + + " \"summary\" : \"ref path item\",\n" + + " \"description\" : \"This is a ref path item\",\n" + + " \"$ref\" : \"#/components/pathItems/pathTest\"\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"pathItems\" : {\n" + + " \"pathTest\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"get\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"response description\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testResponseRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .path("/test", new PathItem() + .description("test path item") + .get(new Operation() + .operationId("testPathItem") + .responses(new ApiResponses() + .addApiResponse("200" , new ApiResponse() + .description("point to a $ref response") + .$ref("#/components/responses/okResponse"))))) + .components(new Components() + .addResponses("okResponse", new ApiResponse().description("everything is good"))); + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /test:\n" + + " description: test path item\n" + + " get:\n" + + " operationId: testPathItem\n" + + " responses:\n" + + " \"200\":\n" + + " description: point to a $ref response\n" + + " $ref: '#/components/responses/okResponse'\n" + + "components:\n" + + " responses:\n" + + " okResponse:\n" + + " description: everything is good"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/test\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"get\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"responses\" : {\n" + + " \"200\" : {\n" + + " \"description\" : \"point to a $ref response\",\n" + + " \"$ref\" : \"#/components/responses/okResponse\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"responses\" : {\n" + + " \"okResponse\" : {\n" + + " \"description\" : \"everything is good\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testParameterRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .components(new Components() + .addParameters("testParameter", new Parameter() + .in("query"))) + .path("/test", new PathItem() + .description("test path item") + .get(new Operation() + .operationId("testPathItem") + .addParametersItem(new Parameter() + .$ref("#/components/parameters/testParameter") + .description("test parameter")))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /test:\n" + + " description: test path item\n" + + " get:\n" + + " operationId: testPathItem\n" + + " parameters:\n" + + " - description: test parameter\n" + + " $ref: '#/components/parameters/testParameter'\n" + + "components:\n" + + " parameters:\n" + + " testParameter:\n" + + " in: query"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/test\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"get\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"parameters\" : [ {\n" + + " \"description\" : \"test parameter\",\n" + + " \"$ref\" : \"#/components/parameters/testParameter\"\n" + + " } ]\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"parameters\" : {\n" + + " \"testParameter\" : {\n" + + " \"in\" : \"query\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testExampleRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .components(new Components() + .addExamples("testExample", new Example() + .value("Example on test") + .description("this is a example desc") + .summary("this is a summary test")) + .addSchemas("schema", new Schema().example(new Example() + .$ref("#/components/examples/testExample") + .description("ref description") + .summary("ref summary")))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "components:\n" + + " schemas:\n" + + " schema:\n" + + " example:\n" + + " summary: ref summary\n" + + " description: ref description\n" + + " $ref: '#/components/examples/testExample'\n" + + " examples:\n" + + " testExample:\n" + + " summary: this is a summary test\n" + + " description: this is a example desc\n" + + " value: Example on test"); + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"components\" : {\n" + + " \"schemas\" : {\n" + + " \"schema\" : {\n" + + " \"example\" : {\n" + + " \"summary\" : \"ref summary\",\n" + + " \"description\" : \"ref description\",\n" + + " \"$ref\" : \"#/components/examples/testExample\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"examples\" : {\n" + + " \"testExample\" : {\n" + + " \"summary\" : \"this is a summary test\",\n" + + " \"description\" : \"this is a example desc\",\n" + + " \"value\" : \"Example on test\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + @Test + public void testRequestBodyRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .path("/test", new PathItem() + .description("test path item") + .post(new Operation() + .operationId("testPathItem") + .requestBody(new RequestBody() + .$ref("#/components/requestBodies/body") + .description("ref request body")))) + .components(new Components() + .addRequestBodies("body", new RequestBody() + .content(new Content() + .addMediaType("application/json", new MediaType() + .schema(new ObjectSchema()))))) + ; + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /test:\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem\n" + + " requestBody:\n" + + " description: ref request body\n" + + " $ref: '#/components/requestBodies/body'\n" + + "components:\n" + + " requestBodies:\n" + + " body:\n" + + " content:\n" + + " application/json:\n" + + " schema: {}"); + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/test\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"requestBody\" : {\n" + + " \"description\" : \"ref request body\",\n" + + " \"$ref\" : \"#/components/requestBodies/body\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"requestBodies\" : {\n" + + " \"body\" : {\n" + + " \"content\" : {\n" + + " \"application/json\" : {\n" + + " \"schema\" : { }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testHeaderRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .path("/test", new PathItem() + .description("test path item") + .post(new Operation() + .operationId("testPathItem") + .responses(new ApiResponses() + .addApiResponse("default", new ApiResponse() + .description("default response") + .addHeaderObject("header", new Header() + .$ref("#/components/responses/okResponse") + .description("ref header description")))) + )) + .components(new Components() + .addHeaders("test-head", new Header() + .description("test header description"))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /test:\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " headers:\n" + + " header:\n" + + " description: ref header description\n" + + " $ref: '#/components/responses/okResponse'\n" + + "components:\n" + + " headers:\n" + + " test-head:\n" + + " description: test header description"); + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/test\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"responses\" : {\n" + + " \"default\" : {\n" + + " \"description\" : \"default response\",\n" + + " \"headers\" : {\n" + + " \"header\" : {\n" + + " \"description\" : \"ref header description\",\n" + + " \"$ref\" : \"#/components/responses/okResponse\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"headers\" : {\n" + + " \"test-head\" : {\n" + + " \"description\" : \"test header description\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testSecuritySchemeRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .components(new Components().addSecuritySchemes("basic", new SecurityScheme() + .$ref("https://external.site.com/#components/securitySchemes/basic") + .description("ref security description"))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "components:\n" + + " securitySchemes:\n" + + " basic:\n" + + " description: ref security description\n" + + " $ref: https://external.site.com/#components/securitySchemes/basic"); + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"components\" : {\n" + + " \"securitySchemes\" : {\n" + + " \"basic\" : {\n" + + " \"description\" : \"ref security description\",\n" + + " \"$ref\" : \"https://external.site.com/#components/securitySchemes/basic\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testLinkRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .path("/test", new PathItem() + .description("test path item") + .post(new Operation() + .operationId("testPathItem") + .responses(new ApiResponses() + .addApiResponse("default", new ApiResponse() + .description("default response") + .addLink("link", new Link() + .$ref("#/components/links/Link") + .description("ref link description")))))) + .components(new Components().addLinks("Link", new Link() + .operationRef("#/paths/~12.0~1repositories~1{username}/get"))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /test:\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " links:\n" + + " link:\n" + + " description: ref link description\n" + + " $ref: '#/components/links/Link'\n" + + "components:\n" + + " links:\n" + + " Link:\n" + + " operationRef: \"#/paths/~12.0~1repositories~1{username}/get\""); + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/test\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"responses\" : {\n" + + " \"default\" : {\n" + + " \"description\" : \"default response\",\n" + + " \"links\" : {\n" + + " \"link\" : {\n" + + " \"description\" : \"ref link description\",\n" + + " \"$ref\" : \"#/components/links/Link\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"links\" : {\n" + + " \"Link\" : {\n" + + " \"operationRef\" : \"#/paths/~12.0~1repositories~1{username}/get\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + @Test + public void testCallRefSerialization() { + OpenAPI openAPI = new OpenAPI() + .openapi("3.1.0") + .path("/test", new PathItem() + .description("test path item") + .post(new Operation() + .operationId("testPathItem") + .addCallback("callbackSample", new Callback() + .$ref("#/components/callbacks/TestCallback")))) + .components(new Components().addCallbacks("TestCallback", new Callback().addPathItem("{$request.query.queryUrl}", new PathItem() + .description("test path item") + .post(new Operation() + .operationId("testPathItem"))))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.1.0\n" + + "paths:\n" + + " /test:\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem\n" + + " callbacks:\n" + + " callbackSample:\n" + + " $ref: '#/components/callbacks/TestCallback'\n" + + "components:\n" + + " callbacks:\n" + + " TestCallback:\n" + + " '{$request.query.queryUrl}':\n" + + " description: test path item\n" + + " post:\n" + + " operationId: testPathItem"); + + SerializationMatchers.assertEqualsToJson31(openAPI, "{\n" + + " \"openapi\" : \"3.1.0\",\n" + + " \"paths\" : {\n" + + " \"/test\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\",\n" + + " \"callbacks\" : {\n" + + " \"callbackSample\" : {\n" + + " \"$ref\" : \"#/components/callbacks/TestCallback\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"components\" : {\n" + + " \"callbacks\" : {\n" + + " \"TestCallback\" : {\n" + + " \"{$request.query.queryUrl}\" : {\n" + + " \"description\" : \"test path item\",\n" + + " \"post\" : {\n" + + " \"operationId\" : \"testPathItem\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + } + + +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/SchemaSerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/SchemaSerializationTest.java new file mode 100644 index 0000000000..00f63796ec --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/SchemaSerializationTest.java @@ -0,0 +1,55 @@ +package io.swagger.v3.core.serialization; + +import io.swagger.v3.core.matchers.SerializationMatchers; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import org.testng.annotations.Test; + +public class SchemaSerializationTest { + + @Test + public void serializeRefSchema3_1() { + OpenAPI openAPI = new OpenAPI() + .components(new Components() + .addSchemas("Pet", new Schema() + .addProperties("id", new Schema().type("integer")) + .addProperties("name", new Schema().type("string")) + .addProperties("tag", new Schema().type("string"))) + .addSchemas("AnotherPet", new Schema() + .title("Another Pet") + .description("Another Pet for petstore referencing Pet schema") + .$ref("#/components/schemas/Pet") + .addProperties("category", new Schema().type("string")) + .addProperties("photoUrl", new Schema().type("string")))); + + SerializationMatchers.assertEqualsToYaml31(openAPI, "openapi: 3.0.1\n" + + "components:\n" + + " schemas:\n" + + " Pet:\n" + + " properties:\n" + + " id: {}\n" + + " name: {}\n" + + " tag: {}\n" + + " AnotherPet:\n" + + " title: Another Pet\n" + + " properties:\n" + + " category: {}\n" + + " photoUrl: {}\n" + + " description: Another Pet for petstore referencing Pet schema\n" + + " $ref: '#/components/schemas/Pet'"); + SerializationMatchers.assertEqualsToYaml(openAPI, "openapi: 3.0.1\n" + + "components:\n" + + " schemas:\n" + + " Pet:\n" + + " properties:\n" + + " id:\n" + + " type: integer\n" + + " name:\n" + + " type: string\n" + + " tag:\n" + + " type: string\n" + + " AnotherPet:\n" + + " $ref: '#/components/schemas/Pet'"); + } +} diff --git a/modules/swagger-core/src/test/resources/specFiles/3.1.0/changelog-3.1.yaml b/modules/swagger-core/src/test/resources/specFiles/3.1.0/changelog-3.1.yaml new file mode 100644 index 0000000000..b241894e96 --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/3.1.0/changelog-3.1.yaml @@ -0,0 +1,137 @@ +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + identifier: test +servers: + - url: http://petstore.swagger.io/v1 +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + "201": + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: + - object + - string + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + testenum: + type: string + enum: + - available + - pending + - sold + default: available + testconst: + type: string + const: pending + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.yaml b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.yaml new file mode 100644 index 0000000000..e09ccbf97a --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1.yaml @@ -0,0 +1,126 @@ +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + identifier: test +servers: + - url: http://petstore.swagger.io/v1 +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + "201": + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: + - string + - integer + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_more.yaml b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_more.yaml new file mode 100644 index 0000000000..73bbdd7e2a --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_more.yaml @@ -0,0 +1,130 @@ +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + identifier: test +servers: + - url: http://petstore.swagger.io/v1 +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + "201": + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: + - string + - integer + tag: + type: string + Pets: + $id: test + $anchor: test + type: array + items: + $ref: "#/components/schemas/Pet" + description: desc + format: int32 + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_refs_siblings.yaml b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_refs_siblings.yaml new file mode 100644 index 0000000000..7d860352e3 --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_refs_siblings.yaml @@ -0,0 +1,149 @@ +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + summary: petstore sample for OAS 3.1.0 + license: + name: MIT + identifier: test +servers: + - url: http://petstore.swagger.io/v1 +paths: + /ref_pet: + $ref: '#/components/pathItems/pet' + description: "ref pathItem description" + summary: "ref pathItem summary" + /pets: + post: + tags: + - pets + summary: Create a pet + operationId: createPets + parameters: + - $ref: '#/components/parameters/testParameter' + description: "ref parameter description" + summary: "ref parameter summary" + - name: randomParam + in: query + schema: + type: string + examples: + refExample: + $ref: '#/components/examples/testExample' + description: "ref example description" + summary: "ref example summary" + callbacks: + callIt: + $ref: '#/components/callbacks/TestCallback' + requestBody: + $ref: '#/components/requestBodies/body' + description: "ref request body description" + summary: "ref request body summary" + responses: + "201": + $ref: '#/components/responses/okResponse' + description: "ref response description" + summary: "ref response summary" + default: + description: 'default response' + headers: + head: + $ref: '#/components/headers/head' + description: "ref header description" + summary: "ref header summary" + + +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + discriminator: + propertyName: tag + x-test-extension: extended + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + description: desc + format: int32 + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + AnotherPet: + title: Another Pet + description: Another Pet for petstore referencing Pet schema + $ref: "#/components/schemas/Pet" + properties: + category: + type: string + photoUrl: + type: string + pathItems: + pet: + description: get a pet + get: + operationId: getPet + responses: + "200": + description: pet returned + links: + address: + operationId: getUserAddressByUUID + parameters: + # get the `uuid` field from the `uuid` field in the response body + userUuid: $response.body#/uuid + callbacks: + TestCallback: + '{$request.query.queryUrl}': + description: test path item + post: + operationId: testPathItem + responses: + default: + description: ok + securitySchemes: + basic: + $ref: "#/components/securitySchemes/http" + http: + type: http + scheme: basic + headers: + head: + schema: + type: string + requestBodies: + body: + description: client model + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + okResponse: + description: everything is good" + examples: + testExample: + value: Example on test + parameters: + testParameter: + name: param + in: query + schema: + type: string diff --git a/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_sample.yaml b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_sample.yaml new file mode 100644 index 0000000000..ed7368dd6e --- /dev/null +++ b/modules/swagger-core/src/test/resources/specFiles/3.1.0/petstore-3.1_sample.yaml @@ -0,0 +1,181 @@ + +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + summary: petstore sample for OAS 3.1.0 + license: + name: MIT + identifier: test +servers: + - url: http://petstore.swagger.io/v1 +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + "201": + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + discriminator: + propertyName: tag + x-test-extension: extended + Pets: + $id: test + $anchor: test + type: array + items: + $ref: "#/components/schemas/Pet" + description: desc + format: int32 + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + responses: + "201": + description: api response description + summary: api response summary + parameters: + param: + in: query + name: param0 + description: parameter description + summary: parameter summary + schema: + type: string + examples: + example: + summary: example summary + description: example description + value: This is an example + requestBodies: + body: + content: + application/json: + schema: { } + headers: + test-head: + description: test header description + summary: test header summary + securitySchemes: + basic: + description: security description + summary: security summary + type: http + links: + Link: + operationRef: "#/paths/~12.0~1repositories~1{username}/get" + callbacks: + TestCallback: + '{$request.query.queryUrl}': + description: test path item + post: + operationId: testPathItem + pathItems: + /pet: + description: get a pet + get: + operationId: getPet + responses: + "200": + description: pet returned diff --git a/modules/swagger-core/src/test/resources/specFiles/oas3.yaml b/modules/swagger-core/src/test/resources/specFiles/oas3.yaml index 06496ed488..5dad635db6 100644 --- a/modules/swagger-core/src/test/resources/specFiles/oas3.yaml +++ b/modules/swagger-core/src/test/resources/specFiles/oas3.yaml @@ -37,7 +37,6 @@ security: - tokenAuth: [] info: description: 'This is a sample server Petstore' - version: 1.0.0 title: Sample Pet Store App termsOfService: http://swagger.io/terms/ x-info: info extension diff --git a/modules/swagger-core/src/test/resources/specFiles/oas3_2.yaml b/modules/swagger-core/src/test/resources/specFiles/oas3_2.yaml index 14e8040dc5..26812c6681 100644 --- a/modules/swagger-core/src/test/resources/specFiles/oas3_2.yaml +++ b/modules/swagger-core/src/test/resources/specFiles/oas3_2.yaml @@ -1,4 +1,4 @@ -openapi: 3.0 +openapi: 3.0.0 servers: - url: http://petstore.swagger.io/api - url: https://development.gigantic-server.com/api @@ -49,7 +49,6 @@ info: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html x-license: license extension - version: 1.0.1 tags: - name: pet description: Everything about your Pets @@ -1112,31 +1111,6 @@ components: default: false xml: name: Order - properties: - id: - type: integer - format: int64 - petId: - type: integer - format: int64 - quantity: - type: integer - format: int32 - shipDate: - type: string - format: date-time - status: - type: string - description: Order Status - enum: - - placed - - approved - - delivered - complete: - type: boolean - default: false - xml: - name: Order Category: type: object properties: @@ -1386,4 +1360,4 @@ components: converter: url: https://github.com/mermade/oas3 version: 1.2.3 - x-api-title: pet store test api in components \ No newline at end of file + x-api-title: pet store test api in components diff --git a/modules/swagger-core/src/test/resources/testOAS31/basicOAS31.yaml b/modules/swagger-core/src/test/resources/testOAS31/basicOAS31.yaml new file mode 100644 index 0000000000..7a7e631695 --- /dev/null +++ b/modules/swagger-core/src/test/resources/testOAS31/basicOAS31.yaml @@ -0,0 +1,164 @@ +openapi: "3.1.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + identifier: test +servers: + - url: http://petstore.swagger.io/v1 +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + description: pet + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + description: error + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + "201": + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Tag" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + - $ref: "#/components/parameters/User" + description: user + summary: user + responses: + "200": + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + parameters: + User: + in: query + description: user + name: user + schema: + type: string + schemas: + Pet: + type: + - object + - string + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + testenum: + type: string + enum: + - available + - pending + - sold + default: available + testconst: + type: string + const: pending + tag: + type: string + arbitraryKeyword: test + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + tag: + $ref: "#/components/schemas/Tag" + Tag: + type: + - object + - string + - string + - foo + properties: + id: + type: integer + format: int64 + name: + type: string diff --git a/modules/swagger-eclipse-transformer-maven-plugin/pom.xml b/modules/swagger-eclipse-transformer-maven-plugin/pom.xml index b4097e22ca..a544b2e64d 100644 --- a/modules/swagger-eclipse-transformer-maven-plugin/pom.xml +++ b/modules/swagger-eclipse-transformer-maven-plugin/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 @@ -13,7 +13,7 @@ swagger-eclipse-transformer-maven-plugin maven-plugin - 3.6.3 + 3.8.4 0.20 3.3.0 1.2.6 @@ -39,7 +39,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.6.0 + 3.6.4 transform @@ -94,7 +94,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.6.0 + 3.6.4 provided diff --git a/modules/swagger-gradle-plugin/gradle.properties b/modules/swagger-gradle-plugin/gradle.properties index d31f621c50..479185eef5 100644 --- a/modules/swagger-gradle-plugin/gradle.properties +++ b/modules/swagger-gradle-plugin/gradle.properties @@ -1,2 +1,2 @@ -version=2.1.14-SNAPSHOT +version=2.2.0-SNAPSHOT jettyVersion=9.4.43.v20210629 diff --git a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java index bc4d96f75b..6c6adc7707 100644 --- a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java +++ b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/SwaggerPlugin.java @@ -16,7 +16,7 @@ public void apply(Project project) { config.defaultDependencies(new Action() { public void execute(DependencySet dependencies) { dependencies.add(project.getDependencies().create("org.apache.commons:commons-lang3:3.7")); - dependencies.add(project.getDependencies().create("io.swagger.core.v3:swagger-jaxrs2:2.1.14-SNAPSHOT")); + dependencies.add(project.getDependencies().create("io.swagger.core.v3:swagger-jaxrs2:2.2.0-SNAPSHOT")); dependencies.add(project.getDependencies().create("javax.ws.rs:javax.ws.rs-api:2.1")); dependencies.add(project.getDependencies().create("javax.servlet:javax.servlet-api:3.1.0")); } diff --git a/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java b/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java index 387b887aaf..8f7638253b 100644 --- a/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java +++ b/modules/swagger-gradle-plugin/src/test/java/io/swagger/v3/plugins/gradle/SwaggerResolveTest.java @@ -81,7 +81,7 @@ public void testSwaggerResolveTask() throws IOException { " mavenCentral()\n" + "}\n" + "dependencies { \n" + - " compile group: 'io.swagger.core.v3', name: 'swagger-jaxrs2', version:'2.1.14-SNAPSHOT'\n" + + " compile group: 'io.swagger.core.v3', name: 'swagger-jaxrs2', version:'2.2.0-SNAPSHOT'\n" + " compile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version:'2.1'\n" + " compile group: 'javax.servlet', name: 'javax.servlet-api', version:'3.1.0'\n" + " testCompile group: 'com.github.tomakehurst', name: 'wiremock', version:'2.27.2'\n" + diff --git a/modules/swagger-integration/pom.xml b/modules/swagger-integration/pom.xml index 39796cb272..d9c49a2622 100644 --- a/modules/swagger-integration/pom.xml +++ b/modules/swagger-integration/pom.xml @@ -6,7 +6,7 @@ io.swagger.core.v3 swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. swagger-integration diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java index 1147189d62..a62b20c3e7 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/GenericOpenApiContext.java @@ -4,7 +4,9 @@ import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -14,7 +16,9 @@ import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.core.jackson.PathsSerializer; import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Json31; import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Yaml31; import io.swagger.v3.oas.integration.api.ObjectMapperProcessor; import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; import io.swagger.v3.oas.integration.api.OpenApiConfigurationLoader; @@ -30,6 +34,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.math.BigDecimal; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashSet; @@ -65,6 +70,8 @@ public class GenericOpenApiContext implements O // -1 perpetual private long cacheTTL = -1; + private Boolean openAPI31; + public long getCacheTTL() { return cacheTTL; } @@ -272,6 +279,28 @@ public final T outputYamlMapper(ObjectMapper outputYamlMapper) { return (T) this; } + /** + * @since 2.1.8 + */ + public Boolean isOpenAPI31() { + return openAPI31; + } + + /** + * @since 2.1.8 + */ + public void setOpenAPI31(Boolean v) { + this.openAPI31 = openAPI31; + } + + /** + * @since 2.1.8 + */ + public T openAPI31(Boolean openAPI31) { + this.openAPI31 = openAPI31; + return (T) this; + } + protected void register() { OpenApiContextLocator.getInstance().putOpenApiContext(id, this); @@ -408,6 +437,7 @@ public T init() throws OpenApiConfigurationException { if (openApiConfiguration == null) { openApiConfiguration = new SwaggerConfiguration().resourcePackages(resourcePackages).resourceClasses(resourceClasses); ((SwaggerConfiguration) openApiConfiguration).setId(id); + ((SwaggerConfiguration) openApiConfiguration).setOpenAPI31(openAPI31); } openApiConfiguration = mergeParentConfiguration(openApiConfiguration, parent); @@ -426,20 +456,35 @@ public T init() throws OpenApiConfigurationException { modelConverters = buildModelConverters(ContextUtils.deepCopy(openApiConfiguration)); } if (outputJsonMapper == null) { - outputJsonMapper = Json.mapper().copy(); + if (Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31())) { + outputJsonMapper = Json31.mapper().copy(); + } else { + outputJsonMapper = Json.mapper().copy(); + } } if (outputYamlMapper == null) { - outputYamlMapper = Yaml.mapper().copy(); + if (Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31())) { + outputYamlMapper = Yaml31.mapper().copy(); + } else { + outputYamlMapper = Yaml.mapper().copy(); + } } if (openApiConfiguration.isSortOutput() != null && openApiConfiguration.isSortOutput()) { outputJsonMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); outputJsonMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); outputYamlMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); outputYamlMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); - outputJsonMapper.addMixIn(OpenAPI.class, SortedOpenAPIMixin.class); - outputJsonMapper.addMixIn(Schema.class, SortedSchemaMixin.class); - outputYamlMapper.addMixIn(OpenAPI.class, SortedOpenAPIMixin.class); - outputYamlMapper.addMixIn(Schema.class, SortedSchemaMixin.class); + if (Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31())) { + outputJsonMapper.addMixIn(OpenAPI.class, SortedOpenAPIMixin31.class); + outputJsonMapper.addMixIn(Schema.class, SortedSchemaMixin31.class); + outputYamlMapper.addMixIn(OpenAPI.class, SortedOpenAPIMixin31.class); + outputYamlMapper.addMixIn(Schema.class, SortedSchemaMixin31.class); + } else { + outputJsonMapper.addMixIn(OpenAPI.class, SortedOpenAPIMixin.class); + outputJsonMapper.addMixIn(Schema.class, SortedSchemaMixin.class); + outputYamlMapper.addMixIn(OpenAPI.class, SortedOpenAPIMixin.class); + outputYamlMapper.addMixIn(Schema.class, SortedSchemaMixin.class); + } } } catch (Exception e) { LOGGER.error("error initializing context: " + e.getMessage(), e); @@ -539,6 +584,9 @@ private OpenAPIConfiguration mergeParentConfiguration(OpenAPIConfiguration confi if (merged.getModelConverterClasses() == null) { merged.setModelConverterClassess(parentConfig.getModelConverterClasses()); } + if (merged.isOpenAPI31() == null) { + merged.setOpenAPI31(parentConfig.isOpenAPI31()); + } return merged; } @@ -611,6 +659,93 @@ static abstract class SortedSchemaMixin { @JsonInclude(JsonInclude.Include.CUSTOM) public abstract Object getExample(); + @JsonIgnore + public abstract Map getJsonSchema(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMinimumValue(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMaximumValue(); + + @JsonIgnore + public abstract Map getPatternProperties(); + + @JsonIgnore + public abstract Schema getContains(); + @JsonIgnore + public abstract String get$id(); + @JsonIgnore + public abstract String get$anchor(); + @JsonIgnore + public abstract String get$schema(); + @JsonIgnore + public abstract Set getTypes(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + + + } + + @JsonPropertyOrder(value = {"openapi", "info", "externalDocs", "servers", "security", "tags", "paths", "components", "webhooks"}, alphabetic = true) + static abstract class SortedOpenAPIMixin31 { + + @JsonAnyGetter + @JsonPropertyOrder(alphabetic = true) + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonSerialize(using = PathsSerializer.class) + public abstract Paths getPaths(); + } + + @JsonPropertyOrder(value = {"type", "format"}, alphabetic = true) + static abstract class SortedSchemaMixin31 { + + @JsonAnyGetter + @JsonPropertyOrder(alphabetic = true) + public abstract Map getExtensions(); + + //@JsonValue + @JsonIgnore + public abstract Map getJsonSchema(); + + @JsonIgnore + public abstract Boolean getNullable(); + + @JsonIgnore + public abstract Boolean getExclusiveMinimum(); + + @JsonIgnore + public abstract Boolean getExclusiveMaximum(); + + @JsonProperty("exclusiveMinimum") + public abstract BigDecimal getExclusiveMinimumValue(); + + @JsonProperty("exclusiveMaximum") + public abstract BigDecimal getExclusiveMaximumValue(); + + @JsonIgnore + public abstract String getType(); + + @JsonProperty("type") + public abstract Set getTypes(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonIgnore + public abstract boolean getExampleSetFlag(); + + @JsonInclude(JsonInclude.Include.CUSTOM) + public abstract Object getExample(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + } } diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/IntegrationObjectMapperFactory.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/IntegrationObjectMapperFactory.java index c3ea9d516f..b4a481b911 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/IntegrationObjectMapperFactory.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/IntegrationObjectMapperFactory.java @@ -8,4 +8,8 @@ public class IntegrationObjectMapperFactory extends ObjectMapperFactory { public static ObjectMapper createJson() { return ObjectMapperFactory.createJson(); } + + public static ObjectMapper createJson31() { + return ObjectMapperFactory.createJson31(); + } } diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java index 7091574270..ce48045b82 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java @@ -34,6 +34,8 @@ public class SwaggerConfiguration implements OpenAPIConfiguration { private Boolean alwaysResolveAppPath; + private Boolean openAPI31; + public Long getCacheTTL() { return cacheTTL; } @@ -281,4 +283,26 @@ public SwaggerConfiguration alwaysResolveAppPath(Boolean alwaysResolveAppPath) { setAlwaysResolveAppPath(alwaysResolveAppPath); return this; } + + /** + * @since 2.1.9 + */ + public Boolean isOpenAPI31() { + return openAPI31; + } + + /** + * @since 2.1.9 + */ + public void setOpenAPI31(Boolean v) { + this.openAPI31 = openAPI31; + } + + /** + * @since 2.1.9 + */ + public SwaggerConfiguration openAPI31(Boolean openAPI31) { + this.openAPI31 = openAPI31; + return this; + } } diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java index 8b88cb0541..780923280f 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java @@ -49,4 +49,8 @@ public interface OpenAPIConfiguration { */ Boolean isAlwaysResolveAppPath(); + /** + * @since 3.0.0 + */ + Boolean isOpenAPI31(); } diff --git a/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml b/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml index 365b1dbfbd..c7b862df26 100644 --- a/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml +++ b/modules/swagger-jaxrs2-servlet-initializer-v2/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2-servlet-initializer/pom.xml b/modules/swagger-jaxrs2-servlet-initializer/pom.xml index b400fd90fd..dd8acf4698 100644 --- a/modules/swagger-jaxrs2-servlet-initializer/pom.xml +++ b/modules/swagger-jaxrs2-servlet-initializer/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2/pom.xml b/modules/swagger-jaxrs2/pom.xml index 1baaf29c4f..11f8a11dd5 100644 --- a/modules/swagger-jaxrs2/pom.xml +++ b/modules/swagger-jaxrs2/pom.xml @@ -5,7 +5,7 @@ swagger-project io.swagger.core.v3 - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../../ 4.0.0 diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java index e48ff1cb12..95098f0427 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java @@ -1076,10 +1076,11 @@ protected Operation parseMethod( } final Class subResource = getSubResourceWithJaxRsSubresourceLocatorSpecs(method); + Schema returnTypeSchema = null; if (!shouldIgnoreClass(returnType.getTypeName()) && !method.getGenericReturnType().equals(subResource)) { ResolvedSchema resolvedSchema = ModelConverters.getInstance().resolveAsResolvedSchema(new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonViewAnnotation)); if (resolvedSchema.schema != null) { - Schema returnTypeSchema = resolvedSchema.schema; + returnTypeSchema = resolvedSchema.schema; Content content = new Content(); MediaType mediaType = new MediaType().schema(returnTypeSchema); AnnotationsUtils.applyTypes(classProduces == null ? new String[0] : classProduces.value(), @@ -1112,18 +1113,62 @@ protected Operation parseMethod( } } if (operation.getResponses() == null || operation.getResponses().isEmpty()) { - Content content = new Content(); - MediaType mediaType = new MediaType(); - AnnotationsUtils.applyTypes(classProduces == null ? new String[0] : classProduces.value(), - methodProduces == null ? new String[0] : methodProduces.value(), content, mediaType); + Content content = resolveEmptyContent(classProduces, methodProduces); ApiResponse apiResponseObject = new ApiResponse().description(DEFAULT_DESCRIPTION).content(content); operation.setResponses(new ApiResponses()._default(apiResponseObject)); } + if (returnTypeSchema != null) { + resolveResponseSchemaFromReturnType(operation, classResponses, returnTypeSchema, classProduces, methodProduces); + if (apiResponses != null) { + resolveResponseSchemaFromReturnType( + operation, + apiResponses.stream().toArray(io.swagger.v3.oas.annotations.responses.ApiResponse[]::new), + returnTypeSchema, + classProduces, + methodProduces); + } + } + return operation; } + protected Content resolveEmptyContent(Produces classProduces, Produces methodProduces) { + Content content = new Content(); + MediaType mediaType = new MediaType(); + AnnotationsUtils.applyTypes(classProduces == null ? new String[0] : classProduces.value(), + methodProduces == null ? new String[0] : methodProduces.value(), content, mediaType); + return content; + } + + protected void resolveResponseSchemaFromReturnType( + Operation operation, + io.swagger.v3.oas.annotations.responses.ApiResponse[] responses, + Schema schema, + Produces classProduces, Produces methodProduces) { + if (responses != null) { + for (io.swagger.v3.oas.annotations.responses.ApiResponse response: responses) { + if (response.useReturnTypeSchema()) { + ApiResponse opResponse = operation.getResponses().get(response.responseCode()); + if (opResponse != null) { + if (opResponse.getContent() != null) { + for (MediaType mediaType : opResponse.getContent().values()) { + mediaType.schema(schema); + } + } else { + Content content = resolveEmptyContent(classProduces, methodProduces); + for (MediaType mediaType : content.values()) { + mediaType.schema(schema); + } + opResponse.content(content); + } + } + } + } + } + } + private boolean shouldIgnoreClass(String className) { if (StringUtils.isBlank(className)) { return true; diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java index 65b33fa195..742141b118 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/ReaderTest.java @@ -13,6 +13,8 @@ import io.swagger.v3.core.model.ApiDescription; import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.jaxrs2.matchers.SerializationMatchers; +import io.swagger.v3.jaxrs2.resources.ResponseReturnTypeResource; +import io.swagger.v3.jaxrs2.resources.SchemaPropertiesResource; import io.swagger.v3.jaxrs2.resources.SingleExampleResource; import io.swagger.v3.jaxrs2.resources.BasicFieldsResource; import io.swagger.v3.jaxrs2.resources.BookStoreTicket2646; @@ -2811,4 +2813,221 @@ public void testTicket3731() { openAPI = reader.read(Ticket3731BisResource.class); SerializationMatchers.assertEqualsToYaml(openAPI, yaml); } + + @Test(description = "Test SchemaProperties and additionalProperties annotations") + public void testSchemaProperties() { + Reader reader = new Reader(new OpenAPI()); + + OpenAPI openAPI = reader.read(SchemaPropertiesResource.class); + String yaml = "openapi: 3.0.1\n" + + "paths:\n" + + " /:\n" + + " get:\n" + + " summary: Simple get operation\n" + + " description: Defines a simple get operation with no inputs and a complex output\n" + + " object\n" + + " operationId: getWithPayloadResponse\n" + + " responses:\n" + + " \"200\":\n" + + " description: voila!\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " maximum: 1\n" + + " type: integer\n" + + " default:\n" + + " description: boo\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " maxProperties: 3\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " maximum: 1\n" + + " type: integer\n" + + " description: various properties\n" + + " \"400\":\n" + + " description: additionalProperties schema\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " maxProperties: 2\n" + + " type: object\n" + + " additionalProperties:\n" + + " type: string\n" + + " \"401\":\n" + + " description: additionalProperties boolean\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " maxProperties: 2\n" + + " type: object\n" + + " additionalProperties: false\n" + + " deprecated: true\n" + + " /one:\n" + + " get:\n" + + " operationId: requestBodySchemaPropertyNoSchema\n" + + " requestBody:\n" + + " content:\n" + + " application/yaml:\n" + + " schema:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/MultipleBaseBean'\n" + + " /two:\n" + + " get:\n" + + " operationId: requestBodySchemaPropertySchema\n" + + " requestBody:\n" + + " content:\n" + + " application/yaml:\n" + + " schema:\n" + + " required:\n" + + " - foo\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/MultipleBaseBean'\n" + + " /three:\n" + + " get:\n" + + " operationId: requestBodySchemaPropertySchemaArray\n" + + " requestBody:\n" + + " content:\n" + + " application/yaml:\n" + + " schema:\n" + + " type: array\n" + + " items:\n" + + " required:\n" + + " - foo\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/MultipleBaseBean'\n" + + "components:\n" + + " schemas:\n" + + " MultipleBaseBean:\n" + + " type: object\n" + + " properties:\n" + + " beanType:\n" + + " type: string\n" + + " a:\n" + + " type: integer\n" + + " format: int32\n" + + " b:\n" + + " type: string\n" + + " description: MultipleBaseBean\n" + + " MultipleSub1Bean:\n" + + " type: object\n" + + " description: MultipleSub1Bean\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/MultipleBaseBean'\n" + + " - type: object\n" + + " properties:\n" + + " c:\n" + + " type: integer\n" + + " format: int32\n" + + " MultipleSub2Bean:\n" + + " type: object\n" + + " description: MultipleSub2Bean\n" + + " allOf:\n" + + " - $ref: '#/components/schemas/MultipleBaseBean'\n" + + " - type: object\n" + + " properties:\n" + + " d:\n" + + " type: integer\n" + + " format: int32\n"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + } + + @Test(description = "Responses schema resolved from return type") + public void testResponseReturnType() { + Reader reader = new Reader(new OpenAPI()); + + OpenAPI openAPI = reader.read(ResponseReturnTypeResource.class); + String yaml = "openapi: 3.0.1\n" + + "paths:\n" + + " /sample/{id}:\n" + + " get:\n" + + " summary: Find by id\n" + + " description: Find by id operation\n" + + " operationId: find\n" + + " parameters:\n" + + " - name: id\n" + + " in: path\n" + + " description: ID\n" + + " required: true\n" + + " schema:\n" + + " type: integer\n" + + " format: int32\n" + + " responses:\n" + + " \"200\":\n" + + " description: Ok\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/TestDTO'\n" + + " \"201\":\n" + + " description: \"201\"\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/TestDTO'\n" + + " \"204\":\n" + + " description: No Content\n" + + " content:\n" + + " application/json: {}\n" + + " /sample/{id}/default:\n" + + " get:\n" + + " summary: Find by id (default)\n" + + " description: Find by id operation (default)\n" + + " operationId: findDefault\n" + + " parameters:\n" + + " - name: id\n" + + " in: path\n" + + " description: ID\n" + + " required: true\n" + + " schema:\n" + + " type: integer\n" + + " format: int32\n" + + " responses:\n" + + " default:\n" + + " description: default response\n" + + " content:\n" + + " application/json:\n" + + " schema:\n" + + " $ref: '#/components/schemas/TestDTO'\n" + + "components:\n" + + " schemas:\n" + + " TestDTO:\n" + + " type: object\n" + + " properties:\n" + + " foo:\n" + + " type: string"; + SerializationMatchers.assertEqualsToYaml(openAPI, yaml); + } } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/integration/SortedOutputTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/integration/SortedOutputTest.java index 2bf08c51dd..7c8074e5f7 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/integration/SortedOutputTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/integration/SortedOutputTest.java @@ -27,6 +27,7 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Application; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Map; @@ -118,6 +119,33 @@ public static abstract class SortedSchemaMixin { @JsonInclude(JsonInclude.Include.CUSTOM) public abstract Object getExample(); + @JsonIgnore + public abstract Map getJsonSchema(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMinimumValue(); + + @JsonIgnore + public abstract BigDecimal getExclusiveMaximumValue(); + + @JsonIgnore + public abstract Map getPatternProperties(); + + @JsonIgnore + public abstract Schema getContains(); + @JsonIgnore + public abstract String get$id(); + @JsonIgnore + public abstract String get$anchor(); + @JsonIgnore + public abstract String get$schema(); + @JsonIgnore + public abstract Set getTypes(); + + @JsonIgnore + public abstract Object getJsonSchemaImpl(); + + } public static class SortedProcessor implements ObjectMapperProcessor { diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/ResponseReturnTypeResource.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/ResponseReturnTypeResource.java new file mode 100644 index 0000000000..1bb16211fc --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/ResponseReturnTypeResource.java @@ -0,0 +1,42 @@ +package io.swagger.v3.jaxrs2.resources; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Path("/sample") +public interface ResponseReturnTypeResource { + + @GET + @Path("/{id}") + @Operation(summary = "Find by id", description = "Find by id operation") + @ApiResponse(responseCode = "200", description = "Ok", useReturnTypeSchema = true) + @ApiResponse(responseCode = "201", + description = "201", + useReturnTypeSchema = true, + content = @Content(mediaType = "application/json")) + @ApiResponse(responseCode = "204", + description = "No Content", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Void.class))) + TestDTO find(@Parameter(description = "ID") @PathParam("id") Integer id); + + @GET + @Path("/{id}/default") + @Operation(summary = "Find by id (default)", description = "Find by id operation (default)") + TestDTO findDefault(@Parameter(description = "ID") @PathParam("id") Integer id); + + class TestDTO { + public String foo; + } +} diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/SchemaPropertiesResource.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/SchemaPropertiesResource.java new file mode 100644 index 0000000000..761e35fd1c --- /dev/null +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/resources/SchemaPropertiesResource.java @@ -0,0 +1,167 @@ +package io.swagger.v3.jaxrs2.resources; + +import io.swagger.v3.jaxrs2.resources.model.MultipleBaseBean; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +public class SchemaPropertiesResource { + + @GET + @Path("/") + @Operation( + summary = "Simple get operation", + description = "Defines a simple get operation with no inputs and a complex output object", + operationId = "getWithPayloadResponse", + deprecated = true, + responses = { + @ApiResponse( + responseCode = "200", + description = "voila!", + content = @Content( + mediaType = "application/json", + schemaProperties = { + @SchemaProperty( + name = "foo", + schema = @Schema( + type = "integer", + maximum = "1" + ) + ) + } + ) + ), + @ApiResponse( + description = "boo", + content = @Content( + schema = @Schema( + type = "object", + description = "various properties", + maxProperties = 3 + ), + mediaType = "application/json", + schemaProperties = { + @SchemaProperty( + name = "foo", + schema = @Schema( + type = "integer", + maximum = "1" + ) + ) + } + ) + ), + @ApiResponse( + responseCode = "400", + description = "additionalProperties schema", + content = @Content( + mediaType = "application/json", + schema = @Schema( + type = "object", + maxProperties = 2 + ), + additionalPropertiesSchema = @Schema( + type = "string" + ) + ) + ), + @ApiResponse( + responseCode = "401", + description = "additionalProperties boolean", + content = @Content( + mediaType = "application/json", + schema = @Schema( + type = "object", + maxProperties = 2, + additionalProperties = Schema.AdditionalPropertiesValue.FALSE + ) + ) + ) + } + ) + public void getResponses() { + } + + @GET + @Path("/one") + @Produces({MediaType.APPLICATION_JSON}) + public MultipleBaseBean requestBodySchemaPropertyNoSchema( + @RequestBody( + content = @Content( + mediaType = "application/yaml", + schemaProperties = { + @SchemaProperty( + name = "foo", + schema = @Schema( + type = "string" + ) + ) + } + ) + ) Object body) { + return null; + } + + @GET + @Path("/two") + @Produces({MediaType.APPLICATION_JSON}) + public MultipleBaseBean requestBodySchemaPropertySchema( + @RequestBody( + content = @Content( + mediaType = "application/yaml", + schema = @Schema( + type = "object", + requiredProperties = { + "foo" + } + ), + schemaProperties= { + @SchemaProperty( + name = "foo", + schema = @Schema( + type = "string" + ) + ) + } + ) + ) Object body) { + return null; + } + + @GET + @Path("/three") + @Produces({MediaType.APPLICATION_JSON}) + public MultipleBaseBean requestBodySchemaPropertySchemaArray( + @RequestBody( + content = @Content( + mediaType = "application/yaml", + array = @ArraySchema( + schema = @Schema( + type = "object", + requiredProperties = { + "foo" + } + ) + ), + schemaProperties = { + @SchemaProperty( + name = "foo", + schema = @Schema( + type = "string" + ) + ) + } + ) + ) Object body) { + return null; + } +} diff --git a/modules/swagger-maven-plugin/pom.xml b/modules/swagger-maven-plugin/pom.xml index 50d097d346..eb9de0d632 100644 --- a/modules/swagger-maven-plugin/pom.xml +++ b/modules/swagger-maven-plugin/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 @@ -29,7 +29,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.6.0 + 3.6.4 io.swagger.core.v3 @@ -88,7 +88,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.6.0 + 3.6.4 provided @@ -290,7 +290,7 @@ UTF-8 - 3.6.3 + 3.8.4 4.13.1 9.4.43.v20210629 1.21 diff --git a/modules/swagger-models/pom.xml b/modules/swagger-models/pom.xml index 3fcf82074d..531d371a7a 100644 --- a/modules/swagger-models/pom.xml +++ b/modules/swagger-models/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/annotations/OpenAPI30.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/annotations/OpenAPI30.java new file mode 100644 index 0000000000..e1c7d63e98 --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/annotations/OpenAPI30.java @@ -0,0 +1,23 @@ +/** + * 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; + +import java.lang.annotation.Inherited; + +@Inherited +public @interface OpenAPI30 { +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/annotations/OpenAPI31.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/annotations/OpenAPI31.java new file mode 100644 index 0000000000..35cec47814 --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/annotations/OpenAPI31.java @@ -0,0 +1,23 @@ +/** + * 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; + +import java.lang.annotation.Inherited; + +@Inherited +public @interface OpenAPI31 { +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Components.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Components.java index 97355fe914..662d6e54cd 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Components.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Components.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.callbacks.Callback; import io.swagger.v3.oas.models.examples.Example; import io.swagger.v3.oas.models.headers.Header; @@ -53,6 +54,12 @@ public class Components { private Map callbacks = null; private java.util.Map extensions = null; + /** + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + private Map pathItems; + /** * returns the schemas property from a Components instance. * @@ -296,6 +303,37 @@ public Components addCallbacks(String key, Callback callbacksItem) { return this; } + /** + * returns the path items property from a Components instance. + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return Map<String, PathItem> pathItems + **/ + @OpenAPI31 + public Map getPathItems() { + return pathItems; + } + + @OpenAPI31 + public void setPathItems(Map pathItems) { + this.pathItems = pathItems; + } + + @OpenAPI31 + public Components pathItems(Map pathItems) { + this.pathItems = pathItems; + return this; + } + + @OpenAPI31 + public Components addPathItem(String key, PathItem pathItem) { + if (this.pathItems == null) { + this.pathItems = new LinkedHashMap<>(); + } + this.pathItems.put(key, pathItem); + return this; + } + @Override public boolean equals(java.lang.Object o) { if (this == o) { @@ -314,12 +352,13 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.securitySchemes, components.securitySchemes) && Objects.equals(this.links, components.links) && Objects.equals(this.callbacks, components.callbacks) && - Objects.equals(this.extensions, components.extensions); + Objects.equals(this.extensions, components.extensions) && + Objects.equals(this.pathItems, components.pathItems); } @Override public int hashCode() { - return Objects.hash(schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links, callbacks, extensions); + return Objects.hash(schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links, callbacks, extensions, pathItems); } public java.util.Map getExtensions() { @@ -336,6 +375,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } @@ -359,6 +406,7 @@ public String toString() { sb.append(" securitySchemes: ").append(toIndentedString(securitySchemes)).append("\n"); sb.append(" links: ").append(toIndentedString(links)).append("\n"); sb.append(" callbacks: ").append(toIndentedString(callbacks)).append("\n"); + sb.append(" pathItems: ").append(toIndentedString(pathItems)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/ExternalDocumentation.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/ExternalDocumentation.java index 24a0fbfecf..e1fe227a12 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/ExternalDocumentation.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/ExternalDocumentation.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -100,6 +102,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/OpenAPI.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/OpenAPI.java index 21054ba4d9..76e21d5b59 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/OpenAPI.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/OpenAPI.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.security.SecurityRequirement; @@ -24,7 +26,9 @@ import io.swagger.v3.oas.models.tags.Tag; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -44,6 +48,33 @@ public class OpenAPI { private Components components = null; private java.util.Map extensions = null; + @OpenAPI31 + private String jsonSchemaDialect; + + public OpenAPI() {} + public OpenAPI(SpecVersion specVersion) { this.specVersion = specVersion;} + private SpecVersion specVersion = SpecVersion.V30; + + @JsonIgnore + public SpecVersion getSpecVersion() { + return this.specVersion; + } + + public void setSpecVersion(SpecVersion specVersion) { + this.specVersion = specVersion; + } + + public OpenAPI specVersion(SpecVersion specVersion) { + this.setSpecVersion(specVersion); + return this; + } + + /** + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + private java.util.Map webhooks = null; + /** * returns the openapi property from a OpenAPI instance. * @@ -249,6 +280,55 @@ public OpenAPI schemaRequirement(String name, SecurityScheme securityScheme) { return this; } + /** + * returns the webhooks property from a OpenAPI instance. + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return Map<String, PathItem> webhooks + **/ + + @OpenAPI31 + public Map getWebhooks() { + return webhooks; + } + + @OpenAPI31 + public void setWebhooks(Map webhooks) { + this.webhooks = webhooks; + } + + @OpenAPI31 + public OpenAPI webhooks(Map webhooks) { + this.webhooks = webhooks; + return this; + } + + @OpenAPI31 + public OpenAPI addWebhooks(String key, PathItem pathItem) { + if (this.webhooks == null) { + this.webhooks = new LinkedHashMap<>(); + } + this.webhooks.put(key, pathItem); + return this; + } + + @OpenAPI31 + public String getJsonSchemaDialect() { + return jsonSchemaDialect; + } + + @OpenAPI31 + public void setJsonSchemaDialect(String jsonSchemaDialect) { + this.jsonSchemaDialect = jsonSchemaDialect; + } + + @OpenAPI31 + public OpenAPI jsonSchemaDialect(String jsonSchemaDialect) { + this.jsonSchemaDialect = jsonSchemaDialect; + return this; + } + + @Override public boolean equals(java.lang.Object o) { if (this == o) { @@ -266,12 +346,14 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.tags, openAPI.tags) && Objects.equals(this.paths, openAPI.paths) && Objects.equals(this.components, openAPI.components) && - Objects.equals(this.extensions, openAPI.extensions); + Objects.equals(this.webhooks, openAPI.webhooks) && + Objects.equals(this.extensions, openAPI.extensions) && + Objects.equals(this.jsonSchemaDialect, openAPI.jsonSchemaDialect); } @Override public int hashCode() { - return Objects.hash(openapi, info, externalDocs, servers, security, tags, paths, components, extensions); + return Objects.hash(openapi, info, externalDocs, servers, security, tags, paths, components, webhooks, extensions, jsonSchemaDialect); } public java.util.Map getExtensions() { @@ -288,6 +370,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } @@ -310,6 +400,8 @@ public String toString() { sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); sb.append(" paths: ").append(toIndentedString(paths)).append("\n"); sb.append(" components: ").append(toIndentedString(components)).append("\n"); + if (specVersion == SpecVersion.V31) sb.append(" webhooks: ").append(toIndentedString(webhooks)).append("\n"); + if (specVersion == SpecVersion.V31) sb.append(" jsonSchemaDialect: ").append(toIndentedString(jsonSchemaDialect)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Operation.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Operation.java index fbcce358c0..e7e641e1a0 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Operation.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Operation.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.callbacks.Callback; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; @@ -24,6 +25,7 @@ import io.swagger.v3.oas.models.servers.Server; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -236,6 +238,14 @@ public Operation callbacks(Map callbacks) { return this; } + public Operation addCallback(String key, Callback callback) { + if (this.callbacks == null) { + this.callbacks = new LinkedHashMap<>(); + } + this.callbacks.put(key, callback); + return this; + } + /** * returns the deprecated property from a Operation instance. * @@ -352,6 +362,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/PathItem.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/PathItem.java index 799d2dfc3d..b44d1c1b40 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/PathItem.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/PathItem.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.servers.Server; @@ -406,6 +407,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Paths.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Paths.java index df58dc90a3..af9e45a209 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Paths.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/Paths.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.LinkedHashMap; import java.util.Objects; @@ -68,6 +70,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/SpecVersion.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/SpecVersion.java new file mode 100644 index 0000000000..cea82fd5ce --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/SpecVersion.java @@ -0,0 +1,6 @@ +package io.swagger.v3.oas.models; + +public enum SpecVersion { + V30, + V31 +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/callbacks/Callback.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/callbacks/Callback.java index 53e6e0e076..cda3eb83e6 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/callbacks/Callback.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/callbacks/Callback.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.callbacks; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.PathItem; import java.util.LinkedHashMap; @@ -100,6 +101,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } @@ -113,6 +122,7 @@ public Callback extensions(java.util.Map extensions) { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Callback {\n"); + sb.append(" $ref: ").append(toIndentedString($ref)).append("\n"); sb.append(" ").append(toIndentedString(super.toString())).append("\n"); sb.append("}"); return sb.toString(); diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/examples/Example.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/examples/Example.java index d6c5765cb4..211ddbabf9 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/examples/Example.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/examples/Example.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.examples; +import io.swagger.v3.oas.annotations.OpenAPI31; + /** * Example */ @@ -137,6 +139,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Contact.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Contact.java index bc20f57d66..172988ade2 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Contact.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Contact.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.info; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -121,6 +123,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Info.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Info.java index 9c6f5e3021..31193b8590 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Info.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/Info.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.info; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -31,6 +33,12 @@ public class Info { private String version = null; private java.util.Map extensions = null; + /** + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + private String summary = null; + /** * returns the title property from a Info instance. * @@ -145,6 +153,28 @@ public Info version(String version) { return this; } + /** + * returns the summary property from a Info instance. + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return String + **/ + @OpenAPI31 + public String getSummary() { + return summary; + } + + @OpenAPI31 + public void setSummary(String summary) { + this.summary = summary; + } + + @OpenAPI31 + public Info summary(String summary) { + this.summary = summary; + return this; + } + @Override public boolean equals(java.lang.Object o) { if (this == o) { @@ -160,12 +190,13 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.contact, info.contact) && Objects.equals(this.license, info.license) && Objects.equals(this.version, info.version) && - Objects.equals(this.extensions, info.extensions); + Objects.equals(this.extensions, info.extensions) && + Objects.equals(this.summary, info.summary); } @Override public int hashCode() { - return Objects.hash(title, description, termsOfService, contact, license, version, extensions); + return Objects.hash(title, description, termsOfService, contact, license, version, extensions, summary); } public java.util.Map getExtensions() { @@ -182,6 +213,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } @@ -198,6 +237,7 @@ public String toString() { sb.append(" title: ").append(toIndentedString(title)).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" summary: ").append(toIndentedString(summary)).append("\n"); sb.append(" termsOfService: ").append(toIndentedString(termsOfService)).append("\n"); sb.append(" contact: ").append(toIndentedString(contact)).append("\n"); sb.append(" license: ").append(toIndentedString(license)).append("\n"); diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/License.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/License.java index 0bc59fc0f2..f403e54e50 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/License.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/info/License.java @@ -16,17 +16,26 @@ package io.swagger.v3.oas.models.info; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** * License * * @see "https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#licenseObject" + * @see "https://github.com/OAI/OpenAPI-Specification/blob/3.1.0/versions/3.1.0.md#licenseObject" */ public class License { private String name = null; private String url = null; + + /** + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + private String identifier = null; private java.util.Map extensions = null; /** @@ -67,8 +76,28 @@ public License url(String url) { return this; } + /** + * returns the identifier property from a License instance. + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return String identifier + **/ + @OpenAPI31 + public String getIdentifier() { + return identifier; + } + @OpenAPI31 + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + @OpenAPI31 + public License identifier(String identifier) { + this.identifier = identifier; + return this; + } + @Override - public boolean equals(java.lang.Object o) { + public boolean equals(Object o) { if (this == o) { return true; } @@ -78,12 +107,13 @@ public boolean equals(java.lang.Object o) { License license = (License) o; return Objects.equals(this.name, license.name) && Objects.equals(this.url, license.url) && + Objects.equals(this.identifier, license.identifier) && Objects.equals(this.extensions, license.extensions); } @Override public int hashCode() { - return Objects.hash(name, url, extensions); + return Objects.hash(name, url, identifier, extensions); } public java.util.Map getExtensions() { @@ -100,6 +130,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } @@ -116,6 +154,7 @@ public String toString() { sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" identifier: ").append(toIndentedString(identifier)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/Link.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/Link.java index 15287b7d3f..aa2f75430c 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/Link.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/Link.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.links; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.headers.Header; import io.swagger.v3.oas.models.servers.Server; @@ -123,12 +124,16 @@ public void setParameters(Map parameters) { this.parameters = parameters; } + @Deprecated public Link parameters(String name, String parameter) { + return this.addParameter(name, parameter); + } + + public Link addParameter(String name, String parameter) { if (this.parameters == null) { this.parameters = new LinkedHashMap<>(); } this.parameters.put(name, parameter); - return this; } @@ -261,6 +266,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/LinkParameter.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/LinkParameter.java index 1689386a29..0b61f15a80 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/LinkParameter.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/links/LinkParameter.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.links; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -77,6 +79,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ArraySchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ArraySchema.java index 5dacdb04a7..9ef004bee7 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ArraySchema.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ArraySchema.java @@ -16,14 +16,11 @@ package io.swagger.v3.oas.models.media; -import java.util.Objects; - /** * ArraySchema */ public class ArraySchema extends Schema { - private Schema items = null; public ArraySchema() { super("array", null); @@ -35,41 +32,10 @@ public ArraySchema type(String type) { return this; } - /** - * returns the items property from a ArraySchema instance. - * - * @return Schema items - **/ - - public Schema getItems() { - return items; - } - - public void setItems(Schema items) { - this.items = items; - } - - public ArraySchema items(Schema items) { - this.items = items; - return this; - } - - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ArraySchema arraySchema = (ArraySchema) o; - return Objects.equals(this.items, arraySchema.items) && - super.equals(o); - } - @Override - public int hashCode() { - return Objects.hash(items, super.hashCode()); + public ArraySchema items(Schema items) { + super.setItems(items); + return this; } @Override @@ -77,7 +43,6 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class ArraySchema {\n"); sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - sb.append(" items: ").append(toIndentedString(items)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/BooleanSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/BooleanSchema.java index 6aa319a557..9d226b6cee 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/BooleanSchema.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/BooleanSchema.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; /** * BooleanSchema @@ -36,6 +37,13 @@ public BooleanSchema type(String type) { return this; } + @Override + public BooleanSchema types(Set types) { + super.setTypes(types); + return this; + } + + public BooleanSchema _default(Boolean _default) { super.setDefault(_default); return this; diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ComposedSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ComposedSchema.java index 530a7700d3..164a90866a 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ComposedSchema.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ComposedSchema.java @@ -16,128 +16,18 @@ package io.swagger.v3.oas.models.media; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - /** * ComposedSchema */ public class ComposedSchema extends Schema { - private List allOf = null; - private List anyOf = null; - private List oneOf = null; - - /** - * returns the allOf property from a ComposedSchema instance. - * - * @return List<Schema> allOf - **/ - - public List getAllOf() { - return allOf; - } - - public void setAllOf(List allOf) { - this.allOf = allOf; - } - - public ComposedSchema allOf(List allOf) { - this.allOf = allOf; - return this; - } - - public ComposedSchema addAllOfItem(Schema allOfItem) { - if (this.allOf == null) { - this.allOf = new ArrayList<>(); - } - this.allOf.add(allOfItem); - return this; - } - - /** - * returns the anyOf property from a ComposedSchema instance. - * - * @return List<Schema> anyOf - **/ - - public List getAnyOf() { - return anyOf; - } - - public void setAnyOf(List anyOf) { - this.anyOf = anyOf; - } - - public ComposedSchema anyOf(List anyOf) { - this.anyOf = anyOf; - return this; - } - public ComposedSchema addAnyOfItem(Schema anyOfItem) { - if (this.anyOf == null) { - this.anyOf = new ArrayList<>(); - } - this.anyOf.add(anyOfItem); - return this; - } - - /** - * returns the oneOf property from a ComposedSchema instance. - * - * @return List<Schema> oneOf - **/ - - public List getOneOf() { - return oneOf; - } - - public void setOneOf(List oneOf) { - this.oneOf = oneOf; - } - - public ComposedSchema oneOf(List oneOf) { - this.oneOf = oneOf; - return this; - } - - public ComposedSchema addOneOfItem(Schema oneOfItem) { - if (this.oneOf == null) { - this.oneOf = new ArrayList<>(); - } - this.oneOf.add(oneOfItem); - return this; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ComposedSchema allOfSchema = (ComposedSchema) o; - return Objects.equals(this.allOf, allOfSchema.allOf) && - Objects.equals(this.anyOf, allOfSchema.anyOf) && - Objects.equals(this.oneOf, allOfSchema.oneOf) && - super.equals(o); - } - - @Override - public int hashCode() { - return Objects.hash(allOf, anyOf, oneOf, super.hashCode()); - } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class ComposedSchema {\n"); sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - sb.append(" allOf: ").append(toIndentedString(allOf)).append("\n"); - sb.append(" anyOf: ").append(toIndentedString(anyOf)).append("\n"); - sb.append(" oneOf: ").append(toIndentedString(oneOf)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Discriminator.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Discriminator.java index cda3f65f2c..b3b09ef4c2 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Discriminator.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Discriminator.java @@ -1,12 +1,21 @@ package io.swagger.v3.oas.models.media; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; public class Discriminator { private String propertyName; private Map mapping; + /** + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + private Map extensions; + public Discriminator propertyName(String propertyName) { this.propertyName = propertyName; return this; @@ -41,6 +50,36 @@ public void setMapping(Map mapping) { this.mapping = mapping; } + /** + * returns the specific extensions from a Discriminator instance. + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return Map<String, Object> extensions + **/ + @OpenAPI31 + public Map getExtensions() { + return extensions; + } + + @OpenAPI31 + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + @OpenAPI31 + public void addExtension(String name, Object value) { + if (name == null || name.isEmpty() || !name.startsWith("x-")) { + return; + } + if (name.startsWith("x-oas-") || name.startsWith("x-oai-")) { + return; + } + if (this.extensions == null) { + this.extensions = new java.util.LinkedHashMap<>(); + } + this.extensions.put(name, value); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -55,15 +94,16 @@ public boolean equals(Object o) { if (propertyName != null ? !propertyName.equals(that.propertyName) : that.propertyName != null) { return false; } + if (extensions != null ? !extensions.equals(that.extensions) : that.extensions != null) { + return false; + } return mapping != null ? mapping.equals(that.mapping) : that.mapping == null; } @Override public int hashCode() { - int result = propertyName != null ? propertyName.hashCode() : 0; - result = 31 * result + (mapping != null ? mapping.hashCode() : 0); - return result; + return Objects.hash(propertyName, mapping, extensions); } @Override @@ -71,6 +111,7 @@ public String toString() { return "Discriminator{" + "propertyName='" + propertyName + '\'' + ", mapping=" + mapping + + ", extensions=" + extensions + '}'; } } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Encoding.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Encoding.java index 110b169f1c..5ca9e45d47 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Encoding.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Encoding.java @@ -16,8 +16,10 @@ package io.swagger.v3.oas.models.media; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.headers.Header; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -91,6 +93,14 @@ public void setHeaders(Map headers) { this.headers = headers; } + public Encoding addHeader(String name, Header header) { + if (this.headers == null) { + this.headers = new LinkedHashMap<>(); + } + this.headers.put(name, header); + return this; + } + public Encoding style(StyleEnum style) { this.style = style; return this; @@ -144,6 +154,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/EncodingProperty.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/EncodingProperty.java index ba7fd89c89..ad45604a64 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/EncodingProperty.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/EncodingProperty.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.media; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.headers.Header; import java.util.LinkedHashMap; @@ -200,6 +201,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/JsonSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/JsonSchema.java new file mode 100644 index 0000000000..065a3becdb --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/JsonSchema.java @@ -0,0 +1,39 @@ +/** + * 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.models.media; + +import io.swagger.v3.oas.models.SpecVersion; + +/** + * JsonSchema + */ + +public class JsonSchema extends Schema { + + public JsonSchema (){ + specVersion(SpecVersion.V31); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class JsonSchema {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append("}"); + return sb.toString(); + } +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/MediaType.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/MediaType.java index 3d7b8aa1b1..0b3800e45d 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/MediaType.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/MediaType.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.media; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.examples.Example; import java.util.LinkedHashMap; @@ -180,6 +181,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java index e3d9097578..626a2dfd98 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/Schema.java @@ -17,32 +17,41 @@ package io.swagger.v3.oas.models.media; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.OpenAPI30; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.SpecVersion; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * Schema * * @see "https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#schemaObject" + * @see "https://github.com/OAI/OpenAPI-Specification/blob/3.1.0/versions/3.1.0.md#schemaObject" */ public class Schema { + protected T _default; private String name; private String title = null; private BigDecimal multipleOf = null; private BigDecimal maximum = null; + @OpenAPI30 private Boolean exclusiveMaximum = null; private BigDecimal minimum = null; + @OpenAPI30 private Boolean exclusiveMinimum = null; private Integer maxLength = null; private Integer minLength = null; @@ -60,6 +69,7 @@ public class Schema { private String description = null; private String format = null; private String $ref = null; + @OpenAPI30 private Boolean nullable = null; private Boolean readOnly = null; private Boolean writeOnly = null; @@ -72,6 +82,394 @@ public class Schema { private Discriminator discriminator = null; private boolean exampleSetFlag; + @OpenAPI31 + private List prefixItems = null; + private List allOf = null; + private List anyOf = null; + private List oneOf = null; + + private Schema items = null; + + protected T _const; + + private SpecVersion specVersion = SpecVersion.V30; + + @JsonIgnore + public SpecVersion getSpecVersion() { + return this.specVersion; + } + + public void setSpecVersion(SpecVersion specVersion) { + this.specVersion = specVersion; + } + + public Schema specVersion(SpecVersion specVersion) { + this.setSpecVersion(specVersion); + return this; + } + + + /* + @OpenAPI31 fields and accessors + */ + + + @OpenAPI31 + private Set types; + + @OpenAPI31 + private Map patternProperties = null; + @OpenAPI31 + private BigDecimal exclusiveMaximumValue = null; + @OpenAPI31 + private BigDecimal exclusiveMinimumValue = null; + + + @OpenAPI31 + private Schema contains = null; + @OpenAPI31 + private String $id; + @OpenAPI31 + private String $schema; + @OpenAPI31 + private String $anchor; + + @OpenAPI31 + private String contentEncoding; + @OpenAPI31 + private String contentMediaType; + @OpenAPI31 + private Schema contentSchema; + @OpenAPI31 + private Schema propertyNames; + @OpenAPI31 + private Object unevaluatedProperties; + @OpenAPI31 + private Integer maxContains; + @OpenAPI31 + private Integer minContains; + @OpenAPI31 + private Schema additionalItems; + @OpenAPI31 + private Schema unevaluatedItems; + @OpenAPI31 + private Schema _if; + @OpenAPI31 + private Schema _else; + @OpenAPI31 + private Schema then; + @OpenAPI31 + private Map dependentSchemas; + @OpenAPI31 + private Map> dependentRequired; + @OpenAPI31 + private String $comment; + @OpenAPI31 + private List examples; + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getContains() { + return contains; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setContains(Schema contains) { + this.contains = contains; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public String get$id() { + return $id; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void set$id(String $id) { + this.$id = $id; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public String get$schema() { + return $schema; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void set$schema(String $schema) { + this.$schema = $schema; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public String get$anchor() { + return $anchor; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void set$anchor(String $anchor) { + this.$anchor = $anchor; + } + + /** + * returns the exclusiveMaximumValue property from a Schema instance for OpenAPI 3.1.x + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return BigDecimal exclusiveMaximumValue + * + **/ + @OpenAPI31 + public BigDecimal getExclusiveMaximumValue() { + return exclusiveMaximumValue; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setExclusiveMaximumValue(BigDecimal exclusiveMaximumValue) { + this.exclusiveMaximumValue = exclusiveMaximumValue; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema exclusiveMaximumValue(BigDecimal exclusiveMaximumValue) { + this.exclusiveMaximumValue = exclusiveMaximumValue; + return this; + } + + /** + * returns the exclusiveMinimumValue property from a Schema instance for OpenAPI 3.1.x + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return BigDecimal exclusiveMinimumValue + * + **/ + @OpenAPI31 + public BigDecimal getExclusiveMinimumValue() { + return exclusiveMinimumValue; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setExclusiveMinimumValue(BigDecimal exclusiveMinimumValue) { + this.exclusiveMinimumValue = exclusiveMinimumValue; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema exclusiveMinimumValue(BigDecimal exclusiveMinimumValue) { + this.exclusiveMinimumValue = exclusiveMinimumValue; + return this; + } + + /** + * returns the patternProperties property from a Schema instance. + * + * @since 2.2.0 (OpenAPI 3.1.0) + * @return Map<String, Schema> patternProperties + **/ + @OpenAPI31 + public Map getPatternProperties() { + return patternProperties; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setPatternProperties(Map patternProperties) { + this.patternProperties = patternProperties; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema patternProperties(Map patternProperties) { + this.patternProperties = patternProperties; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema addPatternProperty(String key, Schema patternPropertiesItem) { + if (this.patternProperties == null) { + this.patternProperties = new LinkedHashMap<>(); + } + this.patternProperties.put(key, patternPropertiesItem); + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema contains(Schema contains) { + this.contains = contains; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema $id(String $id) { + this.$id = $id; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Set getTypes() { + return types; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setTypes(Set types) { + this.types = types; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public boolean addType(String type) { + if (types == null) { + types = new LinkedHashSet<>(); + } + return types.add(type); + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema $schema(String $schema) { + this.$schema = $schema; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema $anchor(String $anchor) { + this.$anchor = $anchor; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema types(Set types) { + this.types = types; + return this; + } + + /* + INTERNAL MEMBERS @OpenAPI31 + */ + + @OpenAPI31 + protected Map jsonSchema = null; + + @OpenAPI31 + public Map getJsonSchema() { + return jsonSchema; + } + + @OpenAPI31 + public void setJsonSchema(Map jsonSchema) { + this.jsonSchema = jsonSchema; + } + + @OpenAPI31 + public Schema jsonSchema(Map jsonSchema) { + this.jsonSchema = jsonSchema; + return this; + } + + @OpenAPI31 + protected transient Object jsonSchemaImpl = null; + + @OpenAPI31 + public Object getJsonSchemaImpl() { + return jsonSchemaImpl; + } + + @OpenAPI31 + public void setJsonSchemaImpl(Object jsonSchemaImpl) { + this.jsonSchemaImpl = jsonSchemaImpl; + } + + @OpenAPI31 + public Schema jsonSchemaImpl(Object jsonSchemaImpl) { + setJsonSchemaImpl(jsonSchemaImpl); + return this; + } + + /* + CONSTRUCTORS + */ + public Schema() { } @@ -81,6 +479,122 @@ protected Schema(String type, String format) { this.format = format; } + public Schema(SpecVersion specVersion) { + this.specVersion = specVersion; + } + + protected Schema(String type, String format, SpecVersion specVersion) { + this.type = type; + this.format = format; + this.specVersion = specVersion; + } + + /* + ACCESSORS + */ + + /** + * returns the allOf property from a ComposedSchema instance. + * + * @return List<Schema> allOf + **/ + + public List getAllOf() { + return allOf; + } + + public void setAllOf(List allOf) { + this.allOf = allOf; + } + + public Schema allOf(List allOf) { + this.allOf = allOf; + return this; + } + + public Schema addAllOfItem(Schema allOfItem) { + if (this.allOf == null) { + this.allOf = new ArrayList<>(); + } + this.allOf.add(allOfItem); + return this; + } + + /** + * returns the anyOf property from a ComposedSchema instance. + * + * @return List<Schema> anyOf + **/ + + public List getAnyOf() { + return anyOf; + } + + public void setAnyOf(List anyOf) { + this.anyOf = anyOf; + } + + public Schema anyOf(List anyOf) { + this.anyOf = anyOf; + return this; + } + + public Schema addAnyOfItem(Schema anyOfItem) { + if (this.anyOf == null) { + this.anyOf = new ArrayList<>(); + } + this.anyOf.add(anyOfItem); + return this; + } + + /** + * returns the oneOf property from a ComposedSchema instance. + * + * @return List<Schema> oneOf + **/ + + public List getOneOf() { + return oneOf; + } + + public void setOneOf(List oneOf) { + this.oneOf = oneOf; + } + + public Schema oneOf(List oneOf) { + this.oneOf = oneOf; + return this; + } + + public Schema addOneOfItem(Schema oneOfItem) { + if (this.oneOf == null) { + this.oneOf = new ArrayList<>(); + } + this.oneOf.add(oneOfItem); + return this; + } + + + /** + * returns the items property from a ArraySchema instance. + * + * @return Schema items + **/ + + public Schema getItems() { + return items; + } + + public void setItems(Schema items) { + this.items = items; + } + + public Schema items(Schema items) { + this.items = items; + return this; + } + + /** * returns the name property from a from a Schema instance. Ignored in serialization. * @@ -139,7 +653,7 @@ public Schema title(String title) { } /** - * returns the _default property from a StringSchema instance. + * returns the _default property from a Schema instance. * * @return String _default **/ @@ -213,19 +727,21 @@ public Schema maximum(BigDecimal maximum) { } /** - * returns the exclusiveMaximum property from a Schema instance. + * returns the exclusiveMaximum property from a Schema instance for OpenAPI 3.0.x * * @return Boolean exclusiveMaximum **/ - + @OpenAPI30 public Boolean getExclusiveMaximum() { return exclusiveMaximum; } + @OpenAPI30 public void setExclusiveMaximum(Boolean exclusiveMaximum) { this.exclusiveMaximum = exclusiveMaximum; } + @OpenAPI30 public Schema exclusiveMaximum(Boolean exclusiveMaximum) { this.exclusiveMaximum = exclusiveMaximum; return this; @@ -250,8 +766,9 @@ public Schema minimum(BigDecimal minimum) { return this; } + /** - * returns the exclusiveMinimum property from a Schema instance. + * returns the exclusiveMinimum property from a Schema instance for OpenAPI 3.0.x * * @return Boolean exclusiveMinimum **/ @@ -269,6 +786,7 @@ public Schema exclusiveMinimum(Boolean exclusiveMinimum) { return this; } + /** * returns the maxLength property from a Schema instance. *

@@ -530,11 +1048,20 @@ public Schema properties(Map properties) { return this; } - public Schema addProperties(String key, Schema propertiesItem) { + @Deprecated + public Schema addProperties(String key, Schema property) { + return addProperty(key, property); + } + + /** + * + * @since 2.2.0 + */ + public Schema addProperty(String key, Schema property) { if (this.properties == null) { this.properties = new LinkedHashMap<>(); } - this.properties.put(key, propertiesItem); + this.properties.put(key, property); return this; } @@ -625,15 +1152,17 @@ public Schema format(String format) { * * @return Boolean nullable **/ - + @OpenAPI30 public Boolean getNullable() { return nullable; } + @OpenAPI30 public void setNullable(Boolean nullable) { this.nullable = nullable; } + @OpenAPI30 public Schema nullable(Boolean nullable) { this.nullable = nullable; return this; @@ -771,9 +1300,497 @@ public void setExampleSetFlag(boolean exampleSetFlag) { this.exampleSetFlag = exampleSetFlag; } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public List getPrefixItems() { + return prefixItems; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setPrefixItems(List prefixItems) { + this.prefixItems = prefixItems; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema prefixItems(List prefixItems) { + this.prefixItems = prefixItems; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public String getContentEncoding() { + return contentEncoding; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setContentEncoding(String contentEncoding) { + this.contentEncoding = contentEncoding; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema contentEncoding(String contentEncoding) { + this.contentEncoding = contentEncoding; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public String getContentMediaType() { + return contentMediaType; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setContentMediaType(String contentMediaType) { + this.contentMediaType = contentMediaType; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema contentMediaType(String contentMediaType) { + this.contentMediaType = contentMediaType; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getContentSchema() { + return contentSchema; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setContentSchema(Schema contentSchema) { + this.contentSchema = contentSchema; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema contentSchema(Schema contentSchema) { + this.contentSchema = contentSchema; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getPropertyNames() { + return propertyNames; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setPropertyNames(Schema propertyNames) { + this.propertyNames = propertyNames; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema propertyNames(Schema propertyNames) { + this.propertyNames = propertyNames; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Object getUnevaluatedProperties() { + return unevaluatedProperties; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setUnevaluatedProperties(Object unevaluatedProperties) { + this.unevaluatedProperties = unevaluatedProperties; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema unevaluatedProperties(Object unevaluatedProperties) { + this.unevaluatedProperties = unevaluatedProperties; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Integer getMaxContains() { + return maxContains; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setMaxContains(Integer maxContains) { + this.maxContains = maxContains; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema maxContains(Integer maxContains) { + this.maxContains = maxContains; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Integer getMinContains() { + return minContains; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setMinContains(Integer minContains) { + this.minContains = minContains; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema minContains(Integer minContains) { + this.minContains = minContains; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getAdditionalItems() { + return additionalItems; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setAdditionalItems(Schema additionalItems) { + this.additionalItems = additionalItems; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema additionalItems(Schema additionalItems) { + this.additionalItems = additionalItems; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getUnevaluatedItems() { + return unevaluatedItems; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setUnevaluatedItems(Schema unevaluatedItems) { + this.unevaluatedItems = unevaluatedItems; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema unevaluatedItems(Schema unevaluatedItems) { + this.unevaluatedItems = unevaluatedItems; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getIf() { + return _if; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setIf(Schema _if) { + this._if = _if; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema _if(Schema _if) { + this._if = _if; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getElse() { + return _else; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setElse(Schema _else) { + this._else = _else; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema _else(Schema _else) { + this._else = _else; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema getThen() { + return then; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setThen(Schema then) { + this.then = then; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema then(Schema then) { + this.then = then; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Map getDependentSchemas() { + return dependentSchemas; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setDependentSchemas(Map dependentSchemas) { + this.dependentSchemas = dependentSchemas; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema dependentSchemas(Map dependentSchemas) { + this.dependentSchemas = dependentSchemas; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Map> getDependentRequired() { + return dependentRequired; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setDependentRequired(Map> dependentRequired) { + this.dependentRequired = dependentRequired; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema dependentRequired(Map> dependentRequired) { + this.dependentRequired = dependentRequired; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public String get$comment() { + return $comment; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void set$comment(String $comment) { + this.$comment = $comment; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema $comment(String $comment) { + this.$comment = $comment; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public List getExamples() { + return examples; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void setExamples(List examples) { + this.examples = examples; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public Schema examples(List examples) { + this.examples = examples; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + @OpenAPI31 + public void addExample(T example) { + if (this.examples == null) { + this.examples = new ArrayList<>(); + } + this.examples.add(example); + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { @@ -784,8 +1801,10 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.multipleOf, schema.multipleOf) && Objects.equals(this.maximum, schema.maximum) && Objects.equals(this.exclusiveMaximum, schema.exclusiveMaximum) && + Objects.equals(this.exclusiveMaximumValue, schema.exclusiveMaximumValue) && Objects.equals(this.minimum, schema.minimum) && Objects.equals(this.exclusiveMinimum, schema.exclusiveMinimum) && + Objects.equals(this.exclusiveMinimumValue, schema.exclusiveMinimumValue) && Objects.equals(this.maxLength, schema.maxLength) && Objects.equals(this.minLength, schema.minLength) && Objects.equals(this.pattern, schema.pattern) && @@ -812,14 +1831,48 @@ public boolean equals(java.lang.Object o) { Objects.equals(this.extensions, schema.extensions) && Objects.equals(this.discriminator, schema.discriminator) && Objects.equals(this._enum, schema._enum) && - Objects.equals(this._default, schema._default); + Objects.equals(this.contains, schema.contains) && + Objects.equals(this.patternProperties, schema.patternProperties) && + Objects.equals(this.$id, schema.$id) && + Objects.equals(this.$anchor, schema.$anchor) && + Objects.equals(this.$schema, schema.$schema) && + Objects.equals(this.types, schema.types) && + Objects.equals(this.allOf, schema.allOf) && + Objects.equals(this.anyOf, schema.anyOf) && + Objects.equals(this.oneOf, schema.oneOf) && + Objects.equals(this._const, schema._const) && + Objects.equals(this._default, schema._default) && + Objects.equals(this.contentEncoding, schema.contentEncoding) && + Objects.equals(this.contentMediaType, schema.contentMediaType) && + Objects.equals(this.contentSchema, schema.contentSchema) && + Objects.equals(this.propertyNames, schema.propertyNames) && + Objects.equals(this.unevaluatedProperties, schema.unevaluatedProperties) && + Objects.equals(this.maxContains, schema.maxContains) && + Objects.equals(this.minContains, schema.minContains) && + Objects.equals(this.additionalItems, schema.additionalItems) && + Objects.equals(this.unevaluatedItems, schema.unevaluatedItems) && + Objects.equals(this._if, schema._if) && + Objects.equals(this._else, schema._else) && + Objects.equals(this.then, schema.then) && + Objects.equals(this.dependentRequired, schema.dependentRequired) && + Objects.equals(this.dependentSchemas, schema.dependentSchemas) && + Objects.equals(this.$comment, schema.$comment) && + Objects.equals(this.examples, schema.examples) && + Objects.equals(this.prefixItems, schema.prefixItems) + + ; } @Override public int hashCode() { - return Objects.hash(title, multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum, maxLength, minLength, pattern, maxItems, - minItems, uniqueItems, maxProperties, minProperties, required, type, not, properties, additionalProperties, description, format, $ref, - nullable, readOnly, writeOnly, example, externalDocs, deprecated, xml, extensions, discriminator, _enum, _default); + return Objects.hash(title, multipleOf, maximum, exclusiveMaximum, exclusiveMaximumValue, minimum, + exclusiveMinimum, exclusiveMinimumValue, maxLength, minLength, pattern, maxItems, minItems, uniqueItems, + maxProperties, minProperties, required, type, not, properties, additionalProperties, description, + format, $ref, nullable, readOnly, writeOnly, example, externalDocs, deprecated, xml, extensions, + discriminator, _enum, _default, patternProperties, $id, $anchor, $schema, types, allOf, anyOf, oneOf, _const, + contentEncoding, contentMediaType, contentSchema, propertyNames, unevaluatedProperties, maxContains, + minContains, additionalItems, unevaluatedItems, _if, _else, then, dependentRequired, dependentSchemas, + $comment, examples, prefixItems); } public java.util.Map getExtensions() { @@ -827,7 +1880,7 @@ public java.util.Map getExtensions() { } public void addExtension(String name, Object value) { - if (name == null || name.isEmpty() || !name.startsWith("x-")) { + if (name == null || name.isEmpty() || (specVersion == SpecVersion.V30 && !name.startsWith("x-"))) { return; } if (this.extensions == null) { @@ -849,16 +1902,19 @@ public Schema extensions(java.util.Map extensions) { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Schema {\n"); - sb.append(" type: ").append(toIndentedString(type)).append("\n"); + Object typeStr = specVersion == SpecVersion.V30 ? type : types; + sb.append(" type: ").append(toIndentedString(typeStr)).append("\n"); sb.append(" format: ").append(toIndentedString(format)).append("\n"); sb.append(" $ref: ").append(toIndentedString($ref)).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); sb.append(" title: ").append(toIndentedString(title)).append("\n"); sb.append(" multipleOf: ").append(toIndentedString(multipleOf)).append("\n"); sb.append(" maximum: ").append(toIndentedString(maximum)).append("\n"); - sb.append(" exclusiveMaximum: ").append(toIndentedString(exclusiveMaximum)).append("\n"); + Object exclusiveMaximumStr = specVersion == SpecVersion.V30 ? exclusiveMaximum : exclusiveMaximumValue; + sb.append(" exclusiveMaximum: ").append(toIndentedString(exclusiveMaximumStr)).append("\n"); sb.append(" minimum: ").append(toIndentedString(minimum)).append("\n"); - sb.append(" exclusiveMinimum: ").append(toIndentedString(exclusiveMinimum)).append("\n"); + Object exclusiveMinimumStr = specVersion == SpecVersion.V30 ? exclusiveMinimum : exclusiveMinimumValue; + sb.append(" exclusiveMinimum: ").append(toIndentedString(exclusiveMinimumStr)).append("\n"); sb.append(" maxLength: ").append(toIndentedString(maxLength)).append("\n"); sb.append(" minLength: ").append(toIndentedString(minLength)).append("\n"); sb.append(" pattern: ").append(toIndentedString(pattern)).append("\n"); @@ -879,6 +1935,30 @@ public String toString() { sb.append(" deprecated: ").append(toIndentedString(deprecated)).append("\n"); sb.append(" discriminator: ").append(toIndentedString(discriminator)).append("\n"); sb.append(" xml: ").append(toIndentedString(xml)).append("\n"); + if (specVersion == SpecVersion.V31) { + sb.append(" patternProperties: ").append(toIndentedString(patternProperties)).append("\n"); + sb.append(" contains: ").append(toIndentedString(contains)).append("\n"); + sb.append(" $id: ").append(toIndentedString($id)).append("\n"); + sb.append(" $anchor: ").append(toIndentedString($anchor)).append("\n"); + sb.append(" $schema: ").append(toIndentedString($schema)).append("\n"); + sb.append(" const: ").append(toIndentedString(_const)).append("\n"); + sb.append(" contentEncoding: ").append(toIndentedString(contentEncoding)).append("\n"); + sb.append(" contentMediaType: ").append(toIndentedString(contentMediaType)).append("\n"); + sb.append(" contentSchema: ").append(toIndentedString(contentSchema)).append("\n"); + sb.append(" propertyNames: ").append(toIndentedString(propertyNames)).append("\n"); + sb.append(" unevaluatedProperties: ").append(toIndentedString(unevaluatedProperties)).append("\n"); + sb.append(" maxContains: ").append(toIndentedString(maxContains)).append("\n"); + sb.append(" minContains: ").append(toIndentedString(minContains)).append("\n"); + sb.append(" additionalItems: ").append(toIndentedString(additionalItems)).append("\n"); + sb.append(" unevaluatedItems: ").append(toIndentedString(unevaluatedItems)).append("\n"); + sb.append(" _if: ").append(toIndentedString(_if)).append("\n"); + sb.append(" _else: ").append(toIndentedString(_else)).append("\n"); + sb.append(" then: ").append(toIndentedString(then)).append("\n"); + sb.append(" dependentRequired: ").append(toIndentedString(dependentRequired)).append("\n"); + sb.append(" dependentSchemas: ").append(toIndentedString(dependentSchemas)).append("\n"); + sb.append(" $comment: ").append(toIndentedString($comment)).append("\n"); + sb.append(" prefixItems: ").append(toIndentedString(prefixItems)).append("\n"); + } sb.append("}"); return sb.toString(); } @@ -894,5 +1974,44 @@ protected String toIndentedString(java.lang.Object o) { return o.toString().replace("\n", "\n "); } + public Schema _default(T _default) { + this._default = _default; + return this; + } + + public Schema _enum(List _enum) { + this._enum = _enum; + return this; + } + + public Schema exampleSetFlag(boolean exampleSetFlag) { + this.exampleSetFlag = exampleSetFlag; + return this; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + public T getConst() { + return _const; + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + public void setConst(Object _const) { + this._const = cast(_const); + } + + /** + * + * @since 2.2.0 (OpenAPI 3.1.0) + */ + public Schema _const(Object _const) { + this._const = cast(_const); + return this; + } } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/XML.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/XML.java index a9c646b793..5cb50938a5 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/XML.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/XML.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.media; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -163,6 +165,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/Parameter.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/Parameter.java index 1f62a98c66..b6d56a3566 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/Parameter.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/Parameter.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.parameters; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.examples.Example; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.Schema; @@ -391,6 +392,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/RequestBody.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/RequestBody.java index 03ea392fdd..947eaabf9a 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/RequestBody.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/parameters/RequestBody.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.parameters; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.media.Content; /** @@ -102,6 +103,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponse.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponse.java index 072820b7c0..cc6087a0a3 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponse.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponse.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.responses; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.headers.Header; import io.swagger.v3.oas.models.links.Link; import io.swagger.v3.oas.models.media.Content; @@ -109,15 +110,20 @@ public ApiResponse content(Content content) { * @return Link links **/ - public java.util.Map getLinks() { + public Map getLinks() { return links; } - public void setLinks(java.util.Map links) { + public void setLinks(Map links) { this.links = links; } - public ApiResponse link(String name, Link link) { + public ApiResponse links(Map links) { + this.links = links; + return this; + } + + public ApiResponse addLink(String name, Link link) { if (this.links == null) { this.links = new LinkedHashMap<>(); } @@ -125,6 +131,10 @@ public ApiResponse link(String name, Link link) { return this; } + public ApiResponse link(String name, Link link) { + return this.addLink(name, link); + } + /** * returns the $ref property from an ApiResponse instance. * @@ -182,6 +192,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponses.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponses.java index 4a4a807e13..03c2a4c18e 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponses.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/responses/ApiResponses.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.responses; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.LinkedHashMap; import java.util.Objects; @@ -69,6 +71,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlow.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlow.java index 30802c6b7d..0c2329e730 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlow.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlow.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.security; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -142,6 +144,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlows.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlows.java index a9d5b9470b..7d31965273 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlows.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/OAuthFlows.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.security; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -142,6 +144,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/SecurityScheme.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/SecurityScheme.java index 184ce97ba4..55c2aae5c7 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/SecurityScheme.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/security/SecurityScheme.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.security; +import io.swagger.v3.oas.annotations.OpenAPI31; + /** * SecurityScheme * @@ -30,7 +32,8 @@ public enum Type { APIKEY("apiKey"), HTTP("http"), OAUTH2("oauth2"), - OPENIDCONNECT("openIdConnect"); + OPENIDCONNECT("openIdConnect"), + MUTUALTLS("mutualTLS"); private String value; @@ -234,6 +237,14 @@ public java.util.Map getExtensions() { return extensions; } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void addExtension(String name, Object value) { if (name == null || name.isEmpty() || !name.startsWith("x-")) { return; diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/Server.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/Server.java index c6bc0da7a8..5ada32920c 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/Server.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/Server.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.servers; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.Objects; /** @@ -121,6 +123,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/ServerVariable.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/ServerVariable.java index 53772d3f2d..aeff3dbaf2 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/ServerVariable.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/servers/ServerVariable.java @@ -16,6 +16,8 @@ package io.swagger.v3.oas.models.servers; +import io.swagger.v3.oas.annotations.OpenAPI31; + import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -131,6 +133,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/tags/Tag.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/tags/Tag.java index f727c1c305..1e5cc6a041 100644 --- a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/tags/Tag.java +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/tags/Tag.java @@ -16,6 +16,7 @@ package io.swagger.v3.oas.models.tags; +import io.swagger.v3.oas.annotations.OpenAPI31; import io.swagger.v3.oas.models.ExternalDocumentation; import java.util.Objects; @@ -123,6 +124,14 @@ public void addExtension(String name, Object value) { this.extensions.put(name, value); } + @OpenAPI31 + public void addExtension31(String name, Object value) { + if (name != null && (name.startsWith("x-oas-") || name.startsWith("x-oai-"))) { + return; + } + addExtension(name, value); + } + public void setExtensions(java.util.Map extensions) { this.extensions = extensions; } diff --git a/modules/swagger-models/src/test/java/io/swagger/test/SimpleBuilderTest.java b/modules/swagger-models/src/test/java/io/swagger/test/SimpleBuilderTest.java index 2dd5a6b8fa..45f82dbea1 100644 --- a/modules/swagger-models/src/test/java/io/swagger/test/SimpleBuilderTest.java +++ b/modules/swagger-models/src/test/java/io/swagger/test/SimpleBuilderTest.java @@ -112,7 +112,7 @@ public void testBuilder() throws Exception { new MediaType().schema(new Schema() .$ref("#/components/schemas/Address"))) ) - .link("funky", new Link() + .addLink("funky", new Link() .operationId("getFunky"))) ) ) diff --git a/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml index 19da802b42..5cd77e475d 100644 --- a/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-annotations-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml index 6dd67914cd..6631112496 100644 --- a/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-core-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml index e25228c533..8acb8c809b 100644 --- a/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-integration-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml index d537aa8a3d..5e3fd214a1 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml index 442c93cddc..d05e5ffe66 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml index f79667d5a4..e40be7b1c0 100644 --- a/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-jaxrs2-servlet-initializer-v2-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml index 71c3b55b45..d0f8a86c7b 100644 --- a/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-maven-plugin-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 @@ -87,7 +87,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.6.0 + 3.6.4 true @@ -140,7 +140,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.6.0 + 3.6.4 provided @@ -175,6 +175,6 @@ UTF-8 - 3.6.3 + 3.8.4 diff --git a/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml b/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml index f26174a411..85ad10f7fd 100644 --- a/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/modules/swagger-models-jakarta/pom.xml @@ -4,7 +4,7 @@ io.swagger.core.v3 swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-project-jakarta/pom.xml b/modules/swagger-project-jakarta/pom.xml index 7d361e6fb9..4c3946e611 100644 --- a/modules/swagger-project-jakarta/pom.xml +++ b/modules/swagger-project-jakarta/pom.xml @@ -6,7 +6,7 @@ pom swagger-project-jakarta swagger-project-jakarta - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT https://github.com/swagger-api/swagger-core scm:git:git@github.com:swagger-api/swagger-core.git @@ -589,7 +589,7 @@ UTF-8 https://oss.sonatype.org/content/repositories/snapshots/ - 3.6.3 + 3.8.4 1.21 diff --git a/mvnw b/mvnw index 34d9dae8d0..d1e14b0eef 100755 --- a/mvnw +++ b/mvnw @@ -212,9 +212,9 @@ else echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" fi while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; @@ -246,7 +246,7 @@ else else curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f fi - + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" diff --git a/mvnw.cmd b/mvnw.cmd index 77b451d837..6925114e69 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -120,7 +120,7 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/mavrapper-0.5.6.jar" FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B @@ -132,11 +132,11 @@ if exist %WRAPPER_JAR% ( echo Found %WRAPPER_JAR% ) else ( if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" ) echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %DOWNLOAD_URL% - + powershell -Command "&{"^ "$webclient = new-object System.Net.WebClient;"^ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ diff --git a/pom.xml b/pom.xml index 1ab5b0ad4c..62f7e0893a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ pom swagger-project swagger-project - 2.1.14-SNAPSHOT + 2.2.0-SNAPSHOT https://github.com/swagger-api/swagger-core scm:git:git@github.com:swagger-api/swagger-core.git @@ -88,21 +88,6 @@ target ${project.artifactId}-${project.version} - - org.owasp - dependency-check-maven - 6.5.3 - - true - - - - - check - - - - maven-compiler-plugin 3.8.1 @@ -369,6 +354,28 @@ + + security + + + + org.owasp + dependency-check-maven + 6.5.3 + + true + + + + + check + + + + + + + release