diff --git a/CHANGELOG.md b/CHANGELOG.md
index e8e3f96e0..6aa47ee35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.8.2] - 2025-01-12
+
+### Added
+- #2849 - Provide better compatibility for projects migrating from OAS 3.0 to OAS 3.1
+
+### Fixed
+- #2846 - ClassCastException with spring-data-rest and openapi version 3.1 bug
+- #2844 - PageableObject and SortObject are called Pageablenull and Sortnull i
+
## [2.8.1] - 2025-01-06
### Fixed
diff --git a/pom.xml b/pom.xml
index 3bb41f32d..ea992c2c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.springdoc
springdoc-openapi
- 2.8.1
+ 2.8.2
pom
Spring openapi documentation
Spring openapi documentation
@@ -35,7 +35,7 @@
scm:git:git@github.com:springdoc/springdoc-openapi.git
scm:git:git@github.com:springdoc/springdoc-openapi.git
- v2.8.1
+ v2.8.2
diff --git a/springdoc-openapi-starter-common/pom.xml b/springdoc-openapi-starter-common/pom.xml
index 59e6527a2..a355db7d5 100644
--- a/springdoc-openapi-starter-common/pom.xml
+++ b/springdoc-openapi-starter-common/pom.xml
@@ -3,7 +3,7 @@
org.springdoc
springdoc-openapi
- 2.8.1
+ 2.8.2
springdoc-openapi-starter-common
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java
index d69abcb72..4c39763c9 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
@@ -52,6 +52,7 @@
import org.springdoc.core.converters.AdditionalModelsConverter;
import org.springdoc.core.converters.FileSupportConverter;
import org.springdoc.core.converters.ModelConverterRegistrar;
+import org.springdoc.core.converters.OAS31ModelConverter;
import org.springdoc.core.converters.PolymorphicModelConverter;
import org.springdoc.core.converters.PropertyCustomizingConverter;
import org.springdoc.core.converters.ResponseSupportConverter;
@@ -597,14 +598,14 @@ public SpringDocCustomizers springDocCustomizers(Optional
Optional> operationCustomizers,
Optional> routerOperationCustomizers,
Optional> dataRestRouterOperationCustomizers,
- Optional> methodFilters, Optional> globalOpenApiCustomizers,
+ Optional> methodFilters, Optional> globalOpenApiCustomizers,
Optional> globalOperationCustomizers,
- Optional> globalOpenApiMethodFilters){
+ Optional> globalOpenApiMethodFilters) {
return new SpringDocCustomizers(openApiCustomizers,
operationCustomizers,
- routerOperationCustomizers,
- dataRestRouterOperationCustomizers,
- methodFilters, globalOpenApiCustomizers, globalOperationCustomizers, globalOpenApiMethodFilters);
+ routerOperationCustomizers,
+ dataRestRouterOperationCustomizers,
+ methodFilters, globalOpenApiCustomizers, globalOperationCustomizers, globalOpenApiMethodFilters);
}
/**
@@ -658,4 +659,17 @@ ParameterObjectNamingStrategyCustomizer parameterObjectNamingStrategyCustomizer(
GlobalOpenApiCustomizer globalOpenApiCustomizer() {
return new OperationIdCustomizer();
}
+
+ /**
+ * Oas 31 model converter oas 31 model converter.
+ *
+ * @param springDocConfigProperties the spring doc config properties
+ * @return the oas 31 model converter
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @Lazy(false)
+ OAS31ModelConverter oas31ModelConverter(SpringDocConfigProperties springDocConfigProperties) {
+ return springDocConfigProperties.isOpenapi31() ? new OAS31ModelConverter() : null;
+ }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocHateoasConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocHateoasConfiguration.java
index f84cdbffd..5c35852ba 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocHateoasConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocHateoasConfiguration.java
@@ -31,7 +31,7 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springdoc.core.converters.CollectionModelContentConverter;
-import org.springdoc.core.converters.RepresentationModelLinksOASMixin;
+import org.springdoc.core.converters.HateoasLinksConverter;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.customizers.OpenApiHateoasLinksCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
@@ -49,7 +49,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.hateoas.Links;
-import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.LinkRelationProvider;
/**
@@ -99,20 +98,32 @@ CollectionModelContentConverter collectionModelContentConverter(HateoasHalProvid
*
* @param halProvider the hal provider
* @param springDocConfigProperties the spring doc config properties
- * @param objectMapperProvider the object mapper provider
* @return the open api customizer
- * @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider) org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)
+ * @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider) org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)
*/
@Bean(Constants.LINKS_SCHEMA_CUSTOMISER)
@ConditionalOnMissingBean(name = Constants.LINKS_SCHEMA_CUSTOMISER)
@Lazy(false)
- GlobalOpenApiCustomizer linksSchemaCustomizer(HateoasHalProvider halProvider, SpringDocConfigProperties springDocConfigProperties,
- ObjectMapperProvider objectMapperProvider) {
+ GlobalOpenApiCustomizer linksSchemaCustomizer(HateoasHalProvider halProvider, SpringDocConfigProperties springDocConfigProperties) {
if (!halProvider.isHalEnabled()) {
return openApi -> {
};
}
- objectMapperProvider.jsonMapper().addMixIn(RepresentationModel.class, RepresentationModelLinksOASMixin.class);
return new OpenApiHateoasLinksCustomizer(springDocConfigProperties);
}
+
+ /**
+ * Hateoas links converter hateoas links converter.
+ *
+ * @param springDocObjectMapper the spring doc object mapper
+ * @return the hateoas links converter
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @Lazy(false)
+ HateoasLinksConverter hateoasLinksConverter(ObjectMapperProvider springDocObjectMapper) {
+ return new HateoasLinksConverter(springDocObjectMapper) ;
+ }
+
+
}
\ No newline at end of file
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java
index a3fd6ab3b..7c7a34f6d 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java
@@ -41,6 +41,8 @@
import org.slf4j.LoggerFactory;
import org.springdoc.core.providers.ObjectMapperProvider;
+import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
+
/**
* The type Additional models converter.
*
@@ -146,8 +148,11 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
Class> cls = javaType.getRawClass();
if (modelToSchemaMap.containsKey(cls))
try {
+ Schema schema = modelToSchemaMap.get(cls);
+ if(springDocObjectMapper.isOpenapi31())
+ handleSchemaTypes(schema);
return springDocObjectMapper.jsonMapper()
- .readValue(springDocObjectMapper.jsonMapper().writeValueAsString(modelToSchemaMap.get(cls)), new TypeReference() {});
+ .readValue(springDocObjectMapper.jsonMapper().writeValueAsString(schema), new TypeReference() {});
}
catch (JsonProcessingException e) {
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/HateoasLinksConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/HateoasLinksConverter.java
new file mode 100644
index 000000000..929115c4e
--- /dev/null
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/HateoasLinksConverter.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * *
+ * * *
+ * * * *
+ * * * * *
+ * * * * * * Copyright 2019-2025 the original author or authors.
+ * * * * * *
+ * * * * * * 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
+ * * * * * *
+ * * * * * * https://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 org.springdoc.core.converters;
+
+
+import java.util.Iterator;
+
+import com.fasterxml.jackson.databind.JavaType;
+import io.swagger.v3.core.converter.ModelConverter;
+import io.swagger.v3.core.converter.ModelConverterContext;
+import io.swagger.v3.core.util.AnnotationsUtils;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.media.ArraySchema;
+import io.swagger.v3.oas.models.media.JsonSchema;
+import io.swagger.v3.oas.models.media.Schema;
+import org.springdoc.core.providers.ObjectMapperProvider;
+
+import org.springframework.hateoas.RepresentationModel;
+
+/**
+ * The type Hateoas links converter.
+ *
+ * @author bnasslahsen
+ */
+public class HateoasLinksConverter implements ModelConverter {
+
+ /**
+ * The Spring doc object mapper.
+ */
+ private final ObjectMapperProvider springDocObjectMapper;
+
+ /**
+ * Instantiates a new Hateoas links converter.
+ *
+ * @param springDocObjectMapper the spring doc object mapper
+ */
+ public HateoasLinksConverter(ObjectMapperProvider springDocObjectMapper) {
+ this.springDocObjectMapper = springDocObjectMapper;
+ }
+
+ @Override
+ public Schema> resolve(
+ io.swagger.v3.core.converter.AnnotatedType type,
+ ModelConverterContext context,
+ Iterator chain
+ ) {
+ JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
+ if (javaType != null) {
+ if (RepresentationModel.class.isAssignableFrom(javaType.getRawClass())) {
+ Schema> schema = chain.next().resolve(type, context, chain);
+ String schemaName = schema.get$ref().substring(Components.COMPONENTS_SCHEMAS_REF.length());
+ Schema original = context.getDefinedModels().get(schemaName);
+ Object links = original.getProperties().get("_links");
+ if(links instanceof JsonSchema jsonSchema) {
+ jsonSchema.set$ref(AnnotationsUtils.COMPONENTS_REF + "Links");
+ jsonSchema.setType(null);
+ jsonSchema.setItems(null);
+ jsonSchema.setTypes(null);
+ } else if (links instanceof ArraySchema arraySchema){
+ arraySchema.set$ref(AnnotationsUtils.COMPONENTS_REF + "Links");
+ }
+ return schema;
+ }
+ }
+ return chain.hasNext() ? chain.next().resolve(type, context, chain) : null;
+ }
+
+}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/OAS31ModelConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/OAS31ModelConverter.java
new file mode 100644
index 000000000..17fbdbbb4
--- /dev/null
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/OAS31ModelConverter.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * *
+ * * *
+ * * * *
+ * * * * *
+ * * * * * * Copyright 2019-2025 the original author or authors.
+ * * * * * *
+ * * * * * * 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
+ * * * * * *
+ * * * * * * https://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 org.springdoc.core.converters;
+
+import java.util.Iterator;
+
+import io.swagger.v3.core.converter.AnnotatedType;
+import io.swagger.v3.core.converter.ModelConverter;
+import io.swagger.v3.core.converter.ModelConverterContext;
+import io.swagger.v3.oas.models.media.Schema;
+
+import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
+import static org.springdoc.core.utils.SpringDocUtils.isComposedSchema;
+
+/**
+ * The type OAS31 Model converter.
+ *
+ * @author bnasslahsen
+ */
+public class OAS31ModelConverter implements ModelConverter {
+
+ @Override
+ public Schema> resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) {
+ if (chain.hasNext()) {
+ Schema> resolvedSchema = chain.next().resolve(type, context, chain);
+ if (resolvedSchema != null && !isComposedSchema(resolvedSchema)) {
+ handleSchemaTypes(resolvedSchema);
+ }
+ return resolvedSchema;
+ }
+ return null;
+ }
+
+}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java
index 281c6efb3..34e7e7f3a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java
@@ -35,12 +35,13 @@
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.oas.models.media.Schema;
-import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springframework.core.ResolvableType;
import org.springframework.data.web.PagedModel;
+import static org.springdoc.core.utils.SpringDocUtils.getParentTypeName;
+
/**
* The Spring Data Page type model converter.
*
@@ -95,7 +96,7 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
if (!type.isSchemaProperty())
type = resolvePagedModelType(type);
else
- type.name(cls.getSimpleName() + StringUtils.capitalize(type.getParent().getType()));
+ type.name(getParentTypeName(type, cls));
}
}
return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageableOpenAPIConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageableOpenAPIConverter.java
index 0e2864c88..0bae9272a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageableOpenAPIConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageableOpenAPIConverter.java
@@ -33,10 +33,11 @@
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.oas.models.media.Schema;
-import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.converters.models.Pageable;
import org.springdoc.core.providers.ObjectMapperProvider;
+import static org.springdoc.core.utils.SpringDocUtils.getParentTypeName;
+
/**
* The Pageable Type models converter.
*
@@ -90,7 +91,7 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
if (!type.isSchemaProperty())
type = PAGEABLE;
else
- type.name(cls.getSimpleName() + StringUtils.capitalize(type.getParent().getType()));
+ type.name(getParentTypeName(type, cls));
}
}
return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java
index 338db8cde..2a8b57b41 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
@@ -64,6 +64,11 @@ public class PolymorphicModelConverter implements ModelConverter {
*/
private static final List PARENT_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
+ /**
+ * The constant PARENT_TYPES_TO_IGNORE.
+ */
+ private static final List TYPES_TO_SKIP = Collections.synchronizedList(new ArrayList<>());
+
static {
PARENT_TYPES_TO_IGNORE.add("JsonSchema");
PARENT_TYPES_TO_IGNORE.add("Pageable");
@@ -115,6 +120,9 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
if (field.isAnnotationPresent(JsonUnwrapped.class)) {
PARENT_TYPES_TO_IGNORE.add(javaType.getRawClass().getSimpleName());
}
+ else if (field.isAnnotationPresent(io.swagger.v3.oas.annotations.media.Schema.class)) {
+ TYPES_TO_SKIP.add(field.getType().getSimpleName());
+ }
}
if (chain.hasNext()) {
if (!type.isResolveAsRef() && type.getParent() != null
@@ -149,12 +157,13 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
private Schema composePolymorphicSchema(AnnotatedType type, Schema schema, Collection schemas) {
String ref = schema.get$ref();
List composedSchemas = findComposedSchemas(ref, schemas);
-
if (composedSchemas.isEmpty()) return schema;
-
ComposedSchema result = new ComposedSchema();
if (isConcreteClass(type)) result.addOneOfItem(schema);
- composedSchemas.forEach(result::addOneOfItem);
+ JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
+ Class> clazz = javaType.getRawClass();
+ if(TYPES_TO_SKIP.stream().noneMatch(typeToSkip -> typeToSkip.equals(clazz.getSimpleName())))
+ composedSchemas.forEach(result::addOneOfItem);
return result;
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/RepresentationModelLinksOASMixin.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/RepresentationModelLinksOASMixin.java
deleted file mode 100644
index 0d0fe462c..000000000
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/RepresentationModelLinksOASMixin.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *
- * *
- * * *
- * * * *
- * * * * *
- * * * * * * Copyright 2019-2025 the original author or authors.
- * * * * * *
- * * * * * * 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
- * * * * * *
- * * * * * * https://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 org.springdoc.core.converters;
-
-import io.swagger.v3.core.util.AnnotationsUtils;
-import io.swagger.v3.oas.annotations.media.Schema;
-
-import org.springframework.hateoas.Links;
-import org.springframework.hateoas.mediatype.hal.RepresentationModelMixin;
-
-/**
- * The type Representation model links oas mixin.
- *
- * @author bnasslahsen
- */
-public abstract class RepresentationModelLinksOASMixin extends RepresentationModelMixin {
- @Override
- @Schema(ref = AnnotationsUtils.COMPONENTS_REF + "Links")
- public abstract Links getLinks();
-}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SortOpenAPIConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SortOpenAPIConverter.java
index 875bba99c..3b2b77929 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SortOpenAPIConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SortOpenAPIConverter.java
@@ -33,10 +33,11 @@
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.oas.models.media.Schema;
-import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.converters.models.Sort;
import org.springdoc.core.providers.ObjectMapperProvider;
+import static org.springdoc.core.utils.SpringDocUtils.getParentTypeName;
+
/**
* The Spring Data Sort type model converter.
*
@@ -85,7 +86,7 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
if (!type.isSchemaProperty())
type = SORT;
else
- type.name(cls.getSimpleName() + StringUtils.capitalize(type.getParent().getType()));
+ type.name(getParentTypeName(type, cls));
}
}
return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOperationCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOperationCustomizer.java
index 73eed5e8b..df37c2b43 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOperationCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOperationCustomizer.java
@@ -54,6 +54,7 @@
import org.springframework.web.method.HandlerMethod;
import static org.springdoc.core.providers.ActuatorProvider.getTag;
+import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
/**
* The type Actuator operation customizer.
@@ -240,7 +241,9 @@ private Parameter getParameterFromField(OperationParameter operationParameter) {
* @return the schema
*/
private Schema> resolveSchema(Parameter parameter) {
- return AnnotationsUtils.resolveSchemaFromType(parameter.getType(), null, null, springDocConfigProperties.isOpenapi31());
+ Schema schema = AnnotationsUtils.resolveSchemaFromType(parameter.getType(), null, null, springDocConfigProperties.isOpenapi31());
+ if(springDocConfigProperties.isOpenapi31()) handleSchemaTypes(schema);
+ return schema;
}
/**
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiHateoasLinksCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiHateoasLinksCustomizer.java
index 028725dcd..c3cae9d1e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiHateoasLinksCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiHateoasLinksCustomizer.java
@@ -33,7 +33,7 @@
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.MapSchema;
-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 org.springdoc.core.properties.SpringDocConfigProperties;
@@ -68,7 +68,7 @@ public void customise(OpenAPI openApi) {
.schema("Link", resolvedLinkSchema.schema)
.schema("Links", new MapSchema()
.additionalProperties(new StringSchema())
- .additionalProperties(new ObjectSchema().$ref(AnnotationsUtils.COMPONENTS_REF + "Link")));
+ .additionalProperties(new Schema<>().$ref(AnnotationsUtils.COMPONENTS_REF + "Link")));
if (springDocConfigProperties.isRemoveBrokenReferenceDefinitions())
this.removeBrokenReferenceDefinitions(openApi);
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin31.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin31.java
index bdc8dd369..8c7b53311 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin31.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin31.java
@@ -42,7 +42,7 @@
*
* @author bnasslashen
*/
-@JsonPropertyOrder(value = { "openapi", "info", "externalDocs", "servers", "security", "tags", "paths", "components", "webhooks" }, alphabetic = true)
+@JsonPropertyOrder(value = {"openapi", "info", "externalDocs", "servers", "security", "tags", "paths", "components", "webhooks"}, alphabetic = true)
public interface SortedOpenAPIMixin31 {
/**
@@ -71,5 +71,4 @@ public interface SortedOpenAPIMixin31 {
*/
@JsonSerialize(using = PathsSerializer.class)
Paths getPaths();
-
}
\ No newline at end of file
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java
index 55595e81c..a7f820a71 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java
@@ -36,13 +36,15 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import io.swagger.v3.core.jackson.mixin.Schema31Mixin;
/**
* The interface Sorted schema mixin 31.
*
* @author bnasslashen
*/
-@JsonPropertyOrder(value = { "type", "format" }, alphabetic = true)
+@JsonPropertyOrder(value = {"type", "format", "if", "then", "else"}, alphabetic = true)
public interface SortedSchemaMixin31 {
/**
@@ -116,6 +118,7 @@ public interface SortedSchemaMixin31 {
* @return the types
*/
@JsonProperty("type")
+ @JsonSerialize(using = Schema31Mixin.TypeSerializer.class)
Set getTypes();
/**
@@ -140,7 +143,7 @@ public interface SortedSchemaMixin31 {
*
* @return the example
*/
- @JsonInclude(JsonInclude.Include.CUSTOM)
+ @JsonInclude(value = JsonInclude.Include.NON_NULL)
Object getExample();
/**
@@ -151,4 +154,12 @@ public interface SortedSchemaMixin31 {
@JsonIgnore
Object getJsonSchemaImpl();
+ /**
+ * Gets boolean schema value.
+ *
+ * @return the boolean schema value
+ */
+ @JsonIgnore
+ Boolean getBooleanSchemaValue();
+
}
\ No newline at end of file
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java
index f5823daf3..1b5f8eeb9 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java
@@ -58,12 +58,18 @@ public class ObjectMapperProvider extends ObjectMapperFactory {
*/
private final ObjectMapper yamlMapper;
+ /**
+ * The Spring doc config properties.
+ */
+ private final SpringDocConfigProperties springDocConfigProperties;
+
/**
* Instantiates a new Spring doc object mapper.
*
* @param springDocConfigProperties the spring doc config properties
*/
public ObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties) {
+ this.springDocConfigProperties = springDocConfigProperties;
OpenApiVersion openApiVersion = springDocConfigProperties.getApiDocs().getVersion();
if (openApiVersion == OpenApiVersion.OPENAPI_3_1) {
jsonMapper = Json31.mapper();
@@ -132,4 +138,12 @@ public ObjectMapper yamlMapper() {
return yamlMapper;
}
+ /**
+ * Is openapi 31 boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isOpenapi31() {
+ return springDocConfigProperties.isOpenapi31();
+ }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java
index 442344d87..c0a19ed07 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java
@@ -302,7 +302,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
for (MethodParameter methodParameter : parameters) {
// check if query param
- Parameter parameter = null;
+ Parameter parameter;
io.swagger.v3.oas.annotations.Parameter parameterDoc = AnnotatedElementUtils.findMergedAnnotation(
AnnotatedElementUtils.forAnnotations(methodParameter.getParameterAnnotations()),
io.swagger.v3.oas.annotations.Parameter.class);
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericParameterService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericParameterService.java
index f605afa41..f29c91198 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericParameterService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericParameterService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
@@ -53,7 +53,6 @@
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.examples.Example;
-import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.FileSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
@@ -74,6 +73,7 @@
import org.springdoc.core.utils.Constants;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
+import org.springdoc.core.utils.SpringDocUtils;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
@@ -89,6 +89,7 @@
import static org.springdoc.core.utils.Constants.DOT;
import static org.springdoc.core.utils.SpringDocUtils.getParameterAnnotations;
+import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
/**
* The type Generic parameter builder.
@@ -299,6 +300,8 @@ public Parameter buildParameterFromDoc(io.swagger.v3.oas.annotations.Parameter p
if (parameterDoc.content().length > 0) {
Optional optionalContent = AnnotationsUtils.getContent(parameterDoc.content(), null, null, null, components, jsonView, propertyResolverUtils.isOpenapi31());
+ if (propertyResolverUtils.isOpenapi31())
+ optionalContent.ifPresent(SpringDocUtils::handleSchemaTypes);
optionalContent.ifPresent(parameter::setContent);
}
else
@@ -350,6 +353,8 @@ private void setSchema(io.swagger.v3.oas.annotations.Parameter parameterDoc, Com
schema.setDefault(defaultValue);
}
}
+ if (isOpenapi31())
+ handleSchemaTypes(schema);
parameter.setSchema(schema);
}
}
@@ -427,7 +432,7 @@ private Schema calculateRequestBodySchema(Components components, ParameterInfo p
requestBodyInfo.getMergedSchema().addProperty(paramName, schemaN);
schemaN = requestBodyInfo.getMergedSchema();
}
- else if (parameterInfo.isRequestPart() || schemaN instanceof FileSchema || schemaN instanceof ArraySchema && ((ArraySchema) schemaN).getItems() instanceof FileSchema) {
+ else if (parameterInfo.isRequestPart() || schemaN instanceof FileSchema || (schemaN!=null && schemaN.getItems() instanceof FileSchema)) {
schemaN = new ObjectSchema().addProperty(paramName, schemaN);
requestBodyInfo.setMergedSchema(schemaN);
}
@@ -470,7 +475,7 @@ private void setExamples(io.swagger.v3.oas.annotations.Parameter parameterDoc, P
* @param parameter the parameter
* @param locale the locale
*/
- private void setExtensions(io.swagger.v3.oas.annotations.Parameter parameterDoc, Parameter parameter, Locale locale) {
+ private void setExtensions(io.swagger.v3.oas.annotations.Parameter parameterDoc, Parameter parameter, Locale locale) {
if (parameterDoc.extensions().length > 0) {
Map extensionMap = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), parameterDoc.extensions());
if (propertyResolverUtils.isResolveExtensionsProperties()) {
@@ -537,7 +542,7 @@ private boolean isExplodable(io.swagger.v3.oas.annotations.Parameter p) {
* @return the boolean
*/
public boolean isFile(MethodParameter methodParameter) {
- if (methodParameter.getGenericParameterType() instanceof ParameterizedType ) {
+ if (methodParameter.getGenericParameterType() instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) methodParameter.getGenericParameterType();
return isFile(parameterizedType);
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java
index 858238d6c..596e61395 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java
@@ -61,7 +61,6 @@
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.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
@@ -477,15 +476,14 @@ public Schema resolveProperties(Schema schema, Locale locale) {
if (!CollectionUtils.isEmpty(properties)) {
LinkedHashMap resolvedSchemas = properties.entrySet().stream().map(es -> {
es.setValue(resolveProperties(es.getValue(), locale));
- if (es.getValue() instanceof ArraySchema arraySchema) {
- resolveProperties(arraySchema.getItems(), locale);
+ if (es.getValue().getItems() !=null ) {
+ resolveProperties(es.getValue().getItems(), locale);
}
return es;
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e2,
LinkedHashMap::new));
schema.setProperties(resolvedSchemas);
}
-
return schema;
}
@@ -606,12 +604,14 @@ public Webhooks[] getWebhooks() {
* @param locale the locale
*/
private void buildOpenAPIWithOpenAPIDefinition(OpenAPI openAPI, OpenAPIDefinition apiDef, Locale locale) {
+ boolean isOpenapi3 = propertyResolverUtils.isOpenapi31();
+ Map extensions = AnnotationsUtils.getExtensions(isOpenapi3, apiDef.info().extensions());
// info
- AnnotationsUtils.getInfo(apiDef.info(), propertyResolverUtils.isOpenapi31()).map(info -> resolveProperties(info, locale)).ifPresent(openAPI::setInfo);
+ AnnotationsUtils.getInfo(apiDef.info(),true).map(info -> resolveProperties(info, extensions, locale)).ifPresent(openAPI::setInfo);
// OpenApiDefinition security requirements
securityParser.getSecurityRequirements(apiDef.security()).ifPresent(openAPI::setSecurity);
// OpenApiDefinition external docs
- AnnotationsUtils.getExternalDocumentation(apiDef.externalDocs(), propertyResolverUtils.isOpenapi31()).ifPresent(openAPI::setExternalDocs);
+ AnnotationsUtils.getExternalDocumentation(apiDef.externalDocs(), isOpenapi3).ifPresent(openAPI::setExternalDocs);
// OpenApiDefinition tags
AnnotationsUtils.getTags(apiDef.tags(), false).ifPresent(tags -> openAPI.setTags(new ArrayList<>(tags)));
// OpenApiDefinition servers
@@ -623,7 +623,7 @@ private void buildOpenAPIWithOpenAPIDefinition(OpenAPI openAPI, OpenAPIDefinitio
);
// OpenApiDefinition extensions
if (apiDef.extensions().length > 0) {
- openAPI.setExtensions(AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), apiDef.extensions()));
+ openAPI.setExtensions(AnnotationsUtils.getExtensions(isOpenapi3, apiDef.extensions()));
}
}
@@ -647,11 +647,12 @@ private List resolveProperties(List servers, Locale locale) {
/**
* Resolve properties info.
*
- * @param info the info
- * @param locale the locale
+ * @param info the info
+ * @param extensions
+ * @param locale the locale
* @return the info
*/
- private Info resolveProperties(Info info, Locale locale) {
+ private Info resolveProperties(Info info, Map extensions, Locale locale) {
resolveProperty(info::getTitle, info::title, propertyResolverUtils, locale);
resolveProperty(info::getDescription, info::description, propertyResolverUtils, locale);
resolveProperty(info::getVersion, info::version, propertyResolverUtils, locale);
@@ -670,9 +671,12 @@ private Info resolveProperties(Info info, Locale locale) {
resolveProperty(contact::getUrl, contact::url, propertyResolverUtils, locale);
}
- if (propertyResolverUtils.isResolveExtensionsProperties()) {
- Map extensionsResolved = propertyResolverUtils.resolveExtensions(locale, info.getExtensions());
- info.setExtensions(extensionsResolved);
+ if (propertyResolverUtils.isResolveExtensionsProperties() && extensions != null) {
+ Map extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
+ if(propertyResolverUtils.isOpenapi31())
+ extensionsResolved.forEach(info::addExtension31);
+ else
+ info.setExtensions(extensionsResolved);
}
return info;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java
index faed6bba9..d9b151cb5 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java
@@ -57,6 +57,7 @@
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Content;
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 org.apache.commons.lang3.ArrayUtils;
@@ -69,6 +70,8 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestAttribute;
+import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
+
/**
* The type Spring doc annotations utils.
*
@@ -131,7 +134,8 @@ public static Schema extractSchema(Components components, Type returnType, JsonV
try {
resolvedSchema = ModelConverters.getInstance(openapi31)
.resolveAsResolvedSchema(
- new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonView).ctxAnnotations(annotations));
+ new AnnotatedType(returnType)
+ .resolveAsRef(true).jsonViewAnnotation(jsonView).ctxAnnotations(annotations));
}
catch (Exception e) {
LOGGER.warn(Constants.GRACEFUL_EXCEPTION_OCCURRED, e);
@@ -168,13 +172,16 @@ else if (componentSchemas.containsKey(entry.getKey()) && schemaMap.containsKey(e
components.setSchemas(componentSchemas);
}
if (resolvedSchema.schema != null) {
- schemaN = new Schema();
+ schemaN = new Schema(specVersion);
if (StringUtils.isNotBlank(resolvedSchema.schema.getName()))
schemaN.set$ref(COMPONENTS_REF + resolvedSchema.schema.getName());
else
schemaN = resolvedSchema.schema;
}
}
+ if(openapi31)
+ handleSchemaTypes(schemaN);
+
return schemaN;
}
@@ -215,9 +222,10 @@ public static Optional getContent(io.swagger.v3.oas.annotations.media.C
}
}
- if (content.size() == 0 && annotationContents.length != 1) {
+ if (content.isEmpty() && annotationContents.length != 1) {
return Optional.empty();
}
+ handleSchemaTypes(content);
return Optional.of(content);
}
@@ -386,7 +394,7 @@ private static MediaType getMediaType(Schema schema, Components components, Json
getSchema(annotationContent, components, jsonViewAnnotation, openapi31).ifPresent(mediaType::setSchema);
if (annotationContent.schemaProperties().length > 0) {
if (mediaType.getSchema() == null) {
- mediaType.schema(new Schema