diff --git a/.gitignore b/.gitignore
index f272f2255..33af7421d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
######################
/target/www/**
/src/test/javascript/coverage/
+.flattened-pom.xml
######################
# Node
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81c356a35..6fa93e9e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,82 +5,247 @@ 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.13] - 2025-09-07
+
+### Added
+
+- #3084 - Add Scalar Support
+
+### Changed
+
+- Upgrade swagger-ui to v5.28.1
+
+### Fixed
+
+- #3076 - With oneOf the response schema contains an extra type: string
+
+## [2.8.12] - 2025-09-01
+
+### Changed
+
+- Upgrade swagger-ui to v5.28.0
+
+### Fixed
+
+- #3073 - Duplicate key class Parameter when documenting two GET methods with same path and PathVariable.
+- #3071 - @io.swagger.v3.oas.annotations.parameters.RequestBody does not work well with @RequestPart
+- #3066 - Parameter is now required after upgrading to springdoc-openapi 2.8.10
+
+## [2.8.11] - 2025-08-23
+
+### Added
+
+- #3065 - javadoc and overall performance optimization
+
+### Changed
+
+- Upgrade spring-boot to v3.5.5
+
+### Fixed
+
+- #3064 -ClassNotFoundException: kotlin.reflect.full.KClasses
+
+## [2.8.10] - 2025-08-20
+
+### Added
+
+- #3046 - Feature Request: Support @jakarta.annotation.Nonnull.
+- #3042 - Support externalDocs configure on SpecPropertiesCustomizer
+- #3057 - Refactor webhook discovery and scanning mechanism
+
+### Changed
+
+- Upgrade spring-boot to v3.5.4
+- Upgrade swagger-ui to v5.27.1
+- Upgrade swagger-core to 2.2.36
+
+### Fixed
+
+- #3050 - @RequestPart JSON parameters missing Content-Type in generated curl commands, causing 415 errors.
+- #2978 - Parameter is no longer optional after upgrade to 2.8.8
+- #3022 - NullPointerException thrown in SchemaUtils.
+- #3026 - Fix unexpected merging of media types
+- #3036 - Fixed "desciption"
+- #3039 - Fix: Property resolution for extensions within @OpenAPIDefinition Info object
+- #3051 - Fixes so that a RequestPart with a Map is added to the RequestBody
+- #3060 - Use adaptFromForwardedHeaders instead of deprecated fromHttpRequest
+
+## [2.8.9] - 2025-06-10
+
+### Added
+
+- #2944 - Support for @Positive
+- #3011 - type-use for method parameters
+
+### Changed
+
+- Upgrade spring-boot to version 3.5.0
+
+### Fixed
+
+- #2982 - application/problem+json content type is not set for ProblemDetails
+- #2990 - Issues with POST Request, application/x-www-form-urlencoded and only one parameter
+- #2998 - io.swagger.v3.oas.annotations.Webhook does not work when defined on the method level
+- #3012 - Order of examples is (sometimes) not preserved
+
+## [2.8.8] - 2025-05-04
+
+### Fixed
+
+- #2977 - Handle projects not using kotlin-reflect #2977
+
+## [2.8.7] - 2025-05-04
+
+### Added
+
+- #2944 - Introducing springdoc-openapi-bom project
+- #2948 - Customize Servers via application.yml
+- #2963 - Set default content type for problem details object to application/problem+jso
+- #2971 - List of value classes in Kotlin
+
+### Changed
+
+- Upgrade swagger-ui to v5.21.0
+- Upgrade swagger-core to 2.2.30
+- Upgrade spring-boot to version 3.4.5
+- Upgrade spring-security-oauth2-authorization-server to version 1.4.3
+
+### Fixed
+
+- #2947 - Unexpected warning "Appended trailing slash to static resource location"
+- #2960 - NPE when customizing group's open-api without specifying any schema
+- #2969 - fix path to register resource handler to work SwaggerIndexPageTransformer
+ considering /webjar path prefix
+- #2964 - Cannot add custom description and example for java.time.Duration since v2.8.6
+- #2972 - @Header(schema = @Schema(type = "string")) generates empty or broken schema in
+ OpenAPI output since 2.8.0
+- #2976, #2967 - Build Failure due to Private Inner Class.
+
+## [2.8.6] - 2025-03-23
+
+### Added
+
+- #2909 - Check both SerDe BeanPropertyDefinition for @JsonUnwrapped/@Schema
+- #2927 - Bail sealed class subtype introspection on Schema
+- #2917 - Add Future to ignored response wrappers
+- #2938 - Add out of the box support for LocalTime, YearMonth, MonthDay
+
+### Changed
+
+- Upgrade swagger-ui to v5.20.1
+- Upgrade swagger-core to 2.2.29
+- Upgrade spring-cloud-function to 4.2.2
+- Upgrade spring-boot to version 3.4.4
+
+### Fixed
+
+- #2928 - Add missing builder methods in SchemaBuilder
+- #2905 - ModelResolver.enumAsRef = true result in invalid openapi with actuator using
+ enum param
+- #2939 - Duplicate ModelConverter registration with Spring Boot DevTools
+- #2941 - SpringBoot native fails /v3/api-docs when using a Map as an http entity field
+
## [2.8.5] - 2025-02-16
### Added
+
- #2696 - Do not require JsonSubType annotation for sealed classes
-- #2898 - add needed runtime reflection hints for native image
+- #2898 - add needed runtime reflection hints for native image
- #2891 - Refactor trimIndent Method
+- #2931 - OpenAPIService serverBaseUrl is not thread safe
+- #2933 - Wrong schema generation with PagedModel generated VIA_DTO and wrapped in
+ ResponseEntity
### Changed
+
- Upgrade swagger-ui to v5.18.3
### Fixed
-- #2902 - Schema replaced by String when using @ApiResponse with RepresentationModel (Hateoas links)
+
+- #2902 - Schema replaced by String when using @ApiResponse with RepresentationModel (
+ Hateoas links)
- #2876 - Restentpoints with same name get mix up
- #2895 - Only filter out actuator endpoints with double asterisks.
- #2894 - respect @JsonUnwrapped & @Schema on props not fields only
-- #2881 - fix defaultValue when using @PageableDefault together with one-indexed-parameters
+- #2881 - fix defaultValue when using @PageableDefault together with
+ one-indexed-parameters
- #2888 - Provide a better consistency for parameters and responses order.
## [2.8.4] - 2025-01-25
### Added
+
- #2873 - Improve performance of getGenericMapResponse
-- #2836 - Provide option to set allowed locales
+- #2836 - Provide option to set allowed locales
- 2862 - Align Swagger-UI Prefix Path with Swagger-WebMvc Behavior
### Changed
+
- Upgrade spring-boot to 3.4.2
- Upgrade spring-cloud-function to 4.2.1
- Upgrade swagger-core to 2.2.28
-### Fixed
+### Fixed
+
- #2870 - Springdoc 2.8.x + Spring Boot 3.4.1 breaks native image support
-- #2869 - Exception logged when generating schema for delete method of Spring Data repository.
+- #2869 - Exception logged when generating schema for delete method of Spring Data
+ repository.
- #2856 - @JsonUnwrapped is ignored in new version of lib.
-- #2852 - @Schema(types = "xxx") does not work for multipart param with enabled springdoc.default-support-form-data config option.
+- #2852 - @Schema(types = "xxx") does not work for multipart param with enabled
+ springdoc.default-support-form-data config option.
## [2.8.3] - 2025-01-12
### Added
+
- #2851 - Refine condition, for ignoring types when using PolymorphicModelConverter
## [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
## [2.8.1] - 2025-01-06
### Fixed
-- #2834 - java.lang.ClassNotFoundException: kotlin.reflect.full.KClasses when upgrade from 2.7.0 to 2.8.0
+
+- #2834 - java.lang.ClassNotFoundException: kotlin.reflect.full.KClasses when upgrade from
+ 2.7.0 to 2.8.0
## [2.8.0] - 2025-01-03
### Added
+
- #2790 - Moving to OpenAPI 3.1 as the default implementation for springdoc-openapi
-- #2817 - Obey annotations when flattening ParameterObject fields
-- #2826 - Make it possible to mark parameters with @RequestParam annotation to be sent in form instead of query.
+- #2817 - Obey annotations when flattening ParameterObject fields
+- #2826 - Make it possible to mark parameters with @RequestParam annotation to be sent in
+ form instead of query.
- #2822 - Support returning null in ParameterCustomizer
- #2830 - Add support for deprecated fields.
- #2780 - Add Security Schema by AutoConfigure
### Changed
+
- Upgrade spring-boot to 3.4.1
- Upgrade spring-cloud-function to 4.2.0
- Upgrade swagger-core to 2.2.27
### Fixed
+
- #2804 - Stable release 2.7.0 depends on Spring Cloud Milestone 4.2.0-M1
-- #2828 - Required a bean of type 'org.springframework.data.rest.webmvc.mapping.Associations' that could not be found.
+- #2828 - Required a bean of type '
+ org.springframework.data.rest.webmvc.mapping.Associations' that could not be found.
- #2823 - Capturing pattern in identical paths only renders the path element of one method
- #2817 - Automatically add required if a field is @notNull or @NotBlank.
-- #2814 - An unresolvable circular reference with management.endpoint.gateway.enabled=true.
+- #2814 - An unresolvable circular reference with
+ management.endpoint.gateway.enabled=true.
- #2798 - Object schema generated for Unit Kotlin type.
- #2797 - Removing operationId via customizer does not work anymore.
- #2833 - Resolve infinite recursion and add example test with OpenAPI v3.1
@@ -89,10 +254,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.7.0] - 2024-23-11
### Added
+
- #2777 - Add SortAsQueryParam annotation
-- #2786 - No static resource swagger-ui/index.html error after migration to 2.7.0-RC1
+- #2786 - No static resource swagger-ui/index.html error after migration to 2.7.0-RC1
### Changed
+
- Upgrade spring-boot to 3.4.0
- Upgrade swagger-ui to 5.18.2
- Upgrade spring-security-oauth2-authorization-server to 1.4.0
@@ -100,17 +267,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.7.0-RC1] - 2024-11-06
### Added
+
- #2649 - Add Encoding to multiple files and JSON payloads request test case
- #2653 - Trim indent apply schema description
-- #2664 - Refactor Replace hardcoded schema prefix length
-- #2509, #2668 - Replace swagger urls in org.springdoc.core.properties.AbstractSwaggerUiConfigProperties#urls only if url is changed
+- #2664 - Refactor Replace hardcoded schema prefix length
+- #2509, #2668 - Replace swagger urls in
+ org.springdoc.core.properties.AbstractSwaggerUiConfigProperties#urls only if url is
+ changed
- #2727 - Display nullable request body with map type
- #2746 - Readme.md add gradle import
-- #2760 - Added support for RequestBody as a meta-annotation
+- #2760 - Added support for RequestBody as a meta-annotation
- #2703 - Display nullable request body with map type
- #2657 - Add support for OAS v3.1 webhooks
### Changed
+
- Upgrade spring-boot to 3.4.0-RC1
- Upgrade swagger-core to 2.2.25
- Upgrade swagger-ui to 5.18.1
@@ -118,44 +289,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Upgrade spring-security-oauth2-authorization-server to 1.4.0-M2
### Fixed
+
- #2752 - Swagger doesn't work after custom annotation replacing request parameters
- #2747 - Move to webjars-locator-lite, in preparation for spring-boot 3.4 GA
- #2705 - @Schema oneOf config is ignored when generate the api-docs
-- #2744 - SpringDocUI doest add Javadoc into swagger from abstract class
-- #2708 - Spring Boot (Webflux) - Swagger UI - redirect URI does not include Gateway Prefix
-- #2725 - Serialization to openapi of org.springframework.data.domain.Sort is wrong for Spring Boot >2.x
+- #2744 - SpringDocUI doest add Javadoc into swagger from abstract class
+- #2708 - Spring Boot (Webflux) - Swagger UI - redirect URI does not include Gateway
+ Prefix
+- #2725 - Serialization to openapi of org.springframework.data.domain.Sort is wrong for
+ Spring Boot >2.x
- #2740 - Swagger-ui ignores property springdoc.swagger-ui.supported-submit-methods
- #2733 - Bad schema return type when created a generic wrapper class for response entity
- #2687 - Failed to load api definition after spring boot 3.4.0-M2
- #2642 - Calling Swagger UI via different context paths fails
-- #2709 - Annotation @Hidden on rest controller class level doesn't work due to spring default proxying mechanism CGLIB
+- #2709 - Annotation @Hidden on rest controller class level doesn't work due to spring
+ default proxying mechanism CGLIB
- #2642 - Calling Swagger UI via different context paths fails
-- #2663 - Content definition in @ApiResponse remove schema generated based on the returned value
-- #2646 - The operationId is unnecessarily deduplicated for a requestBody with multiple content types
+- #2663 - Content definition in @ApiResponse remove schema generated based on the returned
+ value
+- #2646 - The operationId is unnecessarily deduplicated for a requestBody with multiple
+ content types
- #2643 - UpperSnakeCaseStrategy is not working with spring boot and ParameterObject
- #2640 - @JsonUnwrapped is ignored when PolymorphicConverter is enabled
-- #2638 - Boolean Parameter with @Schema Annotation Changes Type to string in OpenAPI Documentation
+- #2638 - Boolean Parameter with @Schema Annotation Changes Type to string in OpenAPI
+ Documentation
- #2659 - Fix typo in SpringSecurityLoginEndpointCustomizer method name
-- #2660 - Update Response Code
-- #2442, #2669 - Fix SpringDocApp193Test for Java 21 and above
+- #2660 - Update Response Code
+- #2442, #2669 - Fix SpringDocApp193Test for Java 21 and above
- #2671 - Ensure default media type order is preserved using LinkedHashSet in mergeArrays
- #2711 - Missing descriptions on Kotlin ByteArray fields
-- #2733 - Bad schema return type when created a generic wrapper class for response entity
+- #2733 - Bad schema return type when created a generic wrapper class for response entity
## [2.6.0] - 2024-06-30
### Added
+
- #2561 - NPE occurs when outputting an OpenAPI document since 2.5.0
-- #2579 - Add support for leading tab characters with trim-kotlin-indent.
-- #2589 - Pass HttpRequest to ServerBaseUrlCustomizer
-- #2596, #2600 - consumes and produces calculation. Fixes
-- #2625, #2626 - Replace Page schema with PagedModel when pageSerializationMode is set to VIA_DTO
+- #2579 - Add support for leading tab characters with trim-kotlin-indent.
+- #2589 - Pass HttpRequest to ServerBaseUrlCustomizer
+- #2596, #2600 - consumes and produces calculation. Fixes
+- #2625, #2626 - Replace Page schema with PagedModel when pageSerializationMode is set to
+ VIA_DTO
- #2627 - Ensure compatibility with previous version of spring data
-- #2576 - GroupedApi orders by displayName instead of name.
-- #2584 - Dynamically define ApiGroups does not work.
+- #2576 - GroupedApi orders by displayName instead of name.
+- #2584 - Dynamically define ApiGroups does not work.
- #2595 - Spring security support of @RegisteredOAuth2AuthorizedClient
### Changed
+
- Upgrade spring-boot to 3.3.0
- Upgrade swagger-core to 2.2.22
- Upgrade swagger-ui to 5.17.14
@@ -163,30 +344,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Upgrade spring-security-oauth2-authorization-server to 1.3.0
### Fixed
+
- #2577 - Fix missing exception response types in OpenAPI spec
- #2591 - When an entity class contains fields of Class> type, an infinite loop.
-- #2603 - PolymorphicModelConverter only handles direct subtypes and misses indirect.
+- #2603 - PolymorphicModelConverter only handles direct subtypes and misses indirect.
- #2606 - Spring Authorization Server Metadata Endpoint not compatible.
-- #2621 - Content-type for POST endpoints with multipart/form-data does not work since v2.4.0.
+- #2621 - Content-type for POST endpoints with multipart/form-data does not work since
+ v2.4.0.
- #2622 - Kotlin enums are always marked as required if used in Java controllers.
-- #2601 - Multiple Superclasses Are Not Mapped To Multiple allOf If Used In Different Services.
-- #2597 - Polymorphic fields on polymorphic parents don't get correct oneOf docs generated.
+- #2601 - Multiple Superclasses Are Not Mapped To Multiple allOf If Used In Different
+ Services.
+- #2597 - Polymorphic fields on polymorphic parents don't get correct oneOf docs
+ generated.
## [2.5.0] - 2024-04-01
### Added
+
- #2318 - Add Info to GroupedOpenAPI properties
- #2554 - Remove duplicate words from comments
- #2418 - Improve support for externalizing strings in generated openapi
-- #2535 - Add 'springdoc.trim-kotlin-indent' property to handle Kotlin multiline string indentation
+- #2535 - Add 'springdoc.trim-kotlin-indent' property to handle Kotlin multiline string
+ indentation
### Changed
+
- Upgrade spring-boot to 3.2.4
- Upgrade swagger-core to 2.2.21
- Upgrade swagger-ui to 5.13.0
### Fixed
-- #2525 - Inherited Methods Not Included in Swagger Documentation with @RouterOperation in Spring Boot WebFlux Application
+
+- #2525 - Inherited Methods Not Included in Swagger Documentation with @RouterOperation in
+ Spring Boot WebFlux Application
- #2526 - SpringDoc bean naming conflict error with GraphQL Spring boot starter
- #2540 - Fix typo in SpringRepositoryRestResourceProvider.java
- #2549 - Fix README.md
@@ -194,47 +384,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.4.0] - 2024-03-12
### Added
+
- #2443 - Respect schema annotations when using spring mvc with kotlin
- #2492, #2488 - Support dynamic evaluation of description field in the RequestBody
- #2510 - Option to disable root api-docs path when using groups
### Changed
+
- Upgrade spring-boot to 3.2.3
- Upgrade swagger-core to 2.2.20
- Upgrade swagger-ui to 5.11.8
### Fixed
+
- #2453 - Fix CODE_OF_CONDUCT.md links
- #2454 - Fix typo in SwaggerWelcomeWebMvc
- #2507 - Fix typo in Constants
- #2472 - Update JavadocPropertyCustomizer.java
- #2495 - Fix broken links in README and CONTRIBUTING
- #2501 - bug fix when "exported" is set to false in RestResource annotation
-- #2447 - Serialization to openapi of org.springframework.data.domain.Sort is not done correctly
+- #2447 - Serialization to openapi of org.springframework.data.domain.Sort is not done
+ correctly
- #2449 - Extensions in subobjects of OpenAPI no longer work
-- #2461 - Springdoc OpenApi Annotations @ExtensionProperty Not Evaluating Properties from application.yml
+- #2461 - Springdoc OpenApi Annotations @ExtensionProperty Not Evaluating Properties from
+ application.yml
- #2469 - Pom contains invalid organizationUrl
- #2518 - Duplicate GroupConfigs in SpringDocConfigProperties
-- #2506 - Springdoc breaks (Unexpected value: TRACE) when a spring-cloud-starter-gateway-mvc universal gateway is configured.
+- #2506 - Springdoc breaks (Unexpected value: TRACE) when a
+ spring-cloud-starter-gateway-mvc universal gateway is configured.
- #2519 - Request parameter parsing error after using @NotBlank from type interface field
- #2516 - Spring Data REST fails when setting version to openapi_3_1
- #2509 - ArrayIndexOutOfBoundsException in SwaggerUiConfigParameters
- #2484 - JavaDoc integration not working with SnakeCaseStrategy property naming
-- #2483 - Controller advice documents ApiResponse on every operation, even if the operation does not annotate the exception to be thrown
-- #2477 - buildApiResponses ignores produced ContentType in case of many @Operation
+- #2483 - Controller advice documents ApiResponse on every operation, even if the
+ operation does not annotate the exception to be thrown
+- #2477 - buildApiResponses ignores produced ContentType in case of many @Operation
## [2.3.0] - 2023-12-03
### Added
+
- #2340 - Add support OIDC with Spring Authorization Server
-- #2345 - Support Schema added in OpenAPI Specification v3.1
-- #2387 - Support get javadoc description from getter method
+- #2345 - Support Schema added in OpenAPI Specification v3.1
+- #2387 - Support get javadoc description from getter method
- #2404 - Update condition to register links schema customizer
- #2359 - Update condition to register links schema customizer
- #2348 - Enhance resource path processing
- #2438, #2315 - Support for @JsonProperty with Javadoc Change in springdoc-openapi
### Changed
+
- Upgrade spring-boot to 3.2.0
- Upgrade swagger-core to 2.2.19
- Upgrade swagger-ui to 5.10.3
@@ -242,20 +441,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- #2366 - Fix the failed test due to hardcoded file separators
-- #2370, #2371 - No empty description for polymorphic subtypes
-- #2373 - SchemaProperty.array Schema is ignored in /api-docs or api-docs.yaml
+- #2370, #2371 - No empty description for polymorphic subtypes
+- #2373 - SchemaProperty.array Schema is ignored in /api-docs or api-docs.yaml
- #2366 - Refactoring AbstractSwaggerResourceResolver.findWebJarResourcePath
- #2320 - javadoc for class attribute ignored when in EntityModel.
- #2347 - Not working if a property of entity contains generic parameters.
-- #2399 - SpringdocRouteBuilder.onError is overriding last route defined.
-- #2426 - StackOverflowError when using @ParameterObject on groovy class.
+- #2399 - SpringdocRouteBuilder.onError is overriding last route defined.
+- #2426 - StackOverflowError when using @ParameterObject on groovy class.
## [2.2.0] - 2023-08-06
### Added
-- #2189 - Add support for swagger-ui.url property
+
+- #2189 - Add support for swagger-ui.url property
- #2200 - Support schema.requiredMode() on ParameterObject
-- #2309 - Added function to preload by specifying locale
+- #2309 - Added function to preload by specifying locale
- #2332 - Group name cannot be null or empty
- #2281 - Initial Virtual thread support
@@ -267,17 +467,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
-- #2199 - Fix Schema get condition of ArraySchema.
-- #2194 - Fix Swagger UI with provided spec
-- #2213 - Using both generated and configured specs stoped working in 1.6.5
+- #2199 - Fix Schema get condition of ArraySchema.
+- #2194 - Fix Swagger UI with provided spec
+- #2213 - Using both generated and configured specs stoped working in 1.6.5
- #2222 - String Index Out of Bounce Exception Fix when deployed on Azure
-- #2243, #2235 - Fix StringIndexOutOfBoundsException when path is same webjar
-- #2291 - Fix default-flat-param-object doesn't work when using http body
-- #2310 - Change bean name of objectMapperProvider
+- #2243, #2235 - Fix StringIndexOutOfBoundsException when path is same webjar
+- #2291 - Fix default-flat-param-object doesn't work when using http body
+- #2310 - Change bean name of objectMapperProvider
- #2207 - swagger-initializer.js is sent endcoded in the JVM's default charset
-- #2271, #2280 - Fix loop when response inherits generic class fixes
+- #2271, #2280 - Fix loop when response inherits generic class fixes
- #2312 - Spec for @ParameterObject disappears if building native-images
-- #2326 - @QuerydslPredicate(root = X.class) annotation at Controller Method level not getting documented in Spring Boot 3
+- #2326 - @QuerydslPredicate(root = X.class) annotation at Controller Method level not
+ getting documented in Spring Boot 3
## [2.1.0] - 2023-04-01
@@ -301,16 +502,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #2131 - Fixed a bug that javadoc of record class parameters was not recognized.
- #2140 - Javadoc record class parameters not recognized
- #2123 #2141 - fix spring authorization server response.
-- #2148 - Fix properties show-oauth2-endpoints and SpringDocConfigProperties#showOauth2Endpoint properties name mismatch
+- #2148 - Fix properties show-oauth2-endpoints and
+ SpringDocConfigProperties#showOauth2Endpoint properties name mismatch
- #2149 - Request parameters with default values are marked as required.
- #2155 - openApi.getServers() is null in OpenApiCustomiser when using different locales.
- #2152 - Redundant(wrong) direction appended to @PageableDefault.
- #2181 #2183 - Fixed DefaultFlatParamObject to work with annotated parameters.
-- #2170 #2187 - All request parameters marked as required for Java controllers in mixed projects in 2.0.3
+- #2170 #2187 - All request parameters marked as required for Java controllers in mixed
+ projects in 2.0.3
- #2165 - Custom Converters are not excluded if not registered for Http Message Converter.
- #2185 - Fix behaviour of required flag for schema class fields.
- #2139 - SpringDocSecurityConfiguration class not sufficiently constrained.
-- #2142 - SpringDocJacksonModuleConfiguration is loaded even though there is no ObjectMapperProvider when springdoc.api-docs.enabled = false.
+- #2142 - SpringDocJacksonModuleConfiguration is loaded even though there is no
+ ObjectMapperProvider when springdoc.api-docs.enabled = false.
## [2.0.4] - 2023-03-15
@@ -332,6 +536,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #2054 - Add copyright and license information to Jar.
- #2021 - Required field in Schema annotation ignored in Kotlin.
- #2094 - Initial support for Spring Authorization Server.
+
### Changed
- Upgrade spring-boot to 3.0.4
@@ -342,20 +547,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #2010 - findByNameContainingIgnoreCaseAndDateBefore throw NullPointerException.
- #2031 - Path variables parameters are not assigned correctly to endpoints.
-- #2038 - When extends JpaRepository, using @Parameter over the method results in duplicate of the same parameter.
+- #2038 - When extends JpaRepository, using @Parameter over the method results in
+ duplicate of the same parameter.
- #2046 - Map Fields Disappear with Groovy on Classpath.
- #2051 - Malformed api-docs JSON when StringHttpMessageConverter is not active
- #2062 - OperationCustomizer is not working with Spring Data REST.
-- #2098 - When getting ExceptionHandler in the controller, use target class in case of AOP Proxy.
+- #2098 - When getting ExceptionHandler in the controller, use target class in case of AOP
+ Proxy.
- #2107 - Ordering of GlobalOpenApiCustomizers different than for OpenApiCustomisers.
-- #2089 - Fixed a bug that a NullPointerException is thrown when the description field of RequestBody is null and there is a javadoc description.
+- #2089 - Fixed a bug that a NullPointerException is thrown when the description field of
+ RequestBody is null and there is a javadoc description.
- #2104 - OpenAPI Extensions no longer work.
## [2.0.2] - 2022-12-16
### Fixed
-- #2008 - Error when com.fasterxml.jackson.module.kotlin.KotlinModule is not present in classpath
+- #2008 - Error when com.fasterxml.jackson.module.kotlin.KotlinModule is not present in
+ classpath
## [2.0.1] - 2022-12-16
@@ -368,7 +577,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1957 - AdditionalModelsConverter Schema params rewriting
- #1962 - override-with-generic-response shouldn't shallow copy
-- #1985 - IllegalStateException: Duplicate key when two endpoints at the same URL with same header exist
+- #1985 - IllegalStateException: Duplicate key when two endpoints at the same URL with
+ same header exist
- #1992 - Java enumeration and Spring Converter no longer generates enum drop-down
- #2001 - Enum Collection parameter missing type info in Spring Data Rest search method
- #1961 - ContinuationObject leaks into schema
@@ -380,6 +590,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1284 - Add support for Jakarta EE
### What's Changed
+
- Upgrade spring-boot to v3.0.0
## [2.0.0-RC2] - 2022-11-20
@@ -388,7 +599,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1929 - Enables no cache on not cache swagger-initializer.js
- #1922 - Check existence of superclass before accessing its name
-- #1923 - Javadoc description of the @RequestPart param of multipart/form-data to the parameter description
+- #1923 - Javadoc description of the @RequestPart param of multipart/form-data to the
+ parameter description
+
### Changed
- Upgrade spring-boot to 3.0.0-RC2
@@ -399,10 +612,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- #1892 - springdoc.model-and-view-allowed enhanced
-- #1901 - When @Get, using @Parameter over the method results in duplicate of the same parameter
+- #1901 - When @Get, using @Parameter over the method results in duplicate of the same
+ parameter
- #1909 - ExceptionHandler in controller is not used by another controller
-- #1904 - springdoc-openapi-webflux-ui 2.0.0-M7 + spring actuator + spring cloud crashes at startup
-- #1911 - Wrong type for springdoc.swagger-ui.oauth.useBasicAuthenticationWithAccessCodeGrant configuration property
+- #1904 - springdoc-openapi-webflux-ui 2.0.0-M7 + spring actuator + spring cloud crashes
+ at startup
+- #1911 - Wrong type for
+ springdoc.swagger-ui.oauth.useBasicAuthenticationWithAccessCodeGrant configuration
+ property
- #1931 - Spring Security form login only offers application/json req body type.
## [2.0.0-RC1] - 2022-10-23
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index f7dce2d75..93ae9062f 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -35,5 +35,6 @@ individual is representing the project or its community.
This Code of Conduct is adapted from the
[Contributor Covenant](https://contributor-covenant.org), version 1.3.0, available at
-[contributor-covenant.org/version/1/3/0/](https://contributor-covenant.org/version/1/3/0/) and [spring-boot
+[contributor-covenant.org/version/1/3/0/](https://contributor-covenant.org/version/1/3/0/)
+and [spring-boot
Contributor Code of Conduct](https://github.com/spring-projects/spring-boot/blob/master/CODE_OF_CONDUCT.adoc)
diff --git a/README.md b/README.md
index 5b3618093..851b03156 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,17 @@
[](https://snyk.io/test/github/springdoc/springdoc-openapi.git)
[](https://stackoverflow.com/questions/tagged/springdoc?tab=Votes)
-IMPORTANT: ``springdoc-openapi v1.8.0`` is the latest Open Source release supporting Spring Boot 2.x and 1.x.
+IMPORTANT: ``springdoc-openapi v1.8.0`` is the latest Open Source release supporting
+Spring Boot 2.x and 1.x.
-An extended support for [*springdoc-openapi v1*](https://springdoc.org/v1)
-project is now available for organizations that need support beyond 2023.
+An extended support for [*springdoc-openapi v1*](https://springdoc.org/v1)
+project is now available for organizations that need support beyond 2023.
-For more details, feel free to reach out: [sales@springdoc.org](mailto:sales@springdoc.org)
+For more details, feel free to reach
+out: [sales@springdoc.org](mailto:sales@springdoc.org)
-``springdoc-openapi`` is on [Open Collective](https://opencollective.com/springdoc). If you ❤️ this project consider becoming
-a [sponsor](https://github.com/sponsors/springdoc).
+``springdoc-openapi`` is on [Open Collective](https://opencollective.com/springdoc). If
+you ❤️ this project consider becoming a [sponsor](https://github.com/sponsors/springdoc).
This project is sponsored by
@@ -33,7 +35,7 @@ This project is sponsored by
-
+
@@ -83,9 +85,11 @@ The following video introduces the Library:
* [https://youtu.be/utRxyPfFlDw](https://youtu.be/utRxyPfFlDw)
-For *spring-boot v3* support, make sure you use [springdoc-openapi v2](https://springdoc.org/)
+For *spring-boot v3* support, make sure you
+use [springdoc-openapi v2](https://springdoc.org/)
-This is a community-based project, not maintained by the Spring Framework Contributors (Pivotal)
+This is a community-based project, not maintained by the Spring Framework Contributors (
+Pivotal)
# **Getting Started**
@@ -106,6 +110,7 @@ This is a community-based project, not maintained by the Spring Framework Contri
additional configuration is needed):
Maven
+
```xml
org.springdoc
@@ -115,6 +120,7 @@ Maven
```
Gradle
+
```groovy
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:latest'
```
@@ -158,6 +164,7 @@ springdoc.swagger-ui.path=/swagger-ui.html
is needed)
Maven
+
```xml
org.springdoc
@@ -167,6 +174,7 @@ Maven
```
Gradle
+
```groovy
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:latest'
```
@@ -209,6 +217,7 @@ and `@SecurityScheme` annotations within a Spring managed bean.
is needed)
Maven
+
```xml
org.springdoc
@@ -218,6 +227,7 @@ Maven
```
Gradle
+
```groovy
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:latest'
```
@@ -235,12 +245,12 @@ The artifacts can be viewed accessed at the following locations:
Releases:
-* [https://s01.oss.sonatype.org/content/groups/public/org/springdoc/](https://s01.oss.sonatype.org/content/groups/public/org/springdoc/)
+* [https://central.sonatype.com/search?q=g:org.springdoc)](https://central.sonatype.com/search?q=g:org.springdoc)
.
Snapshots:
-* [https://s01.oss.sonatype.org/content/repositories/snapshots/org/springdoc/](https://s01.oss.sonatype.org/content/repositories/snapshots/org/springdoc/)
+* [https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/org/springdoc/](https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/org/springdoc/)
.
# Acknowledgements
@@ -263,4 +273,4 @@ Thanks you all for your support!
* [JetBrains](https://www.jetbrains.com/?from=springdoc-openapi) - Thanks a lot for
supporting springdoc-openapi project.
-
+
diff --git a/SECURITY.md b/SECURITY.md
index 0099d9342..fda5252c2 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,9 +2,9 @@
## Supported Versions
-| Version | Supported |
-| ------- | ------------------ |
-| latest-stable | :white_check_mark: |
+| Version | Supported |
+|---------------|--------------------|
+| latest-stable | :white_check_mark: |
## Reporting a Vulnerability
diff --git a/pom.xml b/pom.xml
index dcda74974..e101d2b67 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.springdoc
springdoc-openapi
- 2.8.5
+ 2.8.13
pom
Spring openapi documentation
Spring openapi documentation
@@ -11,7 +11,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.4.2
+ 3.5.5
@@ -35,18 +35,8 @@
scm:git:git@github.com:springdoc/springdoc-openapi.git
scm:git:git@github.com:springdoc/springdoc-openapi.git
- v2.8.5
+ v2.8.13
-
-
- ossrh
- https://s01.oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
springdoc-openapi-starter-common
@@ -54,19 +44,27 @@
springdoc-openapi-starter-webflux-api
springdoc-openapi-starter-webmvc-ui
springdoc-openapi-starter-webflux-ui
+ springdoc-openapi-starter-webmvc-scalar
+ springdoc-openapi-starter-webflux-scalar
+ springdoc-openapi-bom
1.6
2.5.3
- 1.6.8
- 2.2.28
- 5.18.3
+ 0.7.0
+
+ 1.5.0
+ 2.2.36
+ 5.28.1
1.13.1
0.9.1
0.15.0
- 4.2.1
- 1.4.0
+ 4.2.2
+ 1.4.3
+
+ 0.1.0
+ false
@@ -83,74 +81,39 @@
swagger-ui
${swagger-ui.version}
-
- javax.xml
- jaxb-impl
- ${jaxb-impl.version}
-
-
- javax.jws
- javax.jws-api
- ${javax.jws-api.version}
- test
-
io.jsonwebtoken
jjwt
${jjwt.version}
- test
-
+
org.springframework.cloud
- spring-cloud-function-core
- ${spring-cloud-function.version}
-
-
- org.springframework.cloud
- spring-cloud-function-web
- ${spring-cloud-function.version}
-
-
- org.springframework.cloud
- spring-cloud-starter-function-web
- ${spring-cloud-function.version}
-
-
- org.springframework.cloud
- spring-cloud-starter-function-webflux
+ spring-cloud-function-dependencies
${spring-cloud-function.version}
+ pom
+ import
+
org.springframework.security
spring-security-oauth2-authorization-server
${spring-security-oauth2-authorization-server.version}
-
-
- org.springdoc
- springdoc-openapi-starter-common
- ${project.version}
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-api
- ${project.version}
-
- org.springdoc
- springdoc-openapi-starter-webflux-api
- ${project.version}
+ com.scalar.maven
+ scalar
+ ${scalar.version}
- org.springdoc
- springdoc-openapi-starter-webmvc-ui
- ${project.version}
+ com.github.therapi
+ therapi-runtime-javadoc
+ ${therapi-runtime-javadoc.version}
- org.springdoc
- springdoc-openapi-starter-webflux-ui
- ${project.version}
+ com.github.therapi
+ therapi-runtime-javadoc-scribe
+ ${therapi-runtime-javadoc.version}
@@ -159,24 +122,9 @@
org.springframework.boot
spring-boot-starter-test
test
-
-
- org.junit.vintage
- junit-vintage-engine
-
-
-
-
-
- org.codehaus.gmavenplus
- gmavenplus-plugin
- ${gmavenplus-plugin.version}
-
-
-
org.apache.maven.plugins
@@ -189,7 +137,8 @@
copy-resources
- ${basedir}/target/classes/META-INF
+ ${basedir}/target/classes/META-INF
+
${basedir}/..
@@ -268,22 +217,16 @@
- org.sonatype.plugins
- nexus-staging-maven-plugin
- ${nexus-staging-maven-plugin}
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central-publishing-maven-plugin.version}
true
- ossrh
- https://s01.oss.sonatype.org/
- true
+ central
+ true
+ published
+ ${skipPublishing}
-
-
- com.thoughtworks.xstream
- xstream
- 1.4.15
-
-
maven-release-plugin
@@ -301,33 +244,13 @@
-
- central
- https://repo.maven.apache.org/maven2
-
- false
-
-
-
- spring-release
- Spring release
- https://repo.spring.io/release
-
- false
-
-
-
- spring-snapshot
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- true
-
-
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
+
+ true
+
false
@@ -335,9 +258,12 @@
- spring-releases
- Spring Releases
- https://repo.spring.io/release
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ true
+
false
diff --git a/springdoc-openapi-bom/.gitignore b/springdoc-openapi-bom/.gitignore
new file mode 100644
index 000000000..ab21548c1
--- /dev/null
+++ b/springdoc-openapi-bom/.gitignore
@@ -0,0 +1,144 @@
+######################
+# Project Specific
+######################
+/target/www/**
+/src/test/javascript/coverage/
+
+######################
+# Node
+######################
+/node/
+node_tmp/
+node_modules/
+npm-debug.log.*
+/.awcache/*
+/.cache-loader/*
+
+######################
+# SASS
+######################
+.sass-cache/
+
+######################
+# Eclipse
+######################
+*.pydevproject
+.project
+.metadata
+tmp/
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+.factorypath
+/src/main/resources/rebel.xml
+
+# External tool builders
+.externalToolBuilders/**
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+######################
+# Intellij
+######################
+.idea/
+*.iml
+*.iws
+*.ipr
+*.ids
+*.orig
+classes/
+out/
+
+######################
+# Visual Studio Code
+######################
+.vscode/
+
+######################
+# Maven
+######################
+/log/
+/target/
+
+######################
+# Gradle
+######################
+.gradle/
+/build/
+
+######################
+# Package Files
+######################
+*.jar
+*.war
+*.ear
+*.db
+
+######################
+# Windows
+######################
+# Windows image file caches
+Thumbs.db
+
+# Folder config file
+Desktop.ini
+
+######################
+# Mac OSX
+######################
+.DS_Store
+.svn
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+######################
+# Directories
+######################
+/bin/
+/deploy/
+
+######################
+# Logs
+######################
+*.log*
+
+######################
+# Others
+######################
+*.class
+*.*~
+*~
+.merge_file*
+
+######################
+# Gradle Wrapper
+######################
+!gradle/wrapper/gradle-wrapper.jar
+
+######################
+# Maven Wrapper
+######################
+!.mvn/wrapper/maven-wrapper.jar
+
+######################
+# ESLint
+######################
+.eslintcache
\ No newline at end of file
diff --git a/springdoc-openapi-bom/pom.xml b/springdoc-openapi-bom/pom.xml
new file mode 100644
index 000000000..a7aeedb52
--- /dev/null
+++ b/springdoc-openapi-bom/pom.xml
@@ -0,0 +1,83 @@
+
+ 4.0.0
+
+ org.springdoc
+ springdoc-openapi
+ 2.8.13
+
+ springdoc-openapi-bom
+ ${project.artifactId}
+ pom
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-common
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webflux-api
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webflux-ui
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-scalar
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webflux-scalar
+ ${project.version}
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ ${flatten-maven-plugin.version}
+ false
+
+
+
+ flatten
+ process-resources
+
+ flatten
+
+
+ true
+ bom
+
+ remove
+ remove
+ keep
+ remove
+ remove
+
+
+
+
+
+
+
+
+
diff --git a/springdoc-openapi-starter-common/pom.xml b/springdoc-openapi-starter-common/pom.xml
index 93483b2ac..fbb049f7e 100644
--- a/springdoc-openapi-starter-common/pom.xml
+++ b/springdoc-openapi-starter-common/pom.xml
@@ -3,14 +3,19 @@
org.springdoc
springdoc-openapi
- 2.8.5
+ 2.8.13
springdoc-openapi-starter-common
+ ${project.artifactId}
org.springframework.boot
spring-boot-autoconfigure
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.springframework.boot
spring-boot-configuration-processor
@@ -23,7 +28,7 @@
org.springframework.boot
- spring-boot-starter-actuator
+ spring-boot-actuator-autoconfigure
true
@@ -42,7 +47,6 @@
com.github.therapi
therapi-runtime-javadoc
- ${therapi-runtime-javadoc.version}
true
@@ -88,6 +92,11 @@
querydsl-core
true
+
+ com.scalar.maven
+ scalar
+ true
+
@@ -155,4 +164,4 @@
-
\ No newline at end of file
+
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java
index 948a0e667..bb7f44361 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.api;
@@ -86,6 +86,7 @@
import org.slf4j.LoggerFactory;
import org.springdoc.core.annotations.RouterOperations;
import org.springdoc.core.customizers.DataRestRouterOperationCustomizer;
+import org.springdoc.core.customizers.GlobalOperationComponentsCustomizer;
import org.springdoc.core.customizers.OpenApiLocaleCustomizer;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springdoc.core.customizers.RouterOperationCustomizer;
@@ -108,6 +109,7 @@
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.OperationService;
import org.springdoc.core.utils.PropertyResolverUtils;
+import org.springdoc.core.utils.SpringDocAnnotationsUtils;
import org.springdoc.core.utils.SpringDocUtils;
import org.springframework.aop.support.AopUtils;
@@ -130,6 +132,7 @@
import static org.springdoc.core.utils.Constants.DOT;
import static org.springdoc.core.utils.Constants.OPERATION_ATTRIBUTE;
import static org.springdoc.core.utils.Constants.SPRING_MVC_SERVLET_PATH;
+import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
/**
@@ -139,6 +142,7 @@
* @author kevinraddatz
* @author hyeonisism
* @author doljae
+ * @author zdary
*/
public abstract class AbstractOpenApiResource extends SpecFilter {
@@ -177,6 +181,11 @@ public abstract class AbstractOpenApiResource extends SpecFilter {
*/
protected final SpringDocProviders springDocProviders;
+ /**
+ * The Spring doc customizers.
+ */
+ protected final SpringDocCustomizers springDocCustomizers;
+
/**
* The open api builder object factory.
*/
@@ -202,16 +211,6 @@ public abstract class AbstractOpenApiResource extends SpecFilter {
*/
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
- /**
- * The Open api builder.
- */
- protected OpenAPIService openAPIService;
-
- /**
- * The Spring doc customizers.
- */
- protected final SpringDocCustomizers springDocCustomizers;
-
/**
* The Reentrant lock.
*/
@@ -222,6 +221,11 @@ public abstract class AbstractOpenApiResource extends SpecFilter {
*/
private final Pattern pathPattern = Pattern.compile("\\{(.*?)}");
+ /**
+ * The Open api builder.
+ */
+ protected OpenAPIService openAPIService;
+
/**
* Instantiates a new Abstract open api resource.
@@ -255,7 +259,7 @@ protected AbstractOpenApiResource(String groupName, ObjectFactory this.getOpenApi(Locale.forLanguageTag(locale)));
+ Executors.newSingleThreadExecutor().execute(() -> this.getOpenApi(null, Locale.forLanguageTag(locale)));
}
}
}
@@ -333,7 +337,7 @@ public static void setModelAndViewClass(Class> modelAndViewClass) {
* Gets open api.
*/
private void getOpenApi() {
- this.getOpenApi(Locale.getDefault());
+ this.getOpenApi(null, Locale.getDefault());
}
/**
@@ -342,7 +346,7 @@ private void getOpenApi() {
* @param locale the locale
* @return the open api
*/
- protected OpenAPI getOpenApi(Locale locale) {
+ protected OpenAPI getOpenApi(String serverBaseUrl, Locale locale) {
this.reentrantLock.lock();
try {
final OpenAPI openAPI;
@@ -383,22 +387,15 @@ protected OpenAPI getOpenApi(Locale locale) {
openAPIService.setServersPresent(true);
else
openAPIService.setServersPresent(false);
- openAPIService.updateServers(openAPI);
+ openAPIService.updateServers(serverBaseUrl, openAPI);
if (springDocConfigProperties.isRemoveBrokenReferenceDefinitions())
this.removeBrokenReferenceDefinitions(openAPI);
// run the optional customizers
List servers = openAPI.getServers();
- List serversCopy = null;
- try {
- serversCopy = springDocProviders.jsonMapper()
- .readValue(springDocProviders.jsonMapper().writeValueAsString(servers), new TypeReference>() {});
- }
- catch (JsonProcessingException e) {
- LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
- }
-
+ List serversCopy = cloneViaJson(servers, new TypeReference>() {}, springDocProviders.jsonMapper());
+
openAPIService.getContext().getBeansOfType(OpenApiLocaleCustomizer.class).values().forEach(openApiLocaleCustomizer -> openApiLocaleCustomizer.customise(openAPI, finalLocale));
springDocCustomizers.getOpenApiCustomizers().ifPresent(apiCustomizers -> apiCustomizers.forEach(openApiCustomizer -> openApiCustomizer.customise(openAPI)));
if (!CollectionUtils.isEmpty(openAPI.getServers()) && !openAPI.getServers().equals(serversCopy))
@@ -412,12 +409,12 @@ protected OpenAPI getOpenApi(Locale locale) {
else {
LOGGER.debug("Fetching openApi document from cache");
openAPI = openAPIService.getCachedOpenAPI(finalLocale);
- openAPIService.updateServers(openAPI);
+ openAPIService.updateServers(serverBaseUrl, openAPI);
}
- openAPIService.updateServers(openAPI);
return openAPI;
}
finally {
+ SpringDocAnnotationsUtils.clearCache(operationParser.getJavadocProvider());
this.reentrantLock.unlock();
}
}
@@ -426,8 +423,8 @@ private Locale selectLocale(Locale inputLocale) {
List allowedLocales = springDocConfigProperties.getAllowedLocales();
if (!CollectionUtils.isEmpty(allowedLocales)) {
Locale bestMatchingAllowedLocale = Locale.lookup(
- Locale.LanguageRange.parse(inputLocale.toLanguageTag()),
- allowedLocales.stream().map(Locale::forLanguageTag).collect(Collectors.toList())
+ Locale.LanguageRange.parse(inputLocale.toLanguageTag()),
+ allowedLocales.stream().map(Locale::forLanguageTag).toList()
);
return bestMatchingAllowedLocale == null ? Locale.forLanguageTag(allowedLocales.get(0)) : bestMatchingAllowedLocale;
@@ -523,8 +520,12 @@ private void trimIndentOperation(Operation operation) {
* @param locale the locale
*/
protected void calculateWebhooks(OpenAPI calculatedOpenAPI, Locale locale) {
- Webhooks[] webhooksAttr = openAPIService.getWebhooks();
- if(ArrayUtils.isEmpty(webhooksAttr))
+ Class>[] classes = openAPIService.getWebhooksClasses();
+ Class>[] refinedClasses = Arrays.stream(classes)
+ .filter(clazz -> isPackageToScan(clazz.getPackage()))
+ .toArray(Class>[]::new);
+ Webhooks[] webhooksAttr = openAPIService.getWebhooks(refinedClasses);
+ if (ArrayUtils.isEmpty(webhooksAttr))
return;
var webhooks = Arrays.stream(webhooksAttr).map(Webhooks::value).flatMap(Arrays::stream).toArray(Webhook[]::new);
Arrays.stream(webhooks).forEach(webhook -> {
@@ -649,7 +650,7 @@ protected void calculatePath(HandlerMethod handlerMethod, RouterOperation router
buildCallbacks(openAPI, methodAttributes, operation, apiCallbacks);
// allow for customisation
- operation = customizeOperation(operation, handlerMethod);
+ operation = customizeOperation(operation, components, handlerMethod);
if (StringUtils.contains(operationPath, "*")) {
Matcher matcher = pathPattern.matcher(operationPath);
@@ -659,7 +660,7 @@ protected void calculatePath(HandlerMethod handlerMethod, RouterOperation router
operationPath = operationPath.replace("{" + pathParam + "}", "{" + newPathParam + "}");
}
}
-
+
PathItem pathItemObject = buildPathItem(requestMethod, operation, operationPath, paths);
paths.addPathItem(operationPath, pathItemObject);
}
@@ -1011,15 +1012,20 @@ protected Set getDefaultAllowedHttpMethods() {
* Customise operation.
*
* @param operation the operation
+ * @param components
* @param handlerMethod the handler method
* @return the operation
*/
- protected Operation customizeOperation(Operation operation, HandlerMethod handlerMethod) {
+ protected Operation customizeOperation(Operation operation, Components components, HandlerMethod handlerMethod) {
Optional> optionalOperationCustomizers = springDocCustomizers.getOperationCustomizers();
if (optionalOperationCustomizers.isPresent()) {
Set operationCustomizerList = optionalOperationCustomizers.get();
- for (OperationCustomizer operationCustomizer : operationCustomizerList)
- operation = operationCustomizer.customize(operation, handlerMethod);
+ for (OperationCustomizer operationCustomizer : operationCustomizerList) {
+ if (operationCustomizer instanceof GlobalOperationComponentsCustomizer globalOperationComponentsCustomizer)
+ operation = globalOperationComponentsCustomizer.customize(operation, components, handlerMethod);
+ else
+ operation = operationCustomizer.customize(operation, handlerMethod);
+ }
}
return operation;
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/ErrorMessage.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/ErrorMessage.java
index 7a076bfd0..832f9af07 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/ErrorMessage.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/ErrorMessage.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.api;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/OpenApiResourceNotFoundException.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/OpenApiResourceNotFoundException.java
index 3dd2e7e39..46f0e01b9 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/OpenApiResourceNotFoundException.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/OpenApiResourceNotFoundException.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.api;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/ParameterObject.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/ParameterObject.java
index 116b7f231..858f69935 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/ParameterObject.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/ParameterObject.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.annotations;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java
index 79f31ca43..867402354 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.annotations;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperations.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperations.java
index a3495cb7f..2a5a82cdf 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperations.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/annotations/RouterOperations.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.annotations;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/CacheOrGroupedOpenApiCondition.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/CacheOrGroupedOpenApiCondition.java
index ae8da1105..1ac367413 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/CacheOrGroupedOpenApiCondition.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/CacheOrGroupedOpenApiCondition.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.conditions;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java
index b7246fc9a..58c394086 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.conditions;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiSupportCondition.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiSupportCondition.java
index 3c050d0cd..9b16b8b75 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiSupportCondition.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiSupportCondition.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.conditions;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/SpecPropertiesCondition.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/SpecPropertiesCondition.java
index b964da9ec..6f6ee89f8 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/SpecPropertiesCondition.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/SpecPropertiesCondition.java
@@ -21,13 +21,13 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.conditions;
/**
- * @author bnasslahsen
+ *
*/
import java.util.Set;
@@ -49,7 +49,7 @@
* @author bnasslahsen
*/
public class SpecPropertiesCondition implements Condition {
-
+
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
final BindResult result = Binder.get(context.getEnvironment())
@@ -63,5 +63,5 @@ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
}
return false;
}
-
+
}
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 58c7ad34d..9bdbd742d 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
@@ -34,6 +34,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.Future;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.querydsl.core.types.Predicate;
@@ -67,6 +68,7 @@
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springdoc.core.customizers.OperationIdCustomizer;
+import org.springdoc.core.customizers.ParameterCustomizer;
import org.springdoc.core.customizers.ParameterObjectNamingStrategyCustomizer;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springdoc.core.customizers.QuerydslPredicateOperationCustomizer;
@@ -74,6 +76,7 @@
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.customizers.SpringDocCustomizers;
import org.springdoc.core.discoverer.SpringDocParameterNameDiscoverer;
+import org.springdoc.core.extractor.MethodParameterPojoExtractor;
import org.springdoc.core.filters.GlobalOpenApiMethodFilter;
import org.springdoc.core.filters.OpenApiMethodFilter;
import org.springdoc.core.models.GroupedOpenApi;
@@ -96,6 +99,8 @@
import org.springdoc.core.service.RequestBodyService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
+import org.springdoc.core.utils.SchemaUtils;
+import org.springdoc.core.utils.SpringDocKotlinUtils;
import reactor.core.publisher.Flux;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@@ -129,6 +134,8 @@
import static org.springdoc.core.utils.Constants.GLOBAL_OPEN_API_CUSTOMIZER;
import static org.springdoc.core.utils.Constants.SPRINGDOC_DEPRECATING_CONVERTER_ENABLED;
import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED;
+import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLE_EXTRA_SCHEMAS;
+import static org.springdoc.core.utils.Constants.SPRINGDOC_EXPLICIT_OBJECT_SCHEMA;
import static org.springdoc.core.utils.Constants.SPRINGDOC_POLYMORPHIC_CONVERTER_ENABLED;
import static org.springdoc.core.utils.Constants.SPRINGDOC_SCHEMA_RESOLVE_PROPERTIES;
import static org.springdoc.core.utils.Constants.SPRINGDOC_SHOW_ACTUATOR;
@@ -153,7 +160,8 @@ public class SpringDocConfiguration {
static {
getConfig().replaceWithSchema(ObjectNode.class, new ObjectSchema())
.replaceWithClass(Charset.class, String.class)
- .addResponseWrapperToIgnore(DeferredResult.class);
+ .addResponseWrapperToIgnore(DeferredResult.class)
+ .addResponseWrapperToIgnore(Future.class);
}
/**
@@ -183,6 +191,14 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor2() {
return SpringdocBeanFactoryConfigurer::initBeanFactoryPostProcessor;
}
+ @Bean
+ @Lazy(false)
+ @ConditionalOnProperty(name = SPRINGDOC_ENABLE_EXTRA_SCHEMAS, matchIfMissing = true)
+ Object initExtraSchemas() {
+ getConfig().initExtraSchemas();
+ return null;
+ }
+
/**
* Local spring doc parameter name discoverer local variable table parameter name discoverer.
*
@@ -371,7 +387,6 @@ SecurityService securityParser(PropertyResolverUtils propertyResolverUtils) {
* Parameter builder generic parameter builder.
*
* @param propertyResolverUtils the property resolver utils
- * @param optionalDelegatingMethodParameterCustomizers the optional list delegating method parameter customizer
* @param optionalWebConversionServiceProvider the optional web conversion service provider
* @param objectMapperProvider the object mapper provider
* @param javadocProvider the javadoc provider
@@ -381,9 +396,8 @@ SecurityService securityParser(PropertyResolverUtils propertyResolverUtils) {
@ConditionalOnMissingBean
@Lazy(false)
GenericParameterService parameterBuilder(PropertyResolverUtils propertyResolverUtils,
- Optional> optionalDelegatingMethodParameterCustomizers,
Optional optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, Optional javadocProvider) {
- return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizers,
+ return new GenericParameterService(propertyResolverUtils,
optionalWebConversionServiceProvider, objectMapperProvider, javadocProvider);
}
@@ -441,6 +455,77 @@ ObjectMapperProvider springdocObjectMapperProvider(SpringDocConfigProperties spr
return new ObjectMapperProvider(springDocConfigProperties);
}
+ /**
+ * Spring doc customizers spring doc customizers.
+ *
+ * @param openApiCustomizers the open api customizers
+ * @param operationCustomizers the operation customizers
+ * @param routerOperationCustomizers the router operation customizers
+ * @param dataRestRouterOperationCustomizers the data rest router operation customizers
+ * @param methodFilters the method filters
+ * @param globalOpenApiCustomizers the global open api customizers
+ * @param globalOperationCustomizers the global operation customizers
+ * @param globalOpenApiMethodFilters the global open api method filters
+ * @return the spring doc customizers
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @Lazy(false)
+ public SpringDocCustomizers springDocCustomizers(Optional> openApiCustomizers,
+ Optional> operationCustomizers,
+ Optional> routerOperationCustomizers,
+ Optional> dataRestRouterOperationCustomizers,
+ Optional> methodFilters, Optional> globalOpenApiCustomizers,
+ Optional> globalOperationCustomizers,
+ Optional> globalOpenApiMethodFilters,
+ Optional> optionalDelegatingMethodParameterCustomizers,
+ Optional> parameterCustomizers) {
+ return new SpringDocCustomizers(openApiCustomizers,
+ operationCustomizers,
+ routerOperationCustomizers,
+ dataRestRouterOperationCustomizers,
+ methodFilters, globalOpenApiCustomizers, globalOperationCustomizers, globalOpenApiMethodFilters,
+ optionalDelegatingMethodParameterCustomizers, parameterCustomizers);
+ }
+
+ /**
+ * Parameter object naming strategy customizer delegating method parameter customizer.
+ *
+ * @return the delegating method parameter customizer
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @Lazy(false)
+ ParameterObjectNamingStrategyCustomizer parameterObjectNamingStrategyCustomizer() {
+ return new ParameterObjectNamingStrategyCustomizer();
+ }
+
+ /**
+ * Global open api customizer global open api customizer.
+ *
+ * @return the global open api customizer
+ */
+ @Bean
+ @ConditionalOnMissingBean(name = GLOBAL_OPEN_API_CUSTOMIZER)
+ @Lazy(false)
+ 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
+ @ConditionalOnProperty(name = SPRINGDOC_EXPLICIT_OBJECT_SCHEMA, havingValue = "true")
+ @Lazy(false)
+ OAS31ModelConverter oas31ModelConverter(SpringDocConfigProperties springDocConfigProperties) {
+ return springDocConfigProperties.isOpenapi31() ? new OAS31ModelConverter() : null;
+ }
+
/**
* The type Spring doc web mvc actuator configuration.
*
@@ -558,57 +643,6 @@ WebFluxSupportConverter webFluxSupportConverter(ObjectMapperProvider objectMappe
}
- /**
- * The type Open api resource advice.
- *
- * @author bnasslashen
- */
- @RestControllerAdvice
- @Hidden
- class OpenApiResourceAdvice {
- /**
- * Handle no handler found response entity.
- *
- * @param e the e
- * @return the response entity
- */
- @ExceptionHandler(OpenApiResourceNotFoundException.class)
- @ResponseStatus(HttpStatus.NOT_FOUND)
- public ResponseEntity handleNoHandlerFound(OpenApiResourceNotFoundException e) {
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorMessage(e.getMessage()));
- }
- }
-
- /**
- * Spring doc customizers spring doc customizers.
- *
- * @param openApiCustomizers the open api customizers
- * @param operationCustomizers the operation customizers
- * @param routerOperationCustomizers the router operation customizers
- * @param dataRestRouterOperationCustomizers the data rest router operation customizers
- * @param methodFilters the method filters
- * @param globalOpenApiCustomizers the global open api customizers
- * @param globalOperationCustomizers the global operation customizers
- * @param globalOpenApiMethodFilters the global open api method filters
- * @return the spring doc customizers
- */
- @Bean
- @ConditionalOnMissingBean
- @Lazy(false)
- public SpringDocCustomizers springDocCustomizers(Optional> openApiCustomizers,
- Optional> operationCustomizers,
- Optional> routerOperationCustomizers,
- Optional> dataRestRouterOperationCustomizers,
- Optional> methodFilters, Optional> globalOpenApiCustomizers,
- Optional> globalOperationCustomizers,
- Optional> globalOpenApiMethodFilters) {
- return new SpringDocCustomizers(openApiCustomizers,
- operationCustomizers,
- routerOperationCustomizers,
- dataRestRouterOperationCustomizers,
- methodFilters, globalOpenApiCustomizers, globalOperationCustomizers, globalOpenApiMethodFilters);
- }
-
/**
* The type Querydsl provider.
*
@@ -638,39 +672,49 @@ QuerydslPredicateOperationCustomizer queryDslQuerydslPredicateOperationCustomize
}
/**
- * Parameter object naming strategy customizer delegating method parameter customizer.
+ * The type Open api resource advice.
*
- * @return the delegating method parameter customizer
+ * @author bnasslashen
*/
- @Bean
- @ConditionalOnMissingBean
- @Lazy(false)
- ParameterObjectNamingStrategyCustomizer parameterObjectNamingStrategyCustomizer() {
- return new ParameterObjectNamingStrategyCustomizer();
+ @RestControllerAdvice
+ @Hidden
+ class OpenApiResourceAdvice {
+ /**
+ * Handle no handler found response entity.
+ *
+ * @param e the e
+ * @return the response entity
+ */
+ @ExceptionHandler(OpenApiResourceNotFoundException.class)
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public ResponseEntity handleNoHandlerFound(OpenApiResourceNotFoundException e) {
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorMessage(e.getMessage()));
+ }
}
/**
- * Global open api customizer global open api customizer.
+ * Schema utils schema utils.
*
- * @return the global open api customizer
+ * @param springDocKotlinUtils the spring doc kotlin utils
+ * @return the schema utils
*/
@Bean
- @ConditionalOnMissingBean(name = GLOBAL_OPEN_API_CUSTOMIZER)
+ @ConditionalOnMissingBean
@Lazy(false)
- GlobalOpenApiCustomizer globalOpenApiCustomizer() {
- return new OperationIdCustomizer();
+ SchemaUtils schemaUtils(Optional springDocKotlinUtils){
+ return new SchemaUtils(springDocKotlinUtils);
}
/**
- * Oas 31 model converter oas 31 model converter.
+ * Method parameter pojo extractor method parameter pojo extractor.
*
- * @param springDocConfigProperties the spring doc config properties
- * @return the oas 31 model converter
+ * @param schemaUtils the schema utils
+ * @return the method parameter pojo extractor
*/
@Bean
@ConditionalOnMissingBean
@Lazy(false)
- OAS31ModelConverter oas31ModelConverter(SpringDocConfigProperties springDocConfigProperties) {
- return springDocConfigProperties.isOpenapi31() ? new OAS31ModelConverter() : null;
+ MethodParameterPojoExtractor methodParameterPojoExtractor(SchemaUtils schemaUtils){
+ return new MethodParameterPojoExtractor(schemaUtils);
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocDataRestConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocDataRestConfiguration.java
index f7d17aefb..56442ba1e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocDataRestConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocDataRestConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -42,11 +42,9 @@
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.core.providers.SpringRepositoryRestResourceProvider;
import org.springdoc.core.service.AbstractRequestService;
-import org.springdoc.core.service.GenericParameterService;
import org.springdoc.core.service.GenericResponseService;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.OperationService;
-import org.springdoc.core.service.RequestBodyService;
import org.springdoc.core.utils.SpringDocDataRestUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -128,8 +126,8 @@ static class SpringRepositoryRestResourceProviderConfiguration {
@Bean
@ConditionalOnMissingBean
@Lazy(false)
- SpringRepositoryRestResourceProvider springRepositoryRestResourceProvider(DataRestRouterOperationService dataRestRouterOperationService,
- ObjectMapper mapper,
+ SpringRepositoryRestResourceProvider springRepositoryRestResourceProvider(DataRestRouterOperationService dataRestRouterOperationService,
+ ObjectMapper mapper,
SpringDocDataRestUtils springDocDataRestUtils) {
return new SpringRepositoryRestResourceProvider(
dataRestRouterOperationService, mapper, springDocDataRestUtils);
@@ -173,8 +171,6 @@ DataRestOperationService dataRestOperationBuilder(DataRestRequestService dataRes
* Data rest request builder data rest request builder.
*
* @param localSpringDocParameterNameDiscoverer the local spring doc parameter name discoverer
- * @param parameterBuilder the parameter builder
- * @param requestBodyService the request body builder
* @param requestBuilder the request builder
* @param springDocDataRestUtils the spring doc data rest utils
* @return the data rest request builder
@@ -182,10 +178,9 @@ DataRestOperationService dataRestOperationBuilder(DataRestRequestService dataRes
@Bean
@ConditionalOnMissingBean
@Lazy(false)
- DataRestRequestService dataRestRequestBuilder(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer, GenericParameterService parameterBuilder,
- RequestBodyService requestBodyService, AbstractRequestService requestBuilder, SpringDocDataRestUtils springDocDataRestUtils) {
- return new DataRestRequestService(localSpringDocParameterNameDiscoverer, parameterBuilder,
- requestBodyService, requestBuilder, springDocDataRestUtils);
+ DataRestRequestService dataRestRequestBuilder(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer,
+ AbstractRequestService requestBuilder, SpringDocDataRestUtils springDocDataRestUtils) {
+ return new DataRestRequestService(localSpringDocParameterNameDiscoverer, requestBuilder, springDocDataRestUtils);
}
/**
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocFunctionCatalogConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocFunctionCatalogConfiguration.java
index e50f4b00f..d15d8ab70 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocFunctionCatalogConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocFunctionCatalogConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocGroovyConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocGroovyConfiguration.java
index a5d19c91b..589d5caca 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocGroovyConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocGroovyConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
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 17fb5c28a..3f93c82b6 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -122,8 +122,7 @@ GlobalOpenApiCustomizer linksSchemaCustomizer(HateoasHalProvider halProvider, Sp
@ConditionalOnMissingBean
@Lazy(false)
HateoasLinksConverter hateoasLinksConverter(ObjectMapperProvider springDocObjectMapper) {
- return new HateoasLinksConverter(springDocObjectMapper) ;
+ return new HateoasLinksConverter(springDocObjectMapper);
}
-
-
+
}
\ No newline at end of file
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJacksonKotlinModuleConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJacksonKotlinModuleConfiguration.java
index 5316593b0..51d54aecd 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJacksonKotlinModuleConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJacksonKotlinModuleConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -61,7 +61,7 @@ public class SpringDocJacksonKotlinModuleConfiguration {
@Bean
@Primary
ObjectMapperProvider springdocKotlinObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties) {
- ObjectMapperProvider mapperProvider = new ObjectMapperProvider(springDocConfigProperties);
+ ObjectMapperProvider mapperProvider = new ObjectMapperProvider(springDocConfigProperties);
mapperProvider.jsonMapper().registerModule(new KotlinModule.Builder().build());
return mapperProvider;
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJavadocConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJavadocConfiguration.java
index 981878603..fd65c9982 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJavadocConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocJavadocConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt
index d11d0cb91..11812085b 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt
@@ -26,11 +26,11 @@
package org.springdoc.core.configuration
-import io.swagger.v3.oas.annotations.Parameter
+import org.springdoc.core.converters.KotlinInlineClassUnwrappingConverter
import org.springdoc.core.customizers.KotlinDeprecatedPropertyCustomizer
-import org.springdoc.core.customizers.ParameterCustomizer
import org.springdoc.core.providers.ObjectMapperProvider
import org.springdoc.core.utils.Constants
+import org.springdoc.core.utils.SpringDocKotlinUtils
import org.springdoc.core.utils.SpringDocUtils
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
@@ -41,14 +41,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Lazy
-import org.springframework.core.KotlinDetector
-import org.springframework.core.MethodParameter
-import org.springframework.core.annotation.AnnotatedElementUtils
-import org.springframework.web.bind.annotation.RequestParam
-import org.springframework.web.bind.annotation.ValueConstants
import kotlin.coroutines.Continuation
-import kotlin.reflect.KParameter
-import kotlin.reflect.jvm.kotlinFunction
/**
* The type Spring doc kotlin configuration.
@@ -74,60 +67,6 @@ class SpringDocKotlinConfiguration() {
.addDeprecatedType(Deprecated::class.java)
}
- /**
- * Kotlin springdoc-openapi ParameterCustomizer
- *
- * @return the nullable Kotlin Request Parameter Customizer
- */
- @Bean
- @Lazy(false)
- @ConditionalOnProperty(
- name = [Constants.SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED],
- matchIfMissing = true
- )
- @ConditionalOnMissingBean
- fun nullableKotlinRequestParameterCustomizer(): ParameterCustomizer {
- return ParameterCustomizer { parameterModel, methodParameter ->
- if (parameterModel == null) return@ParameterCustomizer null
- if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(methodParameter.parameterType)) {
- val kParameter = methodParameter.toKParameter()
- if (kParameter != null) {
- val parameterDoc = AnnotatedElementUtils.findMergedAnnotation(
- AnnotatedElementUtils.forAnnotations(*methodParameter.parameterAnnotations),
- Parameter::class.java
- )
- val requestParam = AnnotatedElementUtils.findMergedAnnotation(
- AnnotatedElementUtils.forAnnotations(*methodParameter.parameterAnnotations),
- RequestParam::class.java
- )
- // Swagger @Parameter annotation takes precedence
- if (parameterDoc != null && parameterDoc.required)
- parameterModel.required = parameterDoc.required
- // parameter is not required if a default value is provided in @RequestParam
- else if (requestParam != null && requestParam.defaultValue != ValueConstants.DEFAULT_NONE)
- parameterModel.required = false
- else{
- val isJavaNullableAnnotationPresent = methodParameter.parameterAnnotations.any {
- it.annotationClass.qualifiedName == "jakarta.annotation.Nullable"
- }
- parameterModel.required =
- kParameter.type.isMarkedNullable == false && !isJavaNullableAnnotationPresent
- }
- }
- }
- return@ParameterCustomizer parameterModel
- }
- }
-
- private fun MethodParameter.toKParameter(): KParameter? {
- // ignore return type, see org.springframework.core.MethodParameter.getParameterIndex
- if (parameterIndex == -1) return null
- val kotlinFunction = method?.kotlinFunction ?: return null
- // The first parameter of the kotlin function is the "this" reference and not needed here.
- // See also kotlin.reflect.KCallable.getParameters
- return kotlinFunction.parameters[parameterIndex + 1]
- }
-
@ConditionalOnClass(name = ["kotlin.reflect.full.KClasses"])
class KotlinReflectDependingConfiguration {
@@ -137,6 +76,22 @@ class SpringDocKotlinConfiguration() {
fun kotlinDeprecatedPropertyCustomizer(objectMapperProvider: ObjectMapperProvider): KotlinDeprecatedPropertyCustomizer {
return KotlinDeprecatedPropertyCustomizer(objectMapperProvider)
}
+
+ @Bean
+ @Lazy(false)
+ @ConditionalOnMissingBean
+ fun kotlinModelConverter(objectMapperProvider: ObjectMapperProvider): KotlinInlineClassUnwrappingConverter {
+ return KotlinInlineClassUnwrappingConverter(objectMapperProvider)
+ }
+
+ @Bean
+ @ConditionalOnProperty(
+ name = [Constants.SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED],
+ matchIfMissing = true
+ )
+ @Lazy(false)
+ fun springDocKotlinUtils(): SpringDocKotlinUtils =
+ SpringDocKotlinUtils()
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinxConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinxConfiguration.java
index ca51110ad..f1b5706c6 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinxConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinxConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java
index d77d5dbc0..57de3cd76 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -95,8 +95,8 @@ PageableOpenAPIConverter pageableOpenAPIConverter(ObjectMapperProvider objectMap
PageOpenAPIConverter pageOpenAPIConverter(Optional settings,
ObjectMapperProvider objectMapperProvider) {
boolean replacePageWithPagedModel = settings.map(SpringDataWebSettings::pageSerializationMode)
- .map(EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO::equals)
- .orElse(false);
+ .map(EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO::equals)
+ .orElse(false);
return new PageOpenAPIConverter(replacePageWithPagedModel, objectMapperProvider);
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocRequiredModule.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocRequiredModule.java
index 1c2380106..b36a74a4f 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocRequiredModule.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocRequiredModule.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSealedClassModule.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSealedClassModule.java
index d0b27ad81..aa459270e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSealedClassModule.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSealedClassModule.java
@@ -26,10 +26,12 @@
import java.util.Arrays;
import java.util.List;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.module.SimpleModule;
import io.swagger.v3.core.jackson.SwaggerAnnotationIntrospector;
+import io.swagger.v3.oas.annotations.media.Schema;
/**
* The type Spring doc sealed class module.
@@ -52,10 +54,22 @@ private static class RespectSealedClassAnnotationIntrospector extends SwaggerAnn
public List findSubtypes(Annotated annotated) {
ArrayList subTypes = new ArrayList<>();
- if (annotated.getAnnotated() instanceof Class> clazz
+ if (annotated.getAnnotated() instanceof Class> clazz
&& clazz.isSealed()
&& !clazz.getPackage().getName().startsWith("java")
) {
+
+ Schema schema = clazz.getAnnotation(Schema.class);
+ if (schema != null && schema.oneOf().length > 0) {
+ return new ArrayList<>();
+ }
+
+ JsonSubTypes jsonSubTypes = clazz.getAnnotation(JsonSubTypes.class);
+ if (jsonSubTypes != null && jsonSubTypes.value().length > 0) {
+ return new ArrayList<>();
+ }
+
+
Class>[] permittedSubClasses = clazz.getPermittedSubclasses();
if (permittedSubClasses.length > 0) {
Arrays.stream(permittedSubClasses).map(NamedType::new).forEach(subTypes::add);
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java
index 051b2b20e..728c4dfa4 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -66,7 +66,10 @@
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
+import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.web.util.pattern.PathPattern;
import static org.springdoc.core.utils.Constants.SPRINGDOC_SHOW_LOGIN_ENDPOINT;
import static org.springdoc.core.utils.Constants.SPRINGDOC_SHOW_OAUTH2_ENDPOINTS;
@@ -155,8 +158,26 @@ OpenApiCustomizer springSecurityLoginEndpointCustomizer(ApplicationContext appli
operation.addTagsItem("login-endpoint");
PathItem pathItem = new PathItem().post(operation);
try {
- AntPathRequestMatcher requestMatcher = (AntPathRequestMatcher)FieldUtils.readField(usernamePasswordAuthenticationFilter, "requiresAuthenticationRequestMatcher", true);
- String loginPath = requestMatcher.getPattern();
+ RequestMatcher requestMatcher = (RequestMatcher) FieldUtils.readField(
+ usernamePasswordAuthenticationFilter,
+ "requiresAuthenticationRequestMatcher",
+ true
+ );
+
+ String loginPath = null;
+
+ if (requestMatcher instanceof AntPathRequestMatcher) {
+ loginPath = ((AntPathRequestMatcher) requestMatcher).getPattern();
+ }
+ else if (requestMatcher instanceof PathPatternRequestMatcher) {
+ PathPattern pathPattern = (PathPattern) FieldUtils.readField(
+ requestMatcher,
+ "pattern",
+ true
+ );
+ loginPath = pathPattern.getPatternString();
+ }
+
openAPI.getPaths().addPathItem(loginPath, pathItem);
}
catch (IllegalAccessException |
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java
index 94b933eca..dfdfb4cd4 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -114,6 +114,21 @@ public class SpringDocSecurityOAuth2Customizer implements GlobalOpenApiCustomize
*/
private ApplicationContext applicationContext;
+ /**
+ * Build o auth 2 error.
+ *
+ * @param openAPI the open api
+ * @param apiResponses the api responses
+ * @param httpStatus the http status
+ * @param openapi31 the openapi 31
+ */
+ private static void buildOAuth2Error(OpenAPI openAPI, ApiResponses apiResponses, HttpStatus httpStatus, boolean openapi31) {
+ Schema oAuth2ErrorSchema = AnnotationsUtils.resolveSchemaFromType(OAuth2Error.class, openAPI.getComponents(), null, openapi31);
+ apiResponses.addApiResponse(String.valueOf(httpStatus.value()), new ApiResponse().description(httpStatus.getReasonPhrase()).content(new Content().addMediaType(
+ APPLICATION_JSON_VALUE,
+ new MediaType().schema(oAuth2ErrorSchema))));
+ }
+
@Override
public void customise(OpenAPI openAPI) {
FilterChainProxy filterChainProxy = applicationContext.getBean(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, FilterChainProxy.class);
@@ -196,7 +211,7 @@ private void getOAuth2TokenIntrospectionEndpointFilter(OpenAPI openAPI, Security
* @param openapi31 the openapi 31
*/
private void getOAuth2AuthorizationServerMetadataEndpoint(OpenAPI openAPI, SecurityFilterChain securityFilterChain, boolean openapi31) {
- ClassauthorizationServerMetadataEndpointClass = OAuth2AuthorizationServerMetadataEndpointFilter.class;
+ Class authorizationServerMetadataEndpointClass = OAuth2AuthorizationServerMetadataEndpointFilter.class;
Object oAuth2EndpointFilter =
new SpringDocSecurityOAuth2EndpointUtils(authorizationServerMetadataEndpointClass).findEndpoint(securityFilterChain);
if (oAuth2EndpointFilter != null) {
@@ -208,11 +223,11 @@ private void getOAuth2AuthorizationServerMetadataEndpoint(OpenAPI openAPI, Secur
if (field != null) {
ReflectionUtils.makeAccessible(field);
String defaultOauth2MetadataUri = (String) ReflectionUtils.getField(field, null);
- openAPI.getPaths().addPathItem(defaultOauth2MetadataUri , new PathItem().get(operation));
+ openAPI.getPaths().addPathItem(defaultOauth2MetadataUri, new PathItem().get(operation));
operation = buildOperation(apiResponses);
operation.addParametersItem(new PathParameter().name("subpath").schema(new StringSchema()));
operation.summary("Valid when multiple issuers are allowed");
- openAPI.getPaths().addPathItem(defaultOauth2MetadataUri+"/{subpath}" , new PathItem().get(operation));
+ openAPI.getPaths().addPathItem(defaultOauth2MetadataUri + "/{subpath}", new PathItem().get(operation));
}
}
}
@@ -340,11 +355,11 @@ private void getOidcProviderConfigurationEndpoint(OpenAPI openAPI, SecurityFilte
if (field != null) {
ReflectionUtils.makeAccessible(field);
String defaultOidcConfigUri = (String) ReflectionUtils.getField(field, null);
- openAPI.getPaths().addPathItem(defaultOidcConfigUri , new PathItem().get(operation));
+ openAPI.getPaths().addPathItem(defaultOidcConfigUri, new PathItem().get(operation));
operation = buildOperation(apiResponses);
operation.addParametersItem(new PathParameter().name("subpath").schema(new StringSchema()));
operation.summary("Valid when multiple issuers are allowed");
- openAPI.getPaths().addPathItem("/{subpath}"+defaultOidcConfigUri , new PathItem().get(operation));
+ openAPI.getPaths().addPathItem("/{subpath}" + defaultOidcConfigUri, new PathItem().get(operation));
}
}
}
@@ -382,7 +397,7 @@ private void getOidcClientRegistrationEndpoint(OpenAPI openAPI, SecurityFilterCh
if (oAuth2EndpointFilter != null) {
ApiResponses apiResponses = new ApiResponses();
- buildApiResponsesOnCreated(apiResponses, AnnotationsUtils.resolveSchemaFromType(SpringDocOidcClientRegistrationResponse.class, openAPI.getComponents(), null,openapi31 ));
+ buildApiResponsesOnCreated(apiResponses, AnnotationsUtils.resolveSchemaFromType(SpringDocOidcClientRegistrationResponse.class, openAPI.getComponents(), null, openapi31));
buildApiResponsesOnInternalServerError(apiResponses);
buildApiResponsesOnBadRequest(apiResponses, openAPI, openapi31);
buildOAuth2Error(openAPI, apiResponses, HttpStatus.UNAUTHORIZED, openapi31);
@@ -468,21 +483,6 @@ private ApiResponses buildApiResponsesOnBadRequest(ApiResponses apiResponses, Op
return apiResponses;
}
- /**
- * Build o auth 2 error.
- *
- * @param openAPI the open api
- * @param apiResponses the api responses
- * @param httpStatus the http status
- * @param openapi31 the openapi 31
- */
- private static void buildOAuth2Error(OpenAPI openAPI, ApiResponses apiResponses, HttpStatus httpStatus, boolean openapi31) {
- Schema oAuth2ErrorSchema = AnnotationsUtils.resolveSchemaFromType(OAuth2Error.class, openAPI.getComponents(), null, openapi31);
- apiResponses.addApiResponse(String.valueOf(httpStatus.value()), new ApiResponse().description(httpStatus.getReasonPhrase()).content(new Content().addMediaType(
- APPLICATION_JSON_VALUE,
- new MediaType().schema(oAuth2ErrorSchema))));
- }
-
/**
* Build path.
*
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2EndpointUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2EndpointUtils.java
index 201db0de2..07fca30d4 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2EndpointUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2EndpointUtils.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -60,10 +60,10 @@ public SpringDocSecurityOAuth2EndpointUtils(T oauth2EndpointFilter) {
*/
public Object findEndpoint(SecurityFilterChain filterChain) {
Optional> oAuth2EndpointFilterOptional =
- filterChain.getFilters().stream()
- .filter(((Class >) oauth2EndpointFilter)::isInstance)
- .map(((Class >) oauth2EndpointFilter)::cast)
- .findAny();
+ filterChain.getFilters().stream()
+ .filter(((Class>) oauth2EndpointFilter)::isInstance)
+ .map(((Class>) oauth2EndpointFilter)::cast)
+ .findAny();
return oAuth2EndpointFilterOptional.orElse(null);
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSortConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSortConfiguration.java
index ff751eb5e..de665ed0c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSortConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSortConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSpecPropertiesConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSpecPropertiesConfiguration.java
index 5e87fb67e..8893299ab 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSpecPropertiesConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSpecPropertiesConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration;
@@ -63,13 +63,13 @@ public class SpringDocSpecPropertiesConfiguration {
* @return the springdoc customizer
*/
@Bean
- @ConditionalOnMissingBean
- @Lazy(false)
+ @ConditionalOnMissingBean
+ @Lazy(false)
SpecPropertiesCustomizer specificationStringPropertiesCustomizer(
- SpringDocConfigProperties springDocConfigProperties
- ) {
- return new SpecPropertiesCustomizer(springDocConfigProperties);
- }
+ SpringDocConfigProperties springDocConfigProperties
+ ) {
+ return new SpecPropertiesCustomizer(springDocConfigProperties);
+ }
/**
* Bean post processor that applies the specification string properties customization to
@@ -79,19 +79,19 @@ SpecPropertiesCustomizer specificationStringPropertiesCustomizer(
* @return the bean post processor
*/
@Bean
- @ConditionalOnMissingBean
- @Lazy(false)
- SpecificationStringPropertiesCustomizerBeanPostProcessor specificationStringPropertiesCustomizerBeanPostProcessor(
+ @ConditionalOnMissingBean
+ @Lazy(false)
+ SpecificationStringPropertiesCustomizerBeanPostProcessor specificationStringPropertiesCustomizerBeanPostProcessor(
SpringDocConfigProperties springDocConfigProperties
- ) {
- return new SpecificationStringPropertiesCustomizerBeanPostProcessor(springDocConfigProperties);
- }
+ ) {
+ return new SpecificationStringPropertiesCustomizerBeanPostProcessor(springDocConfigProperties);
+ }
/**
* The type Specification string properties customizer bean post processor.
*/
- private static class SpecificationStringPropertiesCustomizerBeanPostProcessor implements BeanPostProcessor {
+ public static class SpecificationStringPropertiesCustomizerBeanPostProcessor implements BeanPostProcessor {
/**
* The Spring doc config properties.
@@ -105,25 +105,24 @@ private static class SpecificationStringPropertiesCustomizerBeanPostProcessor im
*/
public SpecificationStringPropertiesCustomizerBeanPostProcessor(
SpringDocConfigProperties springDocConfigProperties
- ) {
- this.springDocConfigProperties = springDocConfigProperties;
- }
+ ) {
+ this.springDocConfigProperties = springDocConfigProperties;
+ }
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) {
- if (bean instanceof GroupedOpenApi groupedOpenApi) {
- Set groupConfigs = springDocConfigProperties.getGroupConfigs();
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) {
+ if (bean instanceof GroupedOpenApi groupedOpenApi) {
+ Set groupConfigs = springDocConfigProperties.getGroupConfigs();
for (GroupConfig groupConfig : groupConfigs) {
- if(groupConfig.getGroup().equals(groupedOpenApi.getGroup())) {
+ if (groupConfig.getGroup().equals(groupedOpenApi.getGroup())) {
groupedOpenApi.addAllOpenApiCustomizer(List.of(new SpecPropertiesCustomizer(
groupConfig.getOpenApi()
)));
}
}
- }
- return bean;
- }
- }
-
+ }
+ return bean;
+ }
+ }
}
\ No newline at end of file
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocDataRestHints.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocDataRestHints.java
index 27538ccaf..1730031e6 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocDataRestHints.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocDataRestHints.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.hints;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocHints.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocHints.java
index dad4e1b11..55eb0e7d8 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocHints.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocHints.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.hints;
@@ -196,7 +196,9 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
MemberCategory.INVOKE_DECLARED_METHODS))
.registerType(java.lang.ModuleLayer.class, MemberCategory.INVOKE_DECLARED_METHODS)
.registerType(java.lang.module.Configuration.class, MemberCategory.INVOKE_DECLARED_METHODS)
- .registerType(java.lang.module.ResolvedModule.class, MemberCategory.INVOKE_DECLARED_METHODS);
+ .registerType(java.lang.module.ResolvedModule.class, MemberCategory.INVOKE_DECLARED_METHODS)
+ .registerType(java.lang.invoke.MethodHandles.class, MemberCategory.DECLARED_CLASSES)
+ .registerType(java.lang.invoke.MethodHandles.Lookup.class);
//swagger-models
Arrays.stream(typesToRegister).forEach(aClass ->
hints.reflection().registerType(aClass,
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocSecurityHints.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocSecurityHints.java
index 0af857d8e..cb3209c59 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocSecurityHints.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/hints/SpringDocSecurityHints.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.hints;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java
index 6ffccf630..d3948bcef 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.oauth2;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2Token.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2Token.java
index 31667ef27..cdbee67f7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2Token.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2Token.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.oauth2;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2TokenIntrospection.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2TokenIntrospection.java
index cc127ce55..b9a77823a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2TokenIntrospection.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOAuth2TokenIntrospection.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.oauth2;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationRequest.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationRequest.java
index 1894545e8..4f5f3f774 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationRequest.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationRequest.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.oauth2;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationResponse.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationResponse.java
index 365f08a2c..8b404b9ce 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationResponse.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcClientRegistrationResponse.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.oauth2;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcProviderConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcProviderConfiguration.java
index 55d9d7313..62c4c607b 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcProviderConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/oauth2/SpringDocOidcProviderConfiguration.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configuration.oauth2;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocActuatorBeanFactoryConfigurer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocActuatorBeanFactoryConfigurer.java
index 1adedafcd..bf09cdd8c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocActuatorBeanFactoryConfigurer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocActuatorBeanFactoryConfigurer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configurer;
@@ -73,12 +73,12 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
.bind(MANAGEMENT_ENDPOINTS_WEB, WebEndpointProperties.class);
final BindResult springDocConfigPropertiesBindResult = Binder.get(environment)
.bind(SPRINGDOC_PREFIX, SpringDocConfigProperties.class);
-
+
if (result.isBound() && springDocConfigPropertiesBindResult.isBound()) {
WebEndpointProperties webEndpointProperties = result.get();
SpringDocConfigProperties springDocConfigProperties = springDocConfigPropertiesBindResult.get();
List newGroups = new ArrayList<>();
-
+
ActuatorOperationCustomizer actuatorCustomizer = new ActuatorOperationCustomizer(springDocConfigProperties);
beanFactory.registerSingleton("actuatorCustomizer", actuatorCustomizer);
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocBeanFactoryConfigurer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocBeanFactoryConfigurer.java
index 816f407fb..86746bf48 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocBeanFactoryConfigurer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configurer/SpringdocBeanFactoryConfigurer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.configurer;
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 7c7a34f6d..c2b47676c 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
@@ -30,7 +30,6 @@
import java.util.Iterator;
import java.util.Map;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import io.swagger.v3.core.converter.AnnotatedType;
@@ -41,6 +40,7 @@
import org.slf4j.LoggerFactory;
import org.springdoc.core.providers.ObjectMapperProvider;
+import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
/**
@@ -133,36 +133,6 @@ public static void disableReplacement(Class clazz) {
modelToClassMap.remove(clazz);
}
- /**
- * Resolve schema.
- *
- * @param type the type
- * @param context the context
- * @param chain the chain
- * @return the schema
- */
- @Override
- public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) {
- JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
- if (javaType != null) {
- 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(schema), new TypeReference() {});
- }
- catch (JsonProcessingException e) {
- LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
- }
- if (modelToClassMap.containsKey(cls))
- type = new AnnotatedType(modelToClassMap.get(cls)).resolveAsRef(true);
- }
- return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
- }
-
/**
* Remove from schema map.
*
@@ -180,4 +150,29 @@ public static void removeFromSchemaMap(Class clazz) {
public static void removeFromClassMap(Class clazz) {
modelToClassMap.remove(clazz);
}
+
+ /**
+ * Resolve schema.
+ *
+ * @param type the type
+ * @param context the context
+ * @param chain the chain
+ * @return the schema
+ */
+ @Override
+ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) {
+ JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
+ if (javaType != null) {
+ Class> cls = javaType.getRawClass();
+ if (modelToSchemaMap.containsKey(cls)) {
+ Schema schema = modelToSchemaMap.get(cls);
+ if (springDocObjectMapper.isOpenapi31())
+ handleSchemaTypes(schema);
+ return cloneViaJson(schema, new TypeReference() {}, springDocObjectMapper.jsonMapper());
+ }
+ if (modelToClassMap.containsKey(cls))
+ type = new AnnotatedType(modelToClassMap.get(cls)).resolveAsRef(true);
+ }
+ return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
+ }
}
\ No newline at end of file
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/CollectionModelContentConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/CollectionModelContentConverter.java
index 08e8e6e52..e630c06f0 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/CollectionModelContentConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/CollectionModelContentConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ConverterUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ConverterUtils.java
index b49a02e82..e52b7ec6c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ConverterUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ConverterUtils.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/FileSupportConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/FileSupportConverter.java
index 547fd4c17..f112db10a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/FileSupportConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/FileSupportConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
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
index bcde9140c..36b69f9af 100644
--- 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
@@ -77,7 +77,7 @@ public Schema> resolve(
.filter(ref -> ref.startsWith(Components.COMPONENTS_SCHEMAS_REF))
.map(ref -> ref.substring(Components.COMPONENTS_SCHEMAS_REF.length()))
.orElse(schema.getName());
- if(schemaName != null) {
+ if (schemaName != null) {
Schema original = context.getDefinedModels().get(schemaName);
if (original == null || original.getProperties() == null) {
return schema;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/JavaTypeToIgnoreConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/JavaTypeToIgnoreConverter.java
index a8457b378..482128e9a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/JavaTypeToIgnoreConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/JavaTypeToIgnoreConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/KotlinInlineClassUnwrappingConverter.kt b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/KotlinInlineClassUnwrappingConverter.kt
new file mode 100644
index 000000000..0bc789851
--- /dev/null
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/KotlinInlineClassUnwrappingConverter.kt
@@ -0,0 +1,44 @@
+package org.springdoc.core.converters
+
+import com.fasterxml.jackson.databind.JavaType
+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 org.springdoc.core.providers.ObjectMapperProvider
+import kotlin.reflect.full.findAnnotation
+import kotlin.reflect.full.primaryConstructor
+import kotlin.reflect.jvm.jvmErasure
+
+class KotlinInlineClassUnwrappingConverter(
+ private val objectMapperProvider: ObjectMapperProvider
+) : ModelConverter {
+
+ override fun resolve(
+ type: AnnotatedType?,
+ context: ModelConverterContext?,
+ chain: Iterator
+ ): Schema<*>? {
+ if (type?.type == null || context == null || !chain.hasNext()) {
+ return null
+ }
+ val javaType: JavaType =
+ objectMapperProvider.jsonMapper().constructType(type.type)
+ val kClass = javaType.rawClass.kotlin
+ if (kClass.findAnnotation() != null) {
+ val constructor = kClass.primaryConstructor
+ val param = constructor?.parameters?.firstOrNull()
+ val unwrappedClass = param?.type?.jvmErasure?.java
+ if (unwrappedClass != null) {
+ val unwrappedType = AnnotatedType()
+ .type(unwrappedClass)
+ .ctxAnnotations(type.ctxAnnotations)
+ .jsonViewAnnotation(type.jsonViewAnnotation)
+ .resolveAsRef(false)
+
+ return chain.next().resolve(unwrappedType, context, chain)
+ }
+ }
+ return chain.next().resolve(type, context, chain)
+ }
+}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ModelConverterRegistrar.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ModelConverterRegistrar.java
index 8a363c93b..5b7d279c6 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ModelConverterRegistrar.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ModelConverterRegistrar.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
@@ -44,14 +44,14 @@
public class ModelConverterRegistrar {
/**
- * The constant modelConvertersInstance.
+ * The constant LOGGER.
*/
- private final ModelConverters modelConvertersInstance;
+ private static final Logger LOGGER = LoggerFactory.getLogger(ModelConverterRegistrar.class);
/**
- * The constant LOGGER.
+ * The constant modelConvertersInstance.
*/
- private static final Logger LOGGER = LoggerFactory.getLogger(ModelConverterRegistrar.class);
+ private final ModelConverters modelConvertersInstance;
/**
* Instantiates a new Model converter registrar.
@@ -99,6 +99,6 @@ private boolean isSameConverter(ModelConverter modelConverter1, ModelConverter m
// comparing by the converter type
Class extends ModelConverter> modelConverter1Class = modelConverter1.getClass();
Class extends ModelConverter> modelConverter2Class = modelConverter2.getClass();
- return modelConverter1Class.equals(modelConverter2Class);
+ return modelConverter1Class.getName().equals(modelConverter2Class.getName());
}
}
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 34e7e7f3a..9d517981e 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
@@ -21,12 +21,11 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
-import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
@@ -82,9 +81,10 @@ public PageOpenAPIConverter(boolean replacePageWithPagedModel, ObjectMapperProvi
/**
* Resolve schema.
- * @param type the type
+ *
+ * @param type the type
* @param context the context
- * @param chain the chain
+ * @param chain the chain
* @return the schema
*/
@Override
@@ -94,7 +94,7 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
Class> cls = javaType.getRawClass();
if (replacePageWithPagedModel && PAGE_TO_REPLACE.equals(cls.getCanonicalName())) {
if (!type.isSchemaProperty())
- type = resolvePagedModelType(type);
+ type = resolvePagedModelType(javaType);
else
type.name(getParentTypeName(type, cls));
}
@@ -108,13 +108,12 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
* @param type the type
* @return the annotated type
*/
- private AnnotatedType resolvePagedModelType(AnnotatedType type) {
- Type pageType = type.getType();
- if (pageType instanceof ParameterizedType) {
- Type argumentType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
+ private AnnotatedType resolvePagedModelType(JavaType type) {
+ if (type.hasGenericTypes()) {
+ JavaType innerType = type.containedType(0);
Type pagedModelType = ResolvableType
- .forClassWithGenerics(PagedModel.class, ResolvableType.forType(argumentType))
- .getType();
+ .forClassWithGenerics(PagedModel.class, ResolvableType.forType(innerType))
+ .getType();
return new AnnotatedType(pagedModelType).resolveAsRef(true);
}
else {
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 0bae9272a..567b9234e 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
@@ -77,9 +77,9 @@ public PageableOpenAPIConverter(ObjectMapperProvider springDocObjectMapper) {
/**
* Resolve schema.
*
- * @param type the type
+ * @param type the type
* @param context the context
- * @param chain the chain
+ * @param chain the chain
* @return the schema
*/
@Override
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 9a940d349..31ad67cd7 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
@@ -26,7 +26,7 @@
package org.springdoc.core.converters;
-import java.lang.reflect.Field;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
@@ -34,10 +34,10 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
-import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.swagger.v3.core.converter.AnnotatedType;
@@ -50,9 +50,11 @@
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.reflect.FieldUtils;
import org.springdoc.core.providers.ObjectMapperProvider;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+
/**
* The type Polymorphic model converter.
*
@@ -60,11 +62,6 @@
*/
public class PolymorphicModelConverter implements ModelConverter {
- /**
- * The Spring doc object mapper.
- */
- private final ObjectMapperProvider springDocObjectMapper;
-
/**
* The constant PARENT_TYPES_TO_IGNORE.
*/
@@ -81,6 +78,11 @@ public class PolymorphicModelConverter implements ModelConverter {
PARENT_TYPES_TO_IGNORE.add("EntityModel");
}
+ /**
+ * The Spring doc object mapper.
+ */
+ private final ObjectMapperProvider springDocObjectMapper;
+
/**
* Instantiates a new Polymorphic model converter.
*
@@ -122,28 +124,20 @@ else if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getSi
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) {
JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
if (javaType != null) {
- BeanDescription javaTypeIntrospection = springDocObjectMapper.jsonMapper().getDeserializationConfig().introspect(javaType);
- for (BeanPropertyDefinition property : javaTypeIntrospection.findProperties()) {
- boolean isUnwrapped = (property.getField() != null && property.getField().hasAnnotation(JsonUnwrapped.class)) ||
- (property.getGetter() != null && property.getGetter().hasAnnotation(JsonUnwrapped.class));
-
- if (isUnwrapped) {
+ for (BeanPropertyBiDefinition propertyDef : introspectBeanProperties(javaType)) {
+ if (propertyDef.isAnyAnnotated(JsonUnwrapped.class)) {
if (!TypeNameResolver.std.getUseFqn())
PARENT_TYPES_TO_IGNORE.add(javaType.getRawClass().getSimpleName());
else
PARENT_TYPES_TO_IGNORE.add(javaType.getRawClass().getName());
}
else {
- io.swagger.v3.oas.annotations.media.Schema declaredSchema = null;
- if (property.getField() != null) {
- declaredSchema = property.getField().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
- } else if (property.getGetter() != null) {
- declaredSchema = property.getGetter().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
- }
-
+ io.swagger.v3.oas.annotations.media.Schema declaredSchema = propertyDef.getAnyAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
if (declaredSchema != null &&
- (ArrayUtils.isNotEmpty(declaredSchema.oneOf()) || ArrayUtils.isNotEmpty(declaredSchema.allOf()))) {
- TYPES_TO_SKIP.add(property.getPrimaryType().getRawClass().getSimpleName());
+ (ArrayUtils.isNotEmpty(declaredSchema.oneOf()) || ArrayUtils.isNotEmpty(declaredSchema.allOf())) &&
+ propertyDef.getPrimaryType() != null &&
+ propertyDef.getPrimaryType().getRawClass() != null) {
+ TYPES_TO_SKIP.add(propertyDef.getPrimaryType().getRawClass().getSimpleName());
}
}
}
@@ -227,4 +221,99 @@ private boolean isConcreteClass(AnnotatedType type) {
Class> clazz = javaType.getRawClass();
return !Modifier.isAbstract(clazz.getModifiers()) && !clazz.isInterface();
}
+
+ /**
+ * Introspects the properties of the given Java type based on serialization and deserialization configurations.
+ * This method identifies properties present in both JSON serialization and deserialization views,
+ * and pairs them into a list of {@code BeanPropertyBiDefinition}.
+ */
+ private List introspectBeanProperties(JavaType javaType) {
+ Map forSerializationProps =
+ springDocObjectMapper.jsonMapper()
+ .getSerializationConfig()
+ .introspect(javaType)
+ .findProperties()
+ .stream()
+ .collect(toMap(BeanPropertyDefinition::getName, identity()));
+ Map forDeserializationProps =
+ springDocObjectMapper.jsonMapper()
+ .getDeserializationConfig()
+ .introspect(javaType)
+ .findProperties()
+ .stream()
+ .collect(toMap(BeanPropertyDefinition::getName, identity()));
+
+ return forSerializationProps.keySet().stream()
+ .map(key -> new BeanPropertyBiDefinition(forSerializationProps.get(key), forDeserializationProps.get(key)))
+ .toList();
+ }
+
+ /**
+ * A record representing the bi-definition of a bean property, combining both
+ * serialization and deserialization property views.
+ */
+ private record BeanPropertyBiDefinition(
+ BeanPropertyDefinition forSerialization,
+ BeanPropertyDefinition forDeserialization
+ ) {
+
+ /**
+ * Retrieves an annotation of the specified type from either the serialization or
+ * deserialization property definition (field, getter, setter), returning the first available match.
+ */
+ public A getAnyAnnotation(Class acls) {
+ A anyForSerializationAnnotation = getAnyAnnotation(forSerialization, acls);
+ A anyForDeserializationAnnotation = getAnyAnnotation(forDeserialization, acls);
+
+ return anyForSerializationAnnotation != null ? anyForSerializationAnnotation : anyForDeserializationAnnotation;
+ }
+
+ /**
+ * Checks if any annotation of the specified type exists across serialization
+ * or deserialization property definitions.
+ */
+ public boolean isAnyAnnotated(Class acls) {
+ return getAnyAnnotation(acls) != null;
+ }
+
+ /**
+ * Type determined from the primary member for the property being built.
+ */
+ public JavaType getPrimaryType() {
+ JavaType forSerializationType = null;
+ if (forSerialization != null) {
+ forSerializationType = forSerialization.getPrimaryType();
+ }
+
+ JavaType forDeserializationType = null;
+ if (forDeserialization != null) {
+ forDeserializationType = forDeserialization.getPrimaryType();
+ }
+
+ if (forSerializationType != null && forDeserializationType != null && forSerializationType != forDeserializationType) {
+ throw new IllegalStateException("The property " + forSerialization.getName() + " has different types for serialization and deserialization: "
+ + forSerializationType + " and " + forDeserializationType);
+ }
+
+ return forSerializationType != null ? forSerializationType : forDeserializationType;
+ }
+
+ private A getAnyAnnotation(BeanPropertyDefinition prop, Class acls) {
+ if (prop == null) {
+ return null;
+ }
+
+ if (prop.getField() != null) {
+ return prop.getField().getAnnotation(acls);
+ }
+ if (prop.getGetter() != null) {
+ return prop.getGetter().getAnnotation(acls);
+ }
+ if (prop.getSetter() != null) {
+ return prop.getSetter().getAnnotation(acls);
+ }
+
+ return null;
+ }
+ }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyCustomizingConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyCustomizingConverter.java
index 85fb1e30c..f667ea278 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyCustomizingConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyCustomizingConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java
index 118152a3c..fa8539f02 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
@@ -33,6 +33,7 @@
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.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.springdoc.core.providers.ObjectMapperProvider;
@@ -73,6 +74,8 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
JavaType innerType = findResponseEntity(javaType).containedType(0);
if (innerType == null)
return new StringSchema();
+ if (Object.class.equals(innerType.getRawClass()))
+ return new ObjectSchema();
return context.resolve(new AnnotatedType(innerType)
.jsonViewAnnotation(type.getJsonViewAnnotation())
.ctxAnnotations((type.getCtxAnnotations()))
@@ -90,8 +93,8 @@ else if (isResponseTypeToIgnore(cls))
* @param javaType the java type
* @return the java type
*/
- private JavaType findResponseEntity(JavaType javaType){
- if(ResponseEntity.class.isAssignableFrom(javaType.getRawClass())){
+ private JavaType findResponseEntity(JavaType javaType) {
+ if (ResponseEntity.class.isAssignableFrom(javaType.getRawClass())) {
while (ResponseEntity.class != javaType.getRawClass())
javaType = javaType.getSuperClass();
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SchemaPropertyDeprecatingConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SchemaPropertyDeprecatingConverter.java
index 06340b00a..e31802ba5 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SchemaPropertyDeprecatingConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/SchemaPropertyDeprecatingConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
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 3b2b77929..886344982 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
@@ -72,9 +72,9 @@ public SortOpenAPIConverter(ObjectMapperProvider springDocObjectMapper) {
/**
* Resolve schema.
*
- * @param type the type
+ * @param type the type
* @param context the context
- * @param chain the chain
+ * @param chain the chain
* @return the schema
*/
@Override
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/WebFluxSupportConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/WebFluxSupportConverter.java
index 62f04c19e..f948566da 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/WebFluxSupportConverter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/WebFluxSupportConverter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/DefaultPageable.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/DefaultPageable.java
index 2d0afe986..121ae4a86 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/DefaultPageable.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/DefaultPageable.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/MonetaryAmount.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/MonetaryAmount.java
index a37c4c33c..da3a1108e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/MonetaryAmount.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/MonetaryAmount.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Pageable.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Pageable.java
index b469e88a3..a3bb4c62c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Pageable.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Pageable.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/PageableAsQueryParam.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/PageableAsQueryParam.java
index 6bad26ab1..413266004 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/PageableAsQueryParam.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/PageableAsQueryParam.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Sort.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Sort.java
index 15055afc8..a37c5a7a7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Sort.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/Sort.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortAsQueryParam.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortAsQueryParam.java
index 56e3b62bd..ea7cca1b2 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortAsQueryParam.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortAsQueryParam.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
@@ -44,9 +44,9 @@
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Parameter(in = ParameterIn.QUERY
- , description = "Sorting criteria in the format: property,(asc|desc). "
- + "Default sort order is ascending. " + "Multiple sort criteria are supported."
- , name = "sort"
- , array = @ArraySchema(schema = @Schema(type = "string"))
+ , description = "Sorting criteria in the format: property,(asc|desc). "
+ + "Default sort order is ascending. " + "Multiple sort criteria are supported."
+ , name = "sort"
+ , array = @ArraySchema(schema = @Schema(type = "string"))
)
public @interface SortAsQueryParam {}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortObject.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortObject.java
index f59d862eb..88b84bc34 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortObject.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/models/SortObject.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.converters.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOpenApiCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOpenApiCustomizer.java
index 5810af1da..0dd4d6a92 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOpenApiCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ActuatorOpenApiCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
@@ -89,16 +89,16 @@ private void handleActuatorOperationIdUniqueness(OpenAPI openApi) {
Set usedOperationIds = new HashSet<>();
actuatorPathEntryStream(openApi, null)
.sorted(Comparator.comparing(Entry::getKey))
- .forEachOrdered(stringPathItemEntry ->
- stringPathItemEntry.getValue().readOperations().forEach(operation -> {
- String initialOperationId = operation.getOperationId();
- String uniqueOperationId = operation.getOperationId();
- int counter = 1;
- while (!usedOperationIds.add(uniqueOperationId)) {
- uniqueOperationId = initialOperationId + "_" + ++counter;
- }
- operation.setOperationId(uniqueOperationId);
- })
+ .forEachOrdered(stringPathItemEntry ->
+ stringPathItemEntry.getValue().readOperations().forEach(operation -> {
+ String initialOperationId = operation.getOperationId();
+ String uniqueOperationId = operation.getOperationId();
+ int counter = 1;
+ while (!usedOperationIds.add(uniqueOperationId)) {
+ uniqueOperationId = initialOperationId + "_" + ++counter;
+ }
+ operation.setOperationId(uniqueOperationId);
+ })
);
}
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 df37c2b43..2730034a5 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
@@ -31,6 +31,7 @@
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
@@ -61,7 +62,7 @@
*
* @author bnasslahsen
*/
-public class ActuatorOperationCustomizer implements GlobalOperationCustomizer {
+public class ActuatorOperationCustomizer implements GlobalOperationComponentsCustomizer {
/**
* The constant OPERATION.
@@ -94,11 +95,11 @@ public ActuatorOperationCustomizer(SpringDocConfigProperties springDocConfigProp
}
@Override
- public Operation customize(Operation operation, HandlerMethod handlerMethod) {
+ public Operation customize(Operation operation, Components components, HandlerMethod handlerMethod) {
if (operationHasValidTag(operation)) {
- Field operationField = FieldUtils.getDeclaredField(handlerMethod.getBean().getClass(), OPERATION,true);
+ Field operationField = FieldUtils.getDeclaredField(handlerMethod.getBean().getClass(), OPERATION, true);
if (operationField != null) {
- processOperationField(handlerMethod, operation, operationField);
+ processOperationField(handlerMethod, operation, components, operationField);
}
setOperationSummary(operation, handlerMethod);
}
@@ -120,16 +121,17 @@ private boolean operationHasValidTag(Operation operation) {
*
* @param handlerMethod the handler method
* @param operation the operation
+ * @param components the components
* @param operationField the operation field
*/
- private void processOperationField(HandlerMethod handlerMethod, Operation operation, Field operationField) {
+ private void processOperationField(HandlerMethod handlerMethod, Operation operation, Components components, Field operationField) {
try {
Object actuatorOperation = operationField.get(handlerMethod.getBean());
Field actuatorOperationField = FieldUtils.getDeclaredField(actuatorOperation.getClass(), OPERATION, true);
if (actuatorOperationField != null) {
AbstractDiscoveredOperation discoveredOperation =
(AbstractDiscoveredOperation) actuatorOperationField.get(actuatorOperation);
- handleOperationMethod(discoveredOperation.getOperationMethod(), operation);
+ handleOperationMethod(discoveredOperation.getOperationMethod(), components, operation);
}
}
catch (IllegalAccessException e) {
@@ -141,25 +143,26 @@ private void processOperationField(HandlerMethod handlerMethod, Operation operat
* Handle operation method.
*
* @param operationMethod the operation method
+ * @param components the components
* @param operation the operation
*/
- private void handleOperationMethod(OperationMethod operationMethod, Operation operation) {
+ private void handleOperationMethod(OperationMethod operationMethod, Components components, Operation operation) {
String operationId = operationMethod.getMethod().getName();
operation.setOperationId(operationId);
switch (operationMethod.getOperationType()) {
case READ:
- addParameters(operationMethod, operation, ParameterIn.QUERY);
+ addParameters(operationMethod, operation, components, ParameterIn.QUERY);
break;
case WRITE:
- addWriteParameters(operationMethod, operation);
+ addWriteParameters(operationMethod, components, operation);
operation.setResponses(new ApiResponses()
.addApiResponse(String.valueOf(HttpStatus.NO_CONTENT.value()), new ApiResponse().description(HttpStatus.NO_CONTENT.getReasonPhrase()))
.addApiResponse(String.valueOf(HttpStatus.BAD_REQUEST.value()), new ApiResponse().description(HttpStatus.BAD_REQUEST.getReasonPhrase())));
break;
case DELETE:
operation.setResponses(new ApiResponses().addApiResponse(String.valueOf(HttpStatus.NO_CONTENT.value()), new ApiResponse().description(HttpStatus.NO_CONTENT.getReasonPhrase())));
- addParameters(operationMethod, operation, ParameterIn.QUERY);
+ addParameters(operationMethod, operation, components, ParameterIn.QUERY);
break;
default:
break;
@@ -171,13 +174,14 @@ private void handleOperationMethod(OperationMethod operationMethod, Operation op
*
* @param operationMethod the operation method
* @param operation the operation
+ * @param components the components
* @param parameterIn the parameter in
*/
- private void addParameters(OperationMethod operationMethod, Operation operation, ParameterIn parameterIn) {
+ private void addParameters(OperationMethod operationMethod, Operation operation, Components components, ParameterIn parameterIn) {
for (OperationParameter operationParameter : operationMethod.getParameters()) {
Parameter parameter = getParameterFromField(operationParameter);
- if(parameter == null) continue;
- Schema> schema = resolveSchema(parameter);
+ if (parameter == null) continue;
+ Schema> schema = resolveSchema(parameter, components);
if (parameter.getAnnotation(Selector.class) != null) {
operation.addParametersItem(new io.swagger.v3.oas.models.parameters.PathParameter()
.name(parameter.getName())
@@ -197,13 +201,14 @@ else if (isValidParameterType(parameter)) {
* Add write parameters.
*
* @param operationMethod the operation method
+ * @param components the components
* @param operation the operation
*/
- private void addWriteParameters(OperationMethod operationMethod, Operation operation) {
+ private void addWriteParameters(OperationMethod operationMethod, Components components, Operation operation) {
for (OperationParameter operationParameter : operationMethod.getParameters()) {
Parameter parameter = getParameterFromField(operationParameter);
- if(parameter == null) continue;
- Schema> schema = resolveSchema(parameter);
+ if (parameter == null) continue;
+ Schema> schema = resolveSchema(parameter, components);
if (parameter.getAnnotation(Selector.class) != null) {
operation.addParametersItem(new io.swagger.v3.oas.models.parameters.PathParameter()
.name(parameter.getName())
@@ -237,12 +242,13 @@ private Parameter getParameterFromField(OperationParameter operationParameter) {
/**
* Resolve schema schema.
*
- * @param parameter the parameter
+ * @param parameter the parameter
+ * @param components
* @return the schema
*/
- private Schema> resolveSchema(Parameter parameter) {
- Schema schema = AnnotationsUtils.resolveSchemaFromType(parameter.getType(), null, null, springDocConfigProperties.isOpenapi31());
- if(springDocConfigProperties.isOpenapi31()) handleSchemaTypes(schema);
+ private Schema> resolveSchema(Parameter parameter, Components components) {
+ Schema schema = AnnotationsUtils.resolveSchemaFromType(parameter.getType(), components, null, springDocConfigProperties.isOpenapi31());
+ if (springDocConfigProperties.isOpenapi31()) handleSchemaTypes(schema);
return schema;
}
@@ -271,4 +277,8 @@ private void setOperationSummary(Operation operation, HandlerMethod handlerMetho
}
}
+ @Override
+ public Operation customize(Operation operation, HandlerMethod handlerMethod) {
+ return this.customize(operation, null, handlerMethod);
+ }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestDelegatingMethodParameterCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestDelegatingMethodParameterCustomizer.java
index 1db18a182..d4aa5dbd7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestDelegatingMethodParameterCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestDelegatingMethodParameterCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
@@ -65,8 +65,8 @@
/**
* The type Data rest delegating method parameter customizer.
*
- * @author bnasslahsen
- * @author pheyken
+ * @author bnasslahsen
+ * @author pheyken
*/
public class DataRestDelegatingMethodParameterCustomizer implements DelegatingMethodParameterCustomizer {
@@ -192,13 +192,13 @@ public boolean allowReserved() {
@Override
public Schema schema() {
return new Schema() {
+ private Schema parameterSchema = parameter.schema();
+
@Override
public SchemaResolution schemaResolution() {
return parameterSchema.schemaResolution();
}
- private Schema parameterSchema = parameter.schema();
-
@Override
public Class extends Annotation> annotationType() {
return parameterSchema.annotationType();
@@ -449,6 +449,11 @@ public Class> contains() {
return parameterSchema.$dynamicAnchor();
}
+ @Override
+ public String $dynamicRef() {
+ return parameterSchema.$dynamicRef();
+ }
+
@Override
public String contentEncoding() {
return parameterSchema.contentEncoding();
@@ -836,6 +841,11 @@ public Class> contains() {
return schema.$dynamicAnchor();
}
+ @Override
+ public String $dynamicRef() {
+ return schema.$dynamicRef();
+ }
+
@Override
public String contentEncoding() {
return schema.contentEncoding();
@@ -1029,6 +1039,11 @@ public Extension[] extensions() {
public String ref() {
return parameter.ref();
}
+
+ @Override
+ public Class>[] validationGroups() {
+ return new Class[0];
+ }
};
return Optional.of(parameterNew);
}
@@ -1137,7 +1152,8 @@ else if (isSpringDataWebPropertiesPresent())
if (pageableDefault != null) {
if (isSpringDataWebPropertiesPresent() && optionalSpringDataWebPropertiesProvider.get().getSpringDataWebProperties().getPageable().isOneIndexedParameters()) {
defaultValue = String.valueOf(pageableDefault.page() + 1);
- } else {
+ }
+ else {
defaultValue = String.valueOf(pageableDefault.page());
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestRouterOperationCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestRouterOperationCustomizer.java
index a31015f4e..5bd102b10 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestRouterOperationCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DataRestRouterOperationCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DelegatingMethodParameterCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DelegatingMethodParameterCustomizer.java
index d6475c4c4..5d0546ba2 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DelegatingMethodParameterCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DelegatingMethodParameterCustomizer.java
@@ -21,18 +21,34 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
+import java.util.List;
+
import org.springframework.core.MethodParameter;
+import org.springframework.lang.Nullable;
/**
* The interface Delegating method parameter customizer.
+ *
+ * @author dyun
*/
@FunctionalInterface
public interface DelegatingMethodParameterCustomizer {
+ /**
+ * Customize.
+ * tip: parameters include the parent fields, you can choose how to deal with the methodParameters
+ *
+ * @param originalParameter the original parameter
+ * @param methodParameters the exploded parameters
+ */
+ @Nullable
+ default void customizeList(MethodParameter originalParameter, List methodParameters) {
+ methodParameters.forEach(parameter -> customize(originalParameter, parameter));
+ }
/**
* Customize.
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomizer.java
index a2a2fc631..f0338854c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationComponentsCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationComponentsCustomizer.java
new file mode 100644
index 000000000..3e09a9e51
--- /dev/null
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationComponentsCustomizer.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * *
+ * * *
+ * * * *
+ * * * * *
+ * * * * * * 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.customizers;
+
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.Operation;
+
+import org.springframework.web.method.HandlerMethod;
+
+/**
+ * Implement and register a bean of type {@link GlobalOperationComponentsCustomizer} to
+ * customize an operation based on the components and handler method input on default OpenAPI
+ * description and groups
+ *
+ * @author christophejan
+ * @see OperationCustomizer operations on default OpenAPI description but not groups
+ */
+public interface GlobalOperationComponentsCustomizer extends GlobalOperationCustomizer {
+
+ /**
+ * Customize operation.
+ *
+ * @param operation input operation
+ * @param handlerMethod original handler method
+ * @return customized operation
+ */
+ Operation customize(Operation operation, Components components, HandlerMethod handlerMethod);
+}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java
index 22dc998ec..0793af2df 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java
index db9cf5141..b18ed2fcd 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
@@ -109,7 +109,7 @@ else if (resolvedSchema != null && resolvedSchema.get$ref() != null && resolvedS
try {
Field processedTypesField = FieldUtils.getDeclaredField(ModelConverterContextImpl.class, "processedTypes", true);
Set processedType = (Set) processedTypesField.get(context);
- if(processedType.contains(type))
+ if (processedType.contains(type))
setJavadocDescription(cls, fields, clsProperties, resolvedSchema, true);
}
catch (IllegalAccessException e) {
@@ -163,7 +163,7 @@ public void setJavadocDescription(Class> cls, List fields, List optionalPd = clsProperties.stream().filter(pd -> pd.getName().equals(stringSchemaEntry.getKey())).findAny();
optionalPd.ifPresent(pd1 -> {
- if(pd1.getReadMethod() != null) {
+ if (pd1.getReadMethod() != null) {
String fieldJavadoc = javadocProvider.getMethodJavadocDescription(pd1.getReadMethod());
if (StringUtils.isNotBlank(fieldJavadoc))
stringSchemaEntry.getValue().setDescription(fieldJavadoc);
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiBuilderCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiBuilderCustomizer.java
index 4631fbd0b..53a44967c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiBuilderCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiBuilderCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomizer.java
index f54d294c4..40f0be199 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
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 c3cae9d1e..663d642e1 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiLocaleCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiLocaleCustomizer.java
index af86fc0ea..fd2cfd089 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiLocaleCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OpenApiLocaleCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java
index 518802606..c52f16052 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationIdCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationIdCustomizer.java
index dc718aa5e..0f4adebe5 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationIdCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/OperationIdCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
@@ -72,7 +72,7 @@ private void processOperation(Operation operation, Map operatio
String originalOperationId = operation.getOperationId();
// Check if operationId already exists
- if ( originalOperationId!=null && operationIdCount.containsKey(originalOperationId)) {
+ if (originalOperationId != null && operationIdCount.containsKey(originalOperationId)) {
// Get the count for the current operationId and increment
int count = operationIdCount.get(originalOperationId);
count++;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterCustomizer.java
index 084ae2cc6..96987bf6b 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterObjectNamingStrategyCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterObjectNamingStrategyCustomizer.java
index 5a7875474..4cbefc3cb 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterObjectNamingStrategyCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ParameterObjectNamingStrategyCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/PropertyCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/PropertyCustomizer.java
index 9f500c4d7..0b6228cb9 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/PropertyCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/PropertyCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/QuerydslPredicateOperationCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/QuerydslPredicateOperationCustomizer.java
index 7f86ea597..e0c280d96 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/QuerydslPredicateOperationCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/QuerydslPredicateOperationCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
@@ -94,8 +94,8 @@ public class QuerydslPredicateOperationCustomizer implements GlobalOperationCust
*/
public QuerydslPredicateOperationCustomizer(QuerydslBindingsFactory querydslBindingsFactory, SpringDocConfigProperties springDocConfigProperties) {
this.querydslBindingsFactory = querydslBindingsFactory;
- this.springDocConfigProperties= springDocConfigProperties;
-
+ this.springDocConfigProperties = springDocConfigProperties;
+
}
@Override
@@ -160,7 +160,8 @@ public Operation customize(Operation operation, HandlerMethod handlerMethod) {
private boolean getFieldValueOfBoolean(QuerydslBindings instance, String fieldName) {
try {
return (boolean) FieldUtils.readDeclaredField(instance, fieldName, true);
- } catch (IllegalAccessException e) {
+ }
+ catch (IllegalAccessException e) {
LOGGER.warn(e.getMessage());
}
return false;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java
index 42b98a7f5..8c4790aa7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java
index 985a4ffba..4ebc95351 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java
index 98ed3883c..f79612e42 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java
@@ -21,17 +21,19 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
@@ -89,6 +91,7 @@
*
* @author Anton Tkachenko tkachenkoas@gmail.com
* @author bnasslahsen
+ * @author Huijin Hong
*/
public class SpecPropertiesCustomizer implements GlobalOpenApiCustomizer {
@@ -135,7 +138,7 @@ private void customizeOpenApi(OpenAPI openApi, OpenAPI openApiProperties) {
Components componentsProperties = openApiProperties.getComponents();
if (componentsProperties != null)
customizeComponents(openApi, componentsProperties);
-
+
Paths pathsProperties = openApiProperties.getPaths();
if (pathsProperties != null)
customizePaths(openApi, pathsProperties);
@@ -144,13 +147,37 @@ private void customizeOpenApi(OpenAPI openApi, OpenAPI openApiProperties) {
if (!CollectionUtils.isEmpty(securityRequirementsProperties)) {
customizeSecurity(openApi, securityRequirementsProperties);
}
+ if (!CollectionUtils.isEmpty(openApiProperties.getServers())) {
+ openApi.setServers(new ArrayList<>(openApiProperties.getServers()));
+ }
+
+ ExternalDocumentation externalDocumentationProperties = openApiProperties.getExternalDocs();
+ if (externalDocumentationProperties != null) {
+ customizeExternalDocs(openApi, externalDocumentationProperties);
+ }
+ }
+ }
+
+ /**
+ * Customized external docs.
+ *
+ * @param openApi the open api
+ * @param externalDocumentationProperties the external documentation
+ */
+ private void customizeExternalDocs(OpenAPI openApi, ExternalDocumentation externalDocumentationProperties) {
+ ExternalDocumentation externalDocumentation = openApi.getExternalDocs();
+ if (externalDocumentation != null) {
+ resolveString(externalDocumentation::description, externalDocumentationProperties::getDescription);
+ resolveString(externalDocumentation::url, externalDocumentationProperties::getUrl);
+ } else {
+ openApi.setExternalDocs(externalDocumentationProperties);
}
}
/**
* Customize security.
*
- * @param openApi the open api
+ * @param openApi the open api
* @param securityRequirementsProperties the security requirements
*/
private void customizeSecurity(OpenAPI openApi, List securityRequirementsProperties) {
@@ -188,8 +215,8 @@ private void customizePaths(OpenAPI openApi, Paths pathsProperties) {
resolveString(pathItem::description, pathItemProperties::getDescription);
resolveString(pathItem::summary, pathItemProperties::getSummary);
- Map operationMap = pathItem.readOperationsMap();
- Map operationMapProperties = pathItemProperties.readOperationsMap();
+ Map operationMap = pathItem.readOperationsMap();
+ Map operationMapProperties = pathItemProperties.readOperationsMap();
operationMapProperties.forEach((httpMethod, operationProperties) -> {
Operation operationToCustomize = operationMap.get(httpMethod);
@@ -199,7 +226,8 @@ private void customizePaths(OpenAPI openApi, Paths pathsProperties) {
resolveSet(operationToCustomize::tags, operationProperties::getTags);
}
});
- }});
+ }
+ });
}
}
@@ -217,25 +245,25 @@ private void customizeComponents(OpenAPI openApi, Components componentsPropertie
else {
Map schemaMap = components.getSchemas();
schemaMap.forEach((key, schema) -> {
- Schema schemaProperties = componentsProperties.getSchemas().get(key);
- if (schemaProperties != null) {
- resolveString(schema::setDescription, schemaProperties::getDescription);
- Map properties = schema.getProperties();
- if (CollectionUtils.isEmpty(properties)) {
- return;
- }
- properties.forEach((propKey, propSchema) -> {
- Schema propSchemaProperties = (Schema) schemaProperties.getProperties().get(propKey);
- if (propSchemaProperties != null) {
- resolveString(propSchema::description, propSchemaProperties::getDescription);
- resolveString(propSchema::title, propSchemaProperties::getTitle);
- resolveString(propSchema::example, propSchemaProperties::getExample);
+ if (!CollectionUtils.isEmpty(componentsProperties.getSchemas())) {
+ Schema schemaProperties = componentsProperties.getSchemas().get(key);
+ if (schemaProperties != null) {
+ resolveString(schema::setDescription, schemaProperties::getDescription);
+ Map properties = schema.getProperties();
+ if (CollectionUtils.isEmpty(properties)) {
+ return;
}
- });
+ properties.forEach((propKey, propSchema) -> {
+ Schema propSchemaProperties = (Schema) schemaProperties.getProperties().get(propKey);
+ if (propSchemaProperties != null) {
+ resolveString(propSchema::description, propSchemaProperties::getDescription);
+ resolveString(propSchema::title, propSchemaProperties::getTitle);
+ resolveString(propSchema::example, propSchemaProperties::getExample);
+ }
+ });
+ }
}
});
-
-
Map securitySchemeMap = components.getSecuritySchemes();
if (CollectionUtils.isEmpty(securitySchemeMap)) {
components.setSecuritySchemes(componentsProperties.getSecuritySchemes());
@@ -319,7 +347,7 @@ private void resolveString(Consumer setter, Supplier getter) {
*/
private void resolveType(Consumer setter, Supplier getter) {
Type value = (Type) getter.get();
- if (value!=null) {
+ if (value != null) {
setter.accept(value);
}
}
@@ -332,7 +360,7 @@ private void resolveType(Consumer setter, Supplier getter) {
*/
private void resolveIn(Consumer setter, Supplier getter) {
In value = (In) getter.get();
- if (value!=null) {
+ if (value != null) {
setter.accept(value);
}
}
@@ -345,12 +373,12 @@ private void resolveIn(Consumer setter, Supplier getter) {
*/
private void resolveOAuthFlows(Consumer setter, Supplier getter) {
OAuthFlows value = (OAuthFlows) getter.get();
- if (value!=null) {
+ if (value != null) {
setter.accept(value);
}
}
-
+
/**
* Resolve set.
*
@@ -358,11 +386,10 @@ private void resolveOAuthFlows(Consumer setter, Supplier get
* @param getter the getter
*/
private void resolveSet(Consumer setter, Supplier getter) {
- List value = getter.get();
+ List value = getter.get();
if (!CollectionUtils.isEmpty(value)) {
setter.accept(value);
}
}
-
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpringDocCustomizers.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpringDocCustomizers.java
index 8acc651c7..8dd56a7fb 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpringDocCustomizers.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpringDocCustomizers.java
@@ -21,10 +21,11 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.customizers;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -70,14 +71,14 @@ public class SpringDocCustomizers implements ApplicationContextAware, Initializi
private final Optional> dataRestRouterOperationCustomizers;
/**
- * The Context.
+ * The method filters to use.
*/
- private ApplicationContext context;
+ private final Optional> methodFilters;
/**
- * The method filters to use.
+ * The Context.
*/
- private final Optional> methodFilters;
+ private ApplicationContext context;
/**
* The Global open api customizers.
@@ -94,7 +95,16 @@ public class SpringDocCustomizers implements ApplicationContextAware, Initializi
*/
private Optional> globalOpenApiMethodFilters;
+ /**
+ * The Optional delegating method parameter customizers.
+ */
+ private final Optional> optionalDelegatingMethodParameterCustomizers;
+ /**
+ * The Parameter customizers.
+ */
+ private final Optional> parameterCustomizers;
+
/**
* Instantiates a new Spring doc customizers.
*
@@ -113,12 +123,14 @@ public SpringDocCustomizers(Optional> openApiCustomizers,
Optional> dataRestRouterOperationCustomizers,
Optional> methodFilters,
Optional> globalOpenApiCustomizers, Optional> globalOperationCustomizers,
- Optional> globalOpenApiMethodFilters) {
+ Optional> globalOpenApiMethodFilters, Optional> optionalDelegatingMethodParameterCustomizers, Optional> parameterCustomizers) {
this.openApiCustomizers = openApiCustomizers;
this.operationCustomizers = operationCustomizers;
this.globalOpenApiCustomizers = globalOpenApiCustomizers;
this.globalOperationCustomizers = globalOperationCustomizers;
this.globalOpenApiMethodFilters = globalOpenApiMethodFilters;
+ this.optionalDelegatingMethodParameterCustomizers = optionalDelegatingMethodParameterCustomizers;
+ this.parameterCustomizers = parameterCustomizers;
operationCustomizers.ifPresent(customizers -> customizers.removeIf(Objects::isNull));
this.routerOperationCustomizers = routerOperationCustomizers;
this.dataRestRouterOperationCustomizers = dataRestRouterOperationCustomizers;
@@ -134,11 +146,14 @@ public SpringDocCustomizers(Optional> openApiCustomizers,
* @param openApiMethodFilters the open api method filters
*/
public SpringDocCustomizers(Optional> openApiCustomizers, Optional> operationCustomizers,
- Optional> routerOperationCustomizers, Optional> openApiMethodFilters) {
+ Optional> routerOperationCustomizers, Optional> openApiMethodFilters,
+ Optional> optionalDelegatingMethodParameterCustomizers, Optional> parameterCustomizers) {
this.openApiCustomizers = openApiCustomizers;
this.operationCustomizers = operationCustomizers;
this.routerOperationCustomizers = routerOperationCustomizers;
this.methodFilters = openApiMethodFilters;
+ this.optionalDelegatingMethodParameterCustomizers = optionalDelegatingMethodParameterCustomizers;
+ this.parameterCustomizers = parameterCustomizers;
this.dataRestRouterOperationCustomizers = Optional.empty();
}
@@ -219,6 +234,24 @@ public Optional> getGlobalOpenApiMethodFilters()
return globalOpenApiMethodFilters;
}
+ /**
+ * Gets optional delegating method parameter customizers.
+ *
+ * @return the optional delegating method parameter customizers
+ */
+ public Optional> getOptionalDelegatingMethodParameterCustomizers() {
+ return optionalDelegatingMethodParameterCustomizers;
+ }
+
+ /**
+ * Gets parameter customizers.
+ *
+ * @return the parameter customizers
+ */
+ public Optional> getParameterCustomizers() {
+ return parameterCustomizers;
+ }
+
@Override
public void afterPropertiesSet() {
//add the default customizers
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/ControllerType.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/ControllerType.java
index adfc18cb4..2a3e4f59c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/ControllerType.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/ControllerType.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestOperationService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestOperationService.java
index b75ce3fa3..8de97208f 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestOperationService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestOperationService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
@@ -210,7 +210,7 @@ private Operation buildSearchOperation(HandlerMethod handlerMethod, DataRestRepo
String pName = parameterMetadatum.getName();
ResourceDescription description = parameterMetadatum.getDescription();
if (description instanceof TypedResourceDescription) {
- Type type = getParameterType(pName,method,description);
+ Type type = getParameterType(pName, method, description);
Schema> schema = SpringDocAnnotationsUtils.extractSchema(openAPI.getComponents(), type, null, null, openAPI.getSpecVersion());
Parameter parameter = getParameterFromAnnotations(openAPI, methodAttributes, method, pName);
if (parameter == null) {
@@ -248,7 +248,7 @@ private Type getParameterType(String pName, Method method, ResourceDescription d
java.lang.reflect.Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
java.lang.reflect.Parameter parameter = parameters[i];
- if (pName.equals(parameter.getName()) || (parameter.getAnnotation(Param.class)!=null && pName.equals(parameter.getAnnotation(Param.class).value()))) {
+ if (pName.equals(parameter.getName()) || (parameter.getAnnotation(Param.class) != null && pName.equals(parameter.getAnnotation(Param.class).value()))) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(method, i);
type = resolvableType.getType();
break;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRepository.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRepository.java
index 305fe969e..a6837dd6b 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRepository.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRepository.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java
index 6fad33712..cc0ce4c49 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
@@ -43,8 +43,10 @@
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.apache.commons.lang3.ArrayUtils;
+import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
import org.springdoc.core.discoverer.SpringDocParameterNameDiscoverer;
import org.springdoc.core.extractor.DelegatingMethodParameter;
+import org.springdoc.core.extractor.MethodParameterPojoExtractor;
import org.springdoc.core.models.MethodAttributes;
import org.springdoc.core.models.ParameterInfo;
import org.springdoc.core.models.RequestBodyInfo;
@@ -99,22 +101,33 @@ public class DataRestRequestService {
*/
private final SpringDocDataRestUtils springDocDataRestUtils;
+ /**
+ * The Optional delegating method parameter customizers.
+ */
+ private final Optional> optionalDelegatingMethodParameterCustomizers;
+
+ /**
+ * The Method parameter pojo extractor.
+ */
+ private final MethodParameterPojoExtractor methodParameterPojoExtractor;
+
/**
* Instantiates a new Data rest request builder.
*
* @param localSpringDocParameterNameDiscoverer the local spring doc parameter name discoverer
- * @param parameterBuilder the parameter builder
- * @param requestBodyService the request body builder
* @param requestBuilder the request builder
* @param springDocDataRestUtils the spring doc data rest utils
*/
- public DataRestRequestService(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer, GenericParameterService parameterBuilder,
- RequestBodyService requestBodyService, AbstractRequestService requestBuilder, SpringDocDataRestUtils springDocDataRestUtils) {
+ public DataRestRequestService(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer,
+ AbstractRequestService requestBuilder,
+ SpringDocDataRestUtils springDocDataRestUtils) {
this.localSpringDocParameterNameDiscoverer = localSpringDocParameterNameDiscoverer;
- this.parameterBuilder = parameterBuilder;
- this.requestBodyService = requestBodyService;
this.requestBuilder = requestBuilder;
this.springDocDataRestUtils = springDocDataRestUtils;
+ this.optionalDelegatingMethodParameterCustomizers = requestBuilder.getOptionalDelegatingMethodParameterCustomizers();
+ this.methodParameterPojoExtractor = requestBuilder.getMethodParameterPojoExtractor();
+ this.parameterBuilder = requestBuilder.getParameterBuilder();
+ this.requestBodyService=requestBuilder.getRequestBodyBuilder();
}
/**
@@ -156,7 +169,7 @@ public void buildParameters(OpenAPI openAPI, HandlerMethod handlerMethod, Reques
*/
public void buildCommonParameters(OpenAPI openAPI, RequestMethod requestMethod, MethodAttributes methodAttributes, Operation operation, String[] pNames, MethodParameter[] parameters,
DataRestRepository dataRestRepository) {
- parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getOptionalDelegatingMethodParameterCustomizers(), requestBuilder.isDefaultFlatParamObject());
+ parameters = DelegatingMethodParameter.customize(pNames, parameters, this.optionalDelegatingMethodParameterCustomizers, this.methodParameterPojoExtractor, requestBuilder.isDefaultFlatParamObject());
Class> domainType = dataRestRepository.getDomainType();
for (MethodParameter methodParameter : parameters) {
final String pName = methodParameter.getParameterName();
@@ -226,8 +239,8 @@ private boolean isParamToIgnore(MethodParameter methodParameter) {
private void addParameters(OpenAPI openAPI, RequestMethod requestMethod, MethodAttributes methodAttributes, Operation operation,
MethodParameter methodParameter, ParameterInfo parameterInfo, Parameter parameter) {
List parameterAnnotations = Arrays.asList(getParameterAnnotations(methodParameter));
- if (requestBuilder.isValidParameter(parameter,methodAttributes)) {
- requestBuilder.applyBeanValidatorAnnotations(parameter, parameterAnnotations, parameterInfo.isParameterObject());
+ if (requestBuilder.isValidParameter(parameter, methodAttributes)) {
+ requestBuilder.applyBeanValidatorAnnotations(methodParameter, parameter, parameterAnnotations, parameterInfo.isParameterObject(), openAPI.getOpenapi());
operation.addParametersItem(parameter);
}
else if (!RequestMethod.GET.equals(requestMethod)) {
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestResponseService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestResponseService.java
index 46412ad2e..ba5674861 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestResponseService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestResponseService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
@@ -243,7 +243,7 @@ else if ((HttpEntity.class.equals(parameterizedType.getRawType())
}
}
else if ((CollectionModel.class.equals(parameterizedType.getRawType())
- && parameterizedType.getActualTypeArguments()[0]!=null)) {
+ && parameterizedType.getActualTypeArguments()[0] != null)) {
return getTypeForCollectionModel(returnedEntityType, resourceMetadata.isPagingResource());
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRouterOperationService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRouterOperationService.java
index 41660655b..bd37f965a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRouterOperationService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRouterOperationService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestTagsService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestTagsService.java
index faafd1976..df6d22367 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestTagsService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestTagsService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.data;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/discoverer/SpringDocParameterNameDiscoverer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/discoverer/SpringDocParameterNameDiscoverer.java
index 0bef332a3..e8a97cbca 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/discoverer/SpringDocParameterNameDiscoverer.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/discoverer/SpringDocParameterNameDiscoverer.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.discoverer;
@@ -50,7 +50,7 @@ public class SpringDocParameterNameDiscoverer extends DefaultParameterNameDiscov
@Nullable
public String[] getParameterNames(Method method) {
String[] params = super.getParameterNames(method);
- if(ArrayUtils.isEmpty(params) || Objects.requireNonNull(params).length!=method.getParameters().length)
+ if (ArrayUtils.isEmpty(params) || Objects.requireNonNull(params).length != method.getParameters().length)
params = standardReflectionParameterNameDiscoverer.getParameterNames(method);
return params;
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java
index 11b39e482..e9a0e6708 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.extractor;
@@ -38,6 +38,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -88,6 +89,11 @@ public class DelegatingMethodParameter extends MethodParameter {
*/
private final boolean isParameterObject;
+ /**
+ * If Is parameter object. then The Field should be not null
+ */
+ private final Field field;
+
/**
* The Method annotations.
*/
@@ -108,14 +114,15 @@ public class DelegatingMethodParameter extends MethodParameter {
* @param isParameterObject the is parameter object
* @param isNotRequired the is required
*/
- DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations, Annotation[] methodAnnotations, boolean isParameterObject, boolean isNotRequired) {
+ DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations, Annotation[] methodAnnotations, boolean isParameterObject, Field field, boolean isNotRequired) {
super(delegate);
this.delegate = delegate;
+ this.field = field;
this.additionalParameterAnnotations = additionalParameterAnnotations;
this.parameterName = parameterName;
this.isParameterObject = isParameterObject;
this.isNotRequired = isNotRequired;
- this.methodAnnotations =methodAnnotations;
+ this.methodAnnotations = methodAnnotations;
}
/**
@@ -124,11 +131,12 @@ public class DelegatingMethodParameter extends MethodParameter {
* @param pNames the p names
* @param parameters the parameters
* @param optionalDelegatingMethodParameterCustomizers the optional list delegating method parameter customizer
+ * @param methodParameterPojoExtractor the method parameter pojo extractor
* @param defaultFlatParamObject the default flat param object
* @return the method parameter [ ]
*/
public static MethodParameter[] customize(String[] pNames, MethodParameter[] parameters,
- Optional> optionalDelegatingMethodParameterCustomizers, boolean defaultFlatParamObject) {
+ Optional> optionalDelegatingMethodParameterCustomizers, MethodParameterPojoExtractor methodParameterPojoExtractor, boolean defaultFlatParamObject) {
List explodedParameters = new ArrayList<>();
for (int i = 0; i < parameters.length; ++i) {
MethodParameter p = parameters[i];
@@ -139,14 +147,14 @@ public static MethodParameter[] customize(String[] pNames, MethodParameter[] par
.anyMatch(annotation -> Arrays.asList(RequestBody.class, RequestPart.class).contains(annotation.annotationType()));
if (!MethodParameterPojoExtractor.isSimpleType(paramClass)
&& (hasFlatAnnotation || (defaultFlatParamObject && !hasNotFlatAnnotation && !AbstractRequestService.isRequestTypeToIgnore(paramClass)))) {
- MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> {
- optionalDelegatingMethodParameterCustomizers.ifPresent(delegatingMethodParameterCustomizers -> delegatingMethodParameterCustomizers.forEach(customizer -> customizer.customize(p, methodParameter)));
- explodedParameters.add(methodParameter);
- });
+ List flatParams = new CopyOnWriteArrayList<>();
+ methodParameterPojoExtractor.extractFrom(paramClass).forEach(flatParams::add);
+ optionalDelegatingMethodParameterCustomizers.orElseGet(ArrayList::new).forEach(cz -> cz.customizeList(p, flatParams));
+ explodedParameters.addAll(flatParams);
}
else {
String name = pNames != null ? pNames[i] : p.getParameterName();
- explodedParameters.add(new DelegatingMethodParameter(p, name, null, null, false, false));
+ explodedParameters.add(new DelegatingMethodParameter(p, name, null, null, false, null, false));
}
}
return explodedParameters.toArray(new MethodParameter[0]);
@@ -297,4 +305,14 @@ public boolean isParameterObject() {
return isParameterObject;
}
-}
\ No newline at end of file
+ /**
+ * Gets field. If Is parameter object. then The Field should be not null
+ *
+ * @return the field
+ * @see #isParameterObject
+ */
+ @Nullable
+ public Field getField() {
+ return field;
+ }
+}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java
index 643c2d9fd..96395fe36 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.extractor;
@@ -35,12 +35,18 @@
import java.lang.reflect.TypeVariable;
import java.nio.charset.Charset;
import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.HashSet;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -49,20 +55,20 @@
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
+import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
+import org.springdoc.core.utils.SchemaUtils;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
-import static org.springdoc.core.service.AbstractRequestService.hasNotNullAnnotation;
import static org.springdoc.core.utils.Constants.DOT;
/**
@@ -72,6 +78,11 @@
*/
public class MethodParameterPojoExtractor {
+ /**
+ * The Schema utils.
+ */
+ private final SchemaUtils schemaUtils;
+
/**
* The constant SIMPLE_TYPE_PREDICATES.
*/
@@ -96,7 +107,16 @@ public class MethodParameterPojoExtractor {
SIMPLE_TYPES.add(Iterable.class);
SIMPLE_TYPES.add(Duration.class);
SIMPLE_TYPES.add(LocalTime.class);
+ SIMPLE_TYPES.add(LocalDateTime.class);
+ SIMPLE_TYPES.add(LocalDate.class);
+ SIMPLE_TYPES.add(YearMonth.class);
+ SIMPLE_TYPES.add(MonthDay.class);
+ SIMPLE_TYPES.add(Year.class);
SIMPLE_TYPES.add(Class.class);
+ SIMPLE_TYPES.add(Period.class);
+ SIMPLE_TYPES.add(OffsetTime.class);
+ SIMPLE_TYPES.add(ZoneOffset.class);
+ SIMPLE_TYPES.add(TimeZone.class);
SIMPLE_TYPE_PREDICATES.add(Class::isPrimitive);
SIMPLE_TYPE_PREDICATES.add(Class::isEnum);
@@ -108,7 +128,8 @@ public class MethodParameterPojoExtractor {
/**
* Instantiates a new Method parameter pojo extractor.
*/
- private MethodParameterPojoExtractor() {
+ public MethodParameterPojoExtractor(SchemaUtils schemaUtils) {
+ this.schemaUtils = schemaUtils;
}
/**
@@ -117,7 +138,7 @@ private MethodParameterPojoExtractor() {
* @param clazz the clazz
* @return the stream
*/
- static Stream extractFrom(Class> clazz) {
+ Stream extractFrom(Class> clazz) {
return extractFrom(clazz, "", true);
}
@@ -129,7 +150,7 @@ static Stream extractFrom(Class> clazz) {
* @param parentRequired whether the field that hold the class currently being inspected was required or optional
* @return the stream
*/
- private static Stream extractFrom(Class> clazz, String fieldNamePrefix, boolean parentRequired) {
+ private Stream extractFrom(Class> clazz, String fieldNamePrefix, boolean parentRequired) {
return allFieldsOf(clazz).stream()
.filter(field -> !field.getType().equals(clazz))
.flatMap(f -> fromGetterOfField(clazz, f, fieldNamePrefix, parentRequired))
@@ -145,7 +166,7 @@ private static Stream extractFrom(Class> clazz, String fieldN
* @param parentRequired whether the field that holds the class currently being examined was required or optional
* @return the stream
*/
- private static Stream fromGetterOfField(Class> paramClass, Field field, String fieldNamePrefix, boolean parentRequired) {
+ private Stream fromGetterOfField(Class> paramClass, Field field, String fieldNamePrefix, boolean parentRequired) {
Class> type = extractType(paramClass, field);
if (Objects.isNull(type))
@@ -156,13 +177,13 @@ private static Stream fromGetterOfField(Class> paramClass, Fi
else {
Parameter parameter = field.getAnnotation(Parameter.class);
Schema schema = field.getAnnotation(Schema.class);
- boolean visible = resolveVisible(parameter, schema);
+ boolean visible = SchemaUtils.swaggerVisible(schema, parameter);
if (!visible) {
return Stream.empty();
}
String prefix = fieldNamePrefix + resolveName(parameter, schema).orElse(field.getName()) + DOT;
- boolean isNullable = isNullable(field.getDeclaredAnnotations());
- return extractFrom(type, prefix, parentRequired && resolveRequired(schema, parameter, isNullable));
+ boolean fieldRequired = schemaUtils.fieldRequired(field, schema, parameter);
+ return extractFrom(type, prefix, parentRequired && fieldRequired);
}
}
@@ -190,46 +211,6 @@ private static Optional resolveNameFromSchema(Schema schema) {
return Optional.of(schema.name());
}
- private static boolean resolveVisible(Parameter parameter, Schema schema) {
- if (parameter != null) {
- return !parameter.hidden();
- }
- if (schema != null) {
- return !schema.hidden();
- }
- return true;
- }
-
- private static boolean resolveRequired(Schema schema, Parameter parameter, boolean nullable) {
- if (parameter != null) {
- return resolveRequiredFromParameter(parameter, nullable);
- }
- if (schema != null) {
- return resolveRequiredFromSchema(schema, nullable);
- }
- return !nullable;
- }
-
- private static boolean resolveRequiredFromParameter(Parameter parameter, boolean nullable) {
- if (parameter.required()) {
- return true;
- }
- return !nullable;
- }
-
- private static boolean resolveRequiredFromSchema(Schema schema, boolean nullable) {
- if (schema.required()) {
- return true;
- }
- else if (schema.requiredMode() == Schema.RequiredMode.REQUIRED) {
- return true;
- }
- else if (schema.requiredMode() == Schema.RequiredMode.NOT_REQUIRED) {
- return false;
- }
- return !nullable;
- }
-
/**
* Extract the type
*
@@ -259,20 +240,21 @@ private static Class> extractType(Class> paramClass, Field field) {
* @param fieldNamePrefix the field name prefix
* @return the stream
*/
- private static Stream fromSimpleClass(Class> paramClass, Field field, String fieldNamePrefix, boolean isParentRequired) {
+ private Stream fromSimpleClass(Class> paramClass, Field field, String fieldNamePrefix, boolean parentRequired) {
Annotation[] fieldAnnotations = field.getDeclaredAnnotations();
try {
Parameter parameter = field.getAnnotation(Parameter.class);
Schema schema = field.getAnnotation(Schema.class);
- boolean isNullable = isNullable(fieldAnnotations);
- boolean isNotRequired = !(isParentRequired && resolveRequired(schema, parameter, isNullable));
+ boolean fieldRequired = schemaUtils.fieldRequired(field, schema, parameter);
+
+ boolean paramRequired = parentRequired && fieldRequired;
if (paramClass.getSuperclass() != null && paramClass.isRecord()) {
return Stream.of(paramClass.getRecordComponents())
.filter(d -> d.getName().equals(field.getName()))
.map(RecordComponent::getAccessor)
.map(method -> new MethodParameter(method, -1))
.map(methodParameter -> DelegatingMethodParameter.changeContainingClass(methodParameter, paramClass))
- .map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, isNotRequired));
+ .map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, field, !paramRequired));
}
else
return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors())
@@ -281,7 +263,7 @@ private static Stream fromSimpleClass(Class> paramClass, Fiel
.filter(Objects::nonNull)
.map(method -> new MethodParameter(method, -1))
.map(methodParameter -> DelegatingMethodParameter.changeContainingClass(methodParameter, paramClass))
- .map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, isNotRequired));
+ .map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, field, !paramRequired));
}
catch (IntrospectionException e) {
return Stream.of();
@@ -289,7 +271,7 @@ private static Stream fromSimpleClass(Class> paramClass, Fiel
}
/**
- * All fields of list.
+ * All fields of list. include parent fields
*
* @param clazz the clazz
* @return the list
@@ -352,17 +334,4 @@ public static void removeSimpleTypes(Class>... classes) {
SIMPLE_TYPES.removeAll(Arrays.asList(classes));
}
- /**
- * Is nullable boolean.
- *
- * @param fieldAnnotations the field annotations
- * @return the boolean
- */
- private static boolean isNullable(Annotation[] fieldAnnotations) {
- Collection annotationSimpleNames = Arrays.stream(fieldAnnotations)
- .map(Annotation::annotationType)
- .map(Class::getSimpleName)
- .collect(Collectors.toCollection(LinkedHashSet::new));
- return !hasNotNullAnnotation(annotationSimpleNames);
- }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java
index f47daca2b..90a48fbfb 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.filters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java
index bf6b24124..563278014 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.filters;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractRouterFunctionVisitor.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractRouterFunctionVisitor.java
index 6064b7b1f..e7698523d 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractRouterFunctionVisitor.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractRouterFunctionVisitor.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractSpringdocRouteBuilder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractSpringdocRouteBuilder.java
index cfd641ac1..4391f1301 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractSpringdocRouteBuilder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/AbstractSpringdocRouteBuilder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java
index 1018af8c8..2d1aec84f 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterOperation.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterOperation.java
index 94da9fe60..693547609 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterOperation.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterOperation.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/apiresponse/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/apiresponse/Builder.java
index 782d1df06..4b85172ae 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/apiresponse/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/apiresponse/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.apiresponse;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/arrayschema/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/arrayschema/Builder.java
index 0007e66e2..966af0e1d 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/arrayschema/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/arrayschema/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.arrayschema;
@@ -79,17 +79,17 @@ public class Builder {
/**
* The Max contain.
*/
- private int maxContains= 0;
+ private int maxContains = 0;
/**
* The Min contains.
*/
- private int minContains= 0;
+ private int minContains = 0;
/**
* The Unevaluated items.
*/
- private Schema unevaluatedItems= org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().build();
+ private Schema unevaluatedItems = org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().build();
/**
* The Prefix items.
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/content/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/content/Builder.java
index 7c3a863f7..73d621010 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/content/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/content/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.content;
@@ -139,7 +139,7 @@ public class Builder {
/**
* The All of.
*/
- private Schema[] allOf ={};
+ private Schema[] allOf = {};
/**
* Instantiates a new Content builder.
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/discriminatormapping/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/discriminatormapping/Builder.java
index f30e8a138..580c32b9a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/discriminatormapping/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/discriminatormapping/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.discriminatormapping;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/encoding/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/encoding/Builder.java
index 2e2da146f..bb3ca4b35 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/encoding/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/encoding/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.encoding;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/exampleobject/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/exampleobject/Builder.java
index d0bf83e9a..f9b22f288 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/exampleobject/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/exampleobject/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.exampleobject;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extension/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extension/Builder.java
index 2d5860599..96d7c79b7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extension/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extension/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.extension;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extensionproperty/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extensionproperty/Builder.java
index 4c198043e..7c02a9d31 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extensionproperty/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/extensionproperty/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.extensionproperty;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/externaldocumentation/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/externaldocumentation/Builder.java
index aa6624646..17764d79e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/externaldocumentation/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/externaldocumentation/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.externaldocumentation;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/header/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/header/Builder.java
index 8f57a1ded..14066d790 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/header/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/header/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.header;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/link/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/link/Builder.java
index 964ccf9b2..35e443f72 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/link/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/link/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.link;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/linkparameter/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/linkparameter/Builder.java
index 62588982c..ad1ff5445 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/linkparameter/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/linkparameter/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.linkparameter;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/operation/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/operation/Builder.java
index 35a157dcf..e9cb90e19 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/operation/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/operation/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.operation;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/parameter/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/parameter/Builder.java
index 8c02e861e..c13f93e3c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/parameter/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/parameter/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.parameter;
@@ -132,6 +132,11 @@ public class Builder {
*/
private String ref = "";
+ /**
+ * The Validation groups.
+ */
+ private Class>[] validationGroups = {};
+
/**
* Instantiates a new Parameter builder.
@@ -454,6 +459,11 @@ public Extension[] extensions() {
public String ref() {
return ref;
}
+
+ @Override
+ public Class>[] validationGroups() {
+ return validationGroups;
+ }
};
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/requestbody/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/requestbody/Builder.java
index 8f1ccb2c3..a36d100cf 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/requestbody/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/requestbody/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.requestbody;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/schema/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/schema/Builder.java
index fe7bbaff0..7d96555e0 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/schema/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/schema/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.schema;
@@ -257,7 +257,7 @@ public class Builder {
/**
* The Types.
*/
- private String[] types ={};
+ private String[] types = {};
/**
* The Exclusive maximum value.
@@ -299,6 +299,11 @@ public class Builder {
*/
private String $dynamicAnchor = "";
+ /**
+ * The Dynamic ref.
+ */
+ private String $dynamicRef = "";
+
/**
* The Content encoding.
*/
@@ -382,7 +387,7 @@ public class Builder {
/**
* The Properties.
*/
- private StringToClassMapItem[] properties= {};
+ private StringToClassMapItem[] properties = {};
/**
* The Unevaluated properties.
@@ -837,6 +842,50 @@ public Builder schemaResolution(SchemaResolution schemaResolution) {
return this;
}
+ /**
+ * Dependent required map builder.
+ *
+ * @param dependentRequiredMap the dependent required map
+ * @return the builder
+ */
+ public Builder dependentRequiredMap(DependentRequired[] dependentRequiredMap) {
+ this.dependentRequiredMap = dependentRequiredMap;
+ return this;
+ }
+
+ /**
+ * Dependent schemas builder.
+ *
+ * @param dependentSchemas the dependent schemas
+ * @return the builder
+ */
+ public Builder dependentSchemas(StringToClassMapItem[] dependentSchemas) {
+ this.dependentSchemas = dependentSchemas;
+ return this;
+ }
+
+ /**
+ * Pattern properties builder.
+ *
+ * @param patternProperties the pattern properties
+ * @return the builder
+ */
+ public Builder patternProperties(StringToClassMapItem[] patternProperties) {
+ this.patternProperties = patternProperties;
+ return this;
+ }
+
+ /**
+ * Properties builder.
+ *
+ * @param properties the properties
+ * @return the builder
+ */
+ public Builder properties(StringToClassMapItem[] properties) {
+ this.properties = properties;
+ return this;
+ }
+
/**
* Additional properties builder.
*
@@ -1105,6 +1154,11 @@ public Class> contains() {
return $dynamicAnchor;
}
+ @Override
+ public String $dynamicRef() {
+ return $dynamicRef;
+ }
+
@Override
public String contentEncoding() {
return contentEncoding;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/securityrequirement/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/securityrequirement/Builder.java
index 3aa093f5c..f12fd5d8d 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/securityrequirement/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/securityrequirement/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.securityrequirement;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/server/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/server/Builder.java
index bea8eb511..294c42e7a 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/server/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/server/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.server;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/servervariable/Builder.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/servervariable/Builder.java
index f5a975613..1967d0997 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/servervariable/Builder.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/builders/servervariable/Builder.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.fn.builders.servervariable;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin.java
index d1200600b..b68ca8c25 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedOpenAPIMixin.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.mixins;
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 8c7b53311..1798cb490 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.mixins;
@@ -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 {
/**
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin.java
index 9459b2e96..a41c7f383 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.mixins;
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 a7f820a71..1b7d9d2af 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.mixins;
@@ -44,7 +44,7 @@
*
* @author bnasslashen
*/
-@JsonPropertyOrder(value = {"type", "format", "if", "then", "else"}, alphabetic = true)
+@JsonPropertyOrder(value = { "type", "format", "if", "then", "else" }, alphabetic = true)
public interface SortedSchemaMixin31 {
/**
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ControllerAdviceInfo.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ControllerAdviceInfo.java
index e31eabec8..88d36f6e8 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ControllerAdviceInfo.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ControllerAdviceInfo.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/GroupedOpenApi.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/GroupedOpenApi.java
index 137ba3704..b43c2abba 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/GroupedOpenApi.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/GroupedOpenApi.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
@@ -123,7 +123,7 @@ public class GroupedOpenApi {
*/
private GroupedOpenApi(Builder builder) {
Assert.isTrue(StringUtils.isNotBlank(builder.group), GROUP_NAME_NOT_NULL_OR_EMPTY);
- this.group =builder.group;
+ this.group = builder.group;
this.pathsToMatch = builder.pathsToMatch;
this.packagesToScan = builder.packagesToScan;
this.producesToMatch = builder.producesToMatch;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAdviceInfo.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAdviceInfo.java
index 4da609961..ebe88daf3 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAdviceInfo.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAdviceInfo.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java
index bbe7666ed..8baac19d9 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
@@ -270,7 +270,11 @@ public void calculateConsumesProduces(Method method) {
RequestMapping reqMappingClass = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), RequestMapping.class);
if (reqMappingMethod != null && reqMappingClass != null) {
- fillMethods(ArrayUtils.addAll(reqMappingMethod.produces(), reqMappingClass.produces()), ArrayUtils.addAll(reqMappingMethod.consumes(), reqMappingClass.consumes()), reqMappingMethod.headers());
+ fillMethods(
+ calculateMethodMediaTypes(reqMappingMethod.produces(), reqMappingClass.produces()),
+ calculateMethodMediaTypes(reqMappingMethod.consumes(), reqMappingClass.consumes()),
+ reqMappingMethod.headers()
+ );
}
else if (reqMappingMethod != null) {
fillMethods(reqMappingMethod.produces(), reqMappingMethod.consumes(), reqMappingMethod.headers());
@@ -290,23 +294,42 @@ else if (reqMappingClass != null) {
* @param headers the headers
*/
private void fillMethods(String[] produces, String[] consumes, String[] headers) {
- if (ArrayUtils.isNotEmpty(produces)) {
- methodProduces = mergeArrays(methodProduces, produces);
- } else if (ArrayUtils.isNotEmpty(classProduces)) {
- methodProduces = mergeArrays(methodProduces, classProduces);
- } else if (ArrayUtils.isEmpty(methodProduces)) {
- methodProduces = new String[] {defaultProducesMediaType};
- }
+ if (ArrayUtils.isNotEmpty(produces)) {
+ methodProduces = mergeArrays(methodProduces, produces);
+ }
+ else if (ArrayUtils.isNotEmpty(classProduces)) {
+ methodProduces = mergeArrays(methodProduces, classProduces);
+ }
+ else if (ArrayUtils.isEmpty(methodProduces)) {
+ methodProduces = new String[] { defaultProducesMediaType };
+ }
+
+ if (ArrayUtils.isNotEmpty(consumes)) {
+ methodConsumes = mergeArrays(methodConsumes, consumes);
+ }
+ else if (ArrayUtils.isNotEmpty(classConsumes)) {
+ methodConsumes = mergeArrays(methodConsumes, classConsumes);
+ }
+ else if (ArrayUtils.isEmpty(methodConsumes)) {
+ methodConsumes = new String[] { defaultConsumesMediaType };
+ }
- if (ArrayUtils.isNotEmpty(consumes)) {
- methodConsumes = mergeArrays(methodConsumes, consumes);
- } else if (ArrayUtils.isNotEmpty(classConsumes)) {
- methodConsumes = mergeArrays(methodConsumes, classConsumes);
- } else if (ArrayUtils.isEmpty(methodConsumes)) {
- methodConsumes = new String[] {defaultConsumesMediaType};
- }
+ setHeaders(headers);
+ }
- setHeaders(headers);
+ /**
+ * If there is any method type(s) present, then these will override the class type(s).
+ * See ... for details
+ *
+ * @param methodTypes the method types
+ * @param classTypes the class types
+ * @return the string [ ] containing the types that can be used for the method
+ */
+ private String[] calculateMethodMediaTypes(@Nullable String[] methodTypes, String[] classTypes) {
+ if (ArrayUtils.isNotEmpty(methodTypes)) {
+ return methodTypes;
+ }
+ return classTypes;
}
/**
@@ -474,7 +497,7 @@ public void setWithResponseBodySchemaDoc(boolean withResponseBodySchemaDoc) {
public void calculateHeadersForClass(Class> declaringClass) {
RequestMapping reqMappingClass = AnnotatedElementUtils.findMergedAnnotation(declaringClass, RequestMapping.class);
if (reqMappingClass != null) {
- fillMethods(reqMappingClass.produces(), reqMappingClass.consumes(), reqMappingClass.headers());
+ setHeaders(reqMappingClass.headers());
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterId.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterId.java
index e541d8b4d..5371cff8e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterId.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterId.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterInfo.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterInfo.java
index 158198bed..93098ce6d 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterInfo.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterInfo.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
@@ -85,7 +85,7 @@ public class ParameterInfo {
private String paramType;
/**
- * if the paramater type is RequestPart
+ * if the parameter type is RequestPart
*/
private boolean requestPart;
@@ -345,7 +345,7 @@ public void setParameterId(ParameterId parameterId) {
* @return the boolean
*/
public boolean isParameterObject() {
- return methodParameter instanceof DelegatingMethodParameter delegatingMethodParameter
+ return methodParameter instanceof DelegatingMethodParameter delegatingMethodParameter
&& delegatingMethodParameter.isParameterObject();
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/RequestBodyInfo.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/RequestBodyInfo.java
index b5130e395..957421adf 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/RequestBodyInfo.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/RequestBodyInfo.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.models;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/AbstractSwaggerUiConfigProperties.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/AbstractSwaggerUiConfigProperties.java
index ce857ea4e..49e8fe065 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/AbstractSwaggerUiConfigProperties.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/AbstractSwaggerUiConfigProperties.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.properties;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SpringDocConfigProperties.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SpringDocConfigProperties.java
index 25d76f1d2..d27787f51 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SpringDocConfigProperties.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SpringDocConfigProperties.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.properties;
@@ -274,6 +274,41 @@ public class SpringDocConfigProperties {
*/
private OpenAPI OpenApi;
+ /**
+ * The Enable extra schemas resolution.
+ */
+ private boolean enableExtraSchemas;
+
+ /**
+ * Set explicit-object-schema to true to always include type:
+ * object in the schema, or to false to omit type: object.
+ */
+ private boolean explicitObjectSchema;
+
+ /**
+ * When set to true, schemas without a defined type will be deserialized as an ArbitrarySchema (with no type),
+ * instead of an ObjectSchema with type: object.
+ */
+ private boolean useArbitrarySchemas;
+
+ /**
+ * Is enable additional schemas resolution boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isEnableExtraSchemas() {
+ return enableExtraSchemas;
+ }
+
+ /**
+ * Sets enable additional schemas resolution.
+ *
+ * @param enableExtraSchemas the enable additional schemas resolution
+ */
+ public void setEnableExtraSchemas(boolean enableExtraSchemas) {
+ this.enableExtraSchemas = enableExtraSchemas;
+ }
+
/**
* Gets open api.
*
@@ -319,15 +354,6 @@ public Boolean getOverrideWithGenericResponse() {
return overrideWithGenericResponse;
}
- /**
- * Sets override with generic response.
- *
- * @param overrideWithGenericResponse the override with generic response
- */
- public void setOverrideWithGenericResponse(Boolean overrideWithGenericResponse) {
- this.overrideWithGenericResponse = overrideWithGenericResponse;
- }
-
/**
* Is nullable request parameter enabled boolean.
*
@@ -544,7 +570,6 @@ public void setModelConverters(ModelConverters modelConverters) {
this.modelConverters = modelConverters;
}
-
/**
* Is use management port boolean.
*
@@ -842,7 +867,6 @@ public boolean isCacheDisabled() {
return cache.isDisabled();
}
-
/**
* Gets group configs.
*
@@ -915,6 +939,15 @@ public boolean isOverrideWithGenericResponse() {
return overrideWithGenericResponse != null && overrideWithGenericResponse;
}
+ /**
+ * Sets override with generic response.
+ *
+ * @param overrideWithGenericResponse the override with generic response
+ */
+ public void setOverrideWithGenericResponse(Boolean overrideWithGenericResponse) {
+ this.overrideWithGenericResponse = overrideWithGenericResponse;
+ }
+
/**
* Sets override with generic response.
*
@@ -1036,21 +1069,21 @@ public boolean isPreLoadingEnabled() {
}
/**
- * locale list to pre-loading.
+ * Sets locale list to pre-loading.
*
- * @return the Locales
+ * @param preLoadingEnabled the Locales
*/
- public List getPreLoadingLocales() {
- return preLoadingLocales;
+ public void setPreLoadingEnabled(boolean preLoadingEnabled) {
+ this.preLoadingEnabled = preLoadingEnabled;
}
/**
- * Sets locale list to pre-loading.
+ * locale list to pre-loading.
*
- * @param preLoadingEnabled the Locales
+ * @return the Locales
*/
- public void setPreLoadingEnabled(boolean preLoadingEnabled) {
- this.preLoadingEnabled = preLoadingEnabled;
+ public List getPreLoadingLocales() {
+ return preLoadingLocales;
}
/**
@@ -1062,6 +1095,100 @@ public void setPreLoadingLocales(List preLoadingLocales) {
this.preLoadingLocales = preLoadingLocales;
}
+ /**
+ * Is show oauth 2 endpoints boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isShowOauth2Endpoints() {
+ return showOauth2Endpoints;
+ }
+
+ /**
+ * Sets show oauth 2 endpoints.
+ *
+ * @param showOauth2Endpoints the show oauth 2 endpoints
+ */
+ public void setShowOauth2Endpoints(boolean showOauth2Endpoints) {
+ this.showOauth2Endpoints = showOauth2Endpoints;
+ }
+
+ /**
+ * Gets spec version.
+ *
+ * @return the spec version
+ */
+ public SpecVersion getSpecVersion() {
+ if (apiDocs.getVersion() == OpenApiVersion.OPENAPI_3_1)
+ return SpecVersion.V31;
+ return SpecVersion.V30;
+ }
+
+ /**
+ * Is openapi 31 boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isOpenapi31() {
+ if (apiDocs.getVersion() == OpenApiVersion.OPENAPI_3_1)
+ return true;
+ return false;
+ }
+
+ /**
+ * Is enable default api docs boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isEnableDefaultApiDocs() {
+ return enableDefaultApiDocs;
+ }
+
+ /**
+ * Sets enable default api docs.
+ *
+ * @param enableDefaultApiDocs the enable default api docs
+ */
+ public void setEnableDefaultApiDocs(boolean enableDefaultApiDocs) {
+ this.enableDefaultApiDocs = enableDefaultApiDocs;
+ }
+
+ /**
+ * Is explicit object schema boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isExplicitObjectSchema() {
+ return explicitObjectSchema;
+ }
+
+ /**
+ * Sets explicit object schema.
+ *
+ * @param explicitObjectSchema the explicit object schema
+ */
+ public void setExplicitObjectSchema(boolean explicitObjectSchema) {
+ this.explicitObjectSchema = explicitObjectSchema;
+ }
+
+ /**
+ * Is use arbitrary schemas boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isUseArbitrarySchemas() {
+ return useArbitrarySchemas;
+ }
+
+ /**
+ * Sets use arbitrary schemas.
+ *
+ * @param useArbitrarySchemas the use arbitrary schemas
+ */
+ public void setUseArbitrarySchemas(boolean useArbitrarySchemas) {
+ this.useArbitrarySchemas = useArbitrarySchemas;
+ }
+
/**
* The type Model converters.
*
@@ -1419,6 +1546,24 @@ public void setVersion(OpenApiVersion version) {
this.version = version;
}
+ /**
+ * Is resolve extensions properties boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isResolveExtensionsProperties() {
+ return resolveExtensionsProperties;
+ }
+
+ /**
+ * Sets resolve extensions properties.
+ *
+ * @param resolveExtensionsProperties the resolve extensions properties
+ */
+ public void setResolveExtensionsProperties(boolean resolveExtensionsProperties) {
+ this.resolveExtensionsProperties = resolveExtensionsProperties;
+ }
+
/**
* The enum OpenApiVersion.
*/
@@ -1455,24 +1600,6 @@ public String getVersion() {
return version;
}
}
-
- /**
- * Is resolve extensions properties boolean.
- *
- * @return the boolean
- */
- public boolean isResolveExtensionsProperties() {
- return resolveExtensionsProperties;
- }
-
- /**
- * Sets resolve extensions properties.
- *
- * @param resolveExtensionsProperties the resolve extensions properties
- */
- public void setResolveExtensionsProperties(boolean resolveExtensionsProperties) {
- this.resolveExtensionsProperties = resolveExtensionsProperties;
- }
}
/**
@@ -1505,7 +1632,6 @@ public void setEnabled(boolean enabled) {
}
}
-
/**
* The type Cache.
*
@@ -1821,63 +1947,4 @@ public int hashCode() {
return Objects.hash(group);
}
}
-
-
- /**
- * Is show oauth 2 endpoints boolean.
- *
- * @return the boolean
- */
- public boolean isShowOauth2Endpoints() {
- return showOauth2Endpoints;
- }
-
- /**
- * Sets show oauth 2 endpoints.
- *
- * @param showOauth2Endpoints the show oauth 2 endpoints
- */
- public void setShowOauth2Endpoints(boolean showOauth2Endpoints) {
- this.showOauth2Endpoints = showOauth2Endpoints;
- }
-
- /**
- * Gets spec version.
- *
- * @return the spec version
- */
- public SpecVersion getSpecVersion() {
- if (apiDocs.getVersion() == OpenApiVersion.OPENAPI_3_1)
- return SpecVersion.V31;
- return SpecVersion.V30;
- }
-
- /**
- * Is openapi 31 boolean.
- *
- * @return the boolean
- */
- public boolean isOpenapi31() {
- if (apiDocs.getVersion() == OpenApiVersion.OPENAPI_3_1)
- return true;
- return false;
- }
-
- /**
- * Is enable default api docs boolean.
- *
- * @return the boolean
- */
- public boolean isEnableDefaultApiDocs() {
- return enableDefaultApiDocs;
- }
-
- /**
- * Sets enable default api docs.
- *
- * @param enableDefaultApiDocs the enable default api docs
- */
- public void setEnableDefaultApiDocs(boolean enableDefaultApiDocs) {
- this.enableDefaultApiDocs = enableDefaultApiDocs;
- }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java
index dab9a56fb..ee3dac07c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.properties;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigProperties.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigProperties.java
index d4158bcc3..dd3544909 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigProperties.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigProperties.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.properties;
@@ -63,6 +63,16 @@
@ConditionalOnBean(SpringDocConfiguration.class)
public class SwaggerUiConfigProperties extends AbstractSwaggerUiConfigProperties implements InitializingBean {
+ /**
+ * The constant SPRINGDOC_CONFIG_PROPERTIES.
+ */
+ public static final String SPRINGDOC_CONFIG_PROPERTIES = "springdoc.config.properties";
+
+ /**
+ * The constant SPRINGDOC_SWAGGERUI_VERSION.
+ */
+ private static final String SPRINGDOC_SWAGGER_VERSION = SPRINGDOC_SWAGGER_PREFIX + ".version";
+
/**
* The Disable swagger default url.
*/
@@ -93,17 +103,6 @@ public class SwaggerUiConfigProperties extends AbstractSwaggerUiConfigProperties
*/
private boolean useRootPath;
- /**
- * The constant SPRINGDOC_SWAGGERUI_VERSION.
- */
- private static final String SPRINGDOC_SWAGGER_VERSION = SPRINGDOC_SWAGGER_PREFIX+".version";
-
- /**
- * The constant SPRINGDOC_CONFIG_PROPERTIES.
- */
- public static final String SPRINGDOC_CONFIG_PROPERTIES = "springdoc.config.properties";
-
-
@Override
public void afterPropertiesSet() {
if (StringUtils.isEmpty(version)) {
@@ -117,6 +116,7 @@ public void afterPropertiesSet() {
}
}
}
+
/**
* Gets swagger ui version.
*
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiOAuthProperties.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiOAuthProperties.java
index 68ae492b5..a41491cd6 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiOAuthProperties.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiOAuthProperties.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.properties;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ActuatorProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ActuatorProvider.java
index a4ad04a9b..c072d0283 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ActuatorProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ActuatorProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
@@ -97,7 +97,7 @@ public abstract class ActuatorProvider implements ApplicationListener hateoasPropertiesOptional,
this.objectMapperProvider = objectMapperProvider;
}
+ private static boolean isHalEnabled(@NonNull HateoasProperties hateoasProperties) {
+ // In spring-boot 3.5, the method name was changed from getUseHalAsDefaultJsonMediaType to isUseHalAsDefaultJsonMediaType
+ var possibleMethodNames = List.of("isUseHalAsDefaultJsonMediaType", "getUseHalAsDefaultJsonMediaType");
+
+ for (var methodName : possibleMethodNames) {
+ var method = ReflectionUtils.findMethod(HateoasProperties.class, methodName);
+ if (method != null) {
+ var result = ReflectionUtils.invokeMethod(method, hateoasProperties);
+ if (result instanceof Boolean halEnabled) {
+ return halEnabled;
+ }
+
+ throw new IllegalStateException("Method " + methodName + " did not return a boolean value");
+ }
+ }
+
+ throw new IllegalStateException("No suitable method found to determine if HAL is enabled");
+ }
+
/**
* Init.
*/
@@ -79,8 +101,7 @@ protected void init() {
*/
public boolean isHalEnabled() {
return hateoasPropertiesOptional
- .map(HateoasProperties::getUseHalAsDefaultJsonMediaType)
+ .map(HateoasHalProvider::isHalEnabled)
.orElse(true);
}
-
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/JavadocProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/JavadocProvider.java
index 3a85fb940..7e73c4f28 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/JavadocProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/JavadocProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
@@ -102,4 +102,9 @@ public interface JavadocProvider {
* @return the first sentence based on javadoc guidelines
*/
String getFirstSentence(String text);
+
+ /**
+ * Clean the temp resources.
+ */
+ default void clearCache() {}
}
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 1b5f8eeb9..e7e0710d0 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
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
@@ -74,6 +74,12 @@ public ObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties)
if (openApiVersion == OpenApiVersion.OPENAPI_3_1) {
jsonMapper = Json31.mapper();
yamlMapper = Yaml31.mapper();
+ if (springDocConfigProperties.isUseArbitrarySchemas()) {
+ System.setProperty(Schema.USE_ARBITRARY_SCHEMA_PROPERTY, "true");
+ }
+ if (springDocConfigProperties.isExplicitObjectSchema()) {
+ System.setProperty(Schema.EXPLICIT_OBJECT_SCHEMA_PROPERTY, "true");
+ }
}
else {
jsonMapper = Json.mapper();
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestConfigurationProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestConfigurationProvider.java
index c51746fff..e45b78ee1 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestConfigurationProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestConfigurationProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestResourceProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestResourceProvider.java
index 23fbddf1a..2f5c89859 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestResourceProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RepositoryRestResourceProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RouterFunctionProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RouterFunctionProvider.java
index ab338a25f..6dd455762 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RouterFunctionProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/RouterFunctionProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SecurityOAuth2Provider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SecurityOAuth2Provider.java
index 284685dbf..9b028a848 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SecurityOAuth2Provider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SecurityOAuth2Provider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringCloudFunctionProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringCloudFunctionProvider.java
index c729384c0..c8cdae452 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringCloudFunctionProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringCloudFunctionProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDataWebPropertiesProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDataWebPropertiesProvider.java
index 6892fed96..b2fd4757e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDataWebPropertiesProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDataWebPropertiesProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocJavadocProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocJavadocProvider.java
index dc0fa7dd7..d3b940090 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocJavadocProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocJavadocProvider.java
@@ -21,15 +21,18 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
import com.github.therapi.runtimejavadoc.ClassJavadoc;
@@ -56,6 +59,11 @@ public class SpringDocJavadocProvider implements JavadocProvider {
*/
private final CommentFormatter formatter = new CommentFormatter();
+ /**
+ * The Class javadoc cache.
+ */
+ private final Map, ClassJavadoc> classJavadocCache = new HashMap<>();
+
/**
* Gets class description.
@@ -65,7 +73,7 @@ public class SpringDocJavadocProvider implements JavadocProvider {
*/
@Override
public String getClassJavadoc(Class> cl) {
- ClassJavadoc classJavadoc = RuntimeJavadoc.getJavadoc(cl);
+ ClassJavadoc classJavadoc = getJavadoc(cl);
return formatter.format(classJavadoc.getComment());
}
@@ -77,7 +85,7 @@ public String getClassJavadoc(Class> cl) {
*/
@Override
public Map getRecordClassParamJavadoc(Class> cl) {
- ClassJavadoc classJavadoc = RuntimeJavadoc.getJavadoc(cl);
+ ClassJavadoc classJavadoc = getJavadoc(cl);
return classJavadoc.getRecordComponents().stream()
.collect(Collectors.toMap(ParamJavadoc::getName, recordClass -> formatter.format(recordClass.getComment())));
}
@@ -90,7 +98,7 @@ public Map getRecordClassParamJavadoc(Class> cl) {
*/
@Override
public String getMethodJavadocDescription(Method method) {
- MethodJavadoc methodJavadoc = RuntimeJavadoc.getJavadoc(method);
+ MethodJavadoc methodJavadoc = getJavadoc(method);
return formatter.format(methodJavadoc.getComment());
}
@@ -102,7 +110,7 @@ public String getMethodJavadocDescription(Method method) {
*/
@Override
public String getMethodJavadocReturn(Method method) {
- MethodJavadoc methodJavadoc = RuntimeJavadoc.getJavadoc(method);
+ MethodJavadoc methodJavadoc = getJavadoc(method);
return formatter.format(methodJavadoc.getReturns());
}
@@ -113,7 +121,7 @@ public String getMethodJavadocReturn(Method method) {
* @return the method throws (name-description map)
*/
public Map getMethodJavadocThrows(Method method) {
- return RuntimeJavadoc.getJavadoc(method)
+ return getJavadoc(method)
.getThrows()
.stream()
.collect(toMap(ThrowsJavadoc::getName, javadoc -> formatter.format(javadoc.getComment())));
@@ -123,12 +131,12 @@ public Map getMethodJavadocThrows(Method method) {
* Gets param javadoc.
*
* @param method the method
- * @param name the name
+ * @param name the name
* @return the param javadoc
*/
@Override
public String getParamJavadoc(Method method, String name) {
- MethodJavadoc methodJavadoc = RuntimeJavadoc.getJavadoc(method);
+ MethodJavadoc methodJavadoc = getJavadoc(method);
List paramsDoc = methodJavadoc.getParams();
return paramsDoc.stream().filter(paramJavadoc1 -> name.equals(paramJavadoc1.getName())).findAny()
.map(paramJavadoc1 -> formatter.format(paramJavadoc1.getComment())).orElse(null);
@@ -142,7 +150,7 @@ public String getParamJavadoc(Method method, String name) {
*/
@Override
public String getFieldJavadoc(Field field) {
- FieldJavadoc fieldJavadoc = RuntimeJavadoc.getJavadoc(field);
+ FieldJavadoc fieldJavadoc = getJavadoc(field);
return formatter.format(fieldJavadoc.getComment());
}
@@ -173,4 +181,38 @@ public String getFirstSentence(String text) {
}
return text;
}
+
+ private ClassJavadoc getJavadoc(Class> cl) {
+ ClassJavadoc classJavadoc = classJavadocCache.get(cl);
+ if (classJavadoc != null) {
+ return classJavadoc;
+ }
+ classJavadoc = RuntimeJavadoc.getJavadoc(cl);
+ classJavadocCache.put(cl, classJavadoc);
+ return classJavadoc;
+ }
+
+ private MethodJavadoc getJavadoc(Method method) {
+ ClassJavadoc classJavadoc = getJavadoc(method.getDeclaringClass());
+ List paramTypes = Arrays.stream(method.getParameterTypes())
+ .map(Class::getCanonicalName)
+ .toList();
+ return classJavadoc.getMethods()
+ .stream()
+ .filter(it -> Objects.equals(method.getName(), it.getName()) && Objects.equals(paramTypes, it.getParamTypes()))
+ .findFirst().orElseGet(() -> MethodJavadoc.createEmpty(method));
+ }
+
+ private FieldJavadoc getJavadoc(Field field) {
+ ClassJavadoc classJavadoc = getJavadoc(field.getDeclaringClass());
+ return classJavadoc.getFields()
+ .stream()
+ .filter(it -> Objects.equals(field.getName(), it.getName()))
+ .findFirst().orElseGet(() -> FieldJavadoc.createEmpty(field.getName()));
+ }
+
+ @Override
+ public void clearCache() {
+ classJavadocCache.clear();
+ }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocProviders.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocProviders.java
index c386fb932..a86382b82 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocProviders.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocProviders.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringRepositoryRestResourceProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringRepositoryRestResourceProvider.java
index e1f84ac17..e918da933 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringRepositoryRestResourceProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringRepositoryRestResourceProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
@@ -149,14 +149,14 @@ public class SpringRepositoryRestResourceProvider implements RepositoryRestResou
private final ObjectMapper mapper;
/**
- * The Application context.
+ * The Spring doc data rest utils.
*/
- private ApplicationContext applicationContext;
+ private final SpringDocDataRestUtils springDocDataRestUtils;
/**
- * The Spring doc data rest utils.
+ * The Application context.
*/
- private final SpringDocDataRestUtils springDocDataRestUtils;
+ private ApplicationContext applicationContext;
/**
* The Handler mapping list.
@@ -170,7 +170,7 @@ public class SpringRepositoryRestResourceProvider implements RepositoryRestResou
* @param mapper the mapper
* @param springDocDataRestUtils the spring doc data rest utils
*/
- public SpringRepositoryRestResourceProvider(DataRestRouterOperationService dataRestRouterOperationService,
+ public SpringRepositoryRestResourceProvider(DataRestRouterOperationService dataRestRouterOperationService,
ObjectMapper mapper, SpringDocDataRestUtils springDocDataRestUtils) {
this.dataRestRouterOperationService = dataRestRouterOperationService;
this.mapper = mapper;
@@ -182,7 +182,7 @@ public SpringRepositoryRestResourceProvider(DataRestRouterOperationService dataR
* Gets router operations.
*
* @param openAPI the open api
- * @param locale the locale
+ * @param locale the locale
* @return the router operations
*/
public List getRouterOperations(OpenAPI openAPI, Locale locale) {
@@ -201,7 +201,7 @@ public List getRouterOperations(OpenAPI openAPI, Locale locale)
final JacksonMetadata jackson = new JacksonMetadata(mapper, domainType);
boolean hiddenRepository = (AnnotationUtils.findAnnotation(repository, Hidden.class) != null);
if (!hiddenRepository) {
- if (resourceMetadata!=null && resourceMetadata.isExported()) {
+ if (resourceMetadata != null && resourceMetadata.isExported()) {
for (HandlerMapping handlerMapping : handlerMappingList) {
if (handlerMapping instanceof RepositoryRestHandlerMapping repositoryRestHandlerMapping) {
Map handlerMethodMap = repositoryRestHandlerMapping.getHandlerMethods();
@@ -335,7 +335,7 @@ private void findSearchResourceMappings(OpenAPI openAPI, List r
.getValue().getBeanType().getName()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a1, a2) -> a1));
ResourceMetadata metadata = associations.getMetadataFor(dataRestRepository.getDomainType());
- if(metadata!=null && metadata.isExported()) {
+ if (metadata != null && metadata.isExported()) {
SearchResourceMappings searchResourceMappings = metadata.getSearchResourceMappings();
if (searchResourceMappings.isExported()) {
findSearchControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, dataRestRepository, openAPI, searchResourceMappings);
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringWebProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringWebProvider.java
index 0c392d78c..aebda82b2 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringWebProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringWebProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/WebConversionServiceProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/WebConversionServiceProvider.java
index e572422c8..063550427 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/WebConversionServiceProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/WebConversionServiceProvider.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.providers;
@@ -120,7 +120,7 @@ public Class> getSpringConvertedType(Class> clazz) {
Field convertersField = FieldUtils.getDeclaredField(GenericConversionService.class, CONVERTERS, true);
if (convertersField != null) {
Object converters;
- if (!AopUtils.isAopProxy(formattingConversionService)){
+ if (!AopUtils.isAopProxy(formattingConversionService)) {
try {
converters = convertersField.get(formattingConversionService);
Map springConverters = (Map) FieldUtils.readDeclaredField(converters, CONVERTERS, true);
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 c232d7e79..209cbfd02 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
@@ -31,8 +31,9 @@
import java.io.Reader;
import java.io.Writer;
import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.math.BigDecimal;
import java.security.Principal;
import java.time.ZoneId;
import java.util.ArrayList;
@@ -54,32 +55,35 @@
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonView;
+import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
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.media.ArraySchema;
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 jakarta.validation.constraints.DecimalMax;
-import jakarta.validation.constraints.DecimalMin;
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
-import jakarta.validation.constraints.Pattern;
-import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
import org.springdoc.core.customizers.ParameterCustomizer;
+import org.springdoc.core.customizers.SpringDocCustomizers;
import org.springdoc.core.discoverer.SpringDocParameterNameDiscoverer;
import org.springdoc.core.extractor.DelegatingMethodParameter;
+import org.springdoc.core.extractor.MethodParameterPojoExtractor;
import org.springdoc.core.models.MethodAttributes;
import org.springdoc.core.models.ParameterId;
import org.springdoc.core.models.ParameterInfo;
import org.springdoc.core.models.RequestBodyInfo;
import org.springdoc.core.properties.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
import org.springdoc.core.providers.JavadocProvider;
+import org.springdoc.core.providers.ObjectMapperProvider;
+import org.springdoc.core.utils.SchemaUtils;
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
import org.springframework.core.MethodParameter;
@@ -104,8 +108,7 @@
import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;
import static org.springdoc.core.service.GenericParameterService.isFile;
-import static org.springdoc.core.utils.Constants.OPENAPI_ARRAY_TYPE;
-import static org.springdoc.core.utils.Constants.OPENAPI_STRING_TYPE;
+import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
import static org.springdoc.core.utils.SpringDocUtils.getParameterAnnotations;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
@@ -118,25 +121,14 @@
public abstract class AbstractRequestService {
/**
- * The constant PARAM_TYPES_TO_IGNORE.
- */
- private static final List> PARAM_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
-
- /**
- * The constant ANNOTATIONS_FOR_REQUIRED.
- */
-// using string litterals to support both validation-api v1 and v2
- private static final String[] ANNOTATIONS_FOR_REQUIRED = { "NotNull", "NonNull", "NotBlank", "NotEmpty" };
-
- /**
- * The constant POSITIVE_OR_ZERO.
+ * The constant LOGGER.
*/
- private static final String POSITIVE_OR_ZERO = "PositiveOrZero";
-
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRequestService.class);
+
/**
- * The constant NEGATIVE_OR_ZERO.
+ * The constant PARAM_TYPES_TO_IGNORE.
*/
- private static final String NEGATIVE_OR_ZERO = "NegativeOrZero";
+ private static final List> PARAM_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
static {
PARAM_TYPES_TO_IGNORE.add(WebRequest.class);
@@ -190,23 +182,36 @@ public abstract class AbstractRequestService {
*/
private boolean defaultSupportFormData;
+ /**
+ * The Optional delegating method parameter customizers.
+ */
+ private final Optional> optionalDelegatingMethodParameterCustomizers;
+
+ /**
+ * The Method parameter pojo extractor.
+ */
+ private final MethodParameterPojoExtractor methodParameterPojoExtractor;
/**
* Instantiates a new Abstract request builder.
*
* @param parameterBuilder the parameter builder
* @param requestBodyService the request body builder
- * @param parameterCustomizers the parameter customizers
+ * @param springDocCustomizers the spring doc customizers
* @param localSpringDocParameterNameDiscoverer the local spring doc parameter name discoverer
+ * @param methodParameterPojoExtractor the method parameter pojo extractor
*/
protected AbstractRequestService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
- Optional> parameterCustomizers,
- SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer) {
+ SpringDocCustomizers springDocCustomizers,
+ SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer,
+ MethodParameterPojoExtractor methodParameterPojoExtractor) {
super();
this.parameterBuilder = parameterBuilder;
this.requestBodyService = requestBodyService;
+ this.optionalDelegatingMethodParameterCustomizers = springDocCustomizers.getOptionalDelegatingMethodParameterCustomizers();
+ this.methodParameterPojoExtractor = methodParameterPojoExtractor;
+ this.parameterCustomizers = springDocCustomizers.getParameterCustomizers();
parameterCustomizers.ifPresent(customizers -> customizers.removeIf(Objects::isNull));
- this.parameterCustomizers = parameterCustomizers;
this.localSpringDocParameterNameDiscoverer = localSpringDocParameterNameDiscoverer;
this.defaultFlatParamObject = parameterBuilder.getPropertyResolverUtils().getSpringDocConfigProperties().isDefaultFlatParamObject();
this.defaultSupportFormData = parameterBuilder.getPropertyResolverUtils().getSpringDocConfigProperties().isDefaultSupportFormData();
@@ -280,6 +285,9 @@ public static Collection getHeaders(MethodAttributes methodAttributes
* @param methodAttributes the method attributes
* @param openAPI the open api
* @return the operation
+ * @see org.springdoc.core.customizers.DelegatingMethodParameterCustomizer#customizeList(MethodParameter, List)
+ * @see ParameterCustomizer#customize(Parameter, MethodParameter)
+ * @see org.springdoc.core.customizers.PropertyCustomizer#customize(Schema, AnnotatedType)
*/
public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
Operation operation, MethodAttributes methodAttributes, OpenAPI openAPI) {
@@ -292,7 +300,8 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new);
if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull))
pNames = reflectionParametersNames;
- parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getOptionalDelegatingMethodParameterCustomizers(), this.defaultFlatParamObject);
+ // Process: DelegatingMethodParameterCustomizer
+ parameters = DelegatingMethodParameter.customize(pNames, parameters, optionalDelegatingMethodParameterCustomizers, methodParameterPojoExtractor, this.defaultFlatParamObject);
RequestBodyInfo requestBodyInfo = new RequestBodyInfo();
List operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
Map parametersDocMap = getApiParameters(handlerMethod.getMethod());
@@ -343,15 +352,18 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
parameter.setDescription(paramJavadocDescription);
}
}
- applyBeanValidatorAnnotations(parameter, parameterAnnotations, parameterInfo.isParameterObject());
+ // Process: applyValidationsToSchema
+ applyBeanValidatorAnnotations(methodParameter, parameter, parameterAnnotations, parameterInfo.isParameterObject(), openAPI.getOpenapi());
}
else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.getVersion().equals(openAPI.getOpenapi())) {
if (operation.getRequestBody() != null)
requestBodyInfo.setRequestBody(operation.getRequestBody());
+ // Process: PropertyCustomizer
requestBodyService.calculateRequestBodyInfo(components, methodAttributes,
parameterInfo, requestBodyInfo);
applyBeanValidatorAnnotations(requestBodyInfo.getRequestBody(), parameterAnnotations, methodParameter.isOptional());
}
+ // Process: ParameterCustomizer
customiseParameter(parameter, parameterInfo, operationParameters);
}
}
@@ -402,7 +414,12 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.
*/
private LinkedHashMap getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List operationParameters, Map parametersDocMap) {
LinkedHashMap map = operationParameters.stream().collect(Collectors.toMap(ParameterId::new, parameter -> parameter, (u, v) -> {
- throw new IllegalStateException(String.format("Duplicate key %s", u));
+ LOGGER.warn(
+ "Duplicate OpenAPI parameter detected: name='{}', in='{}'. Keeping the first found and ignoring the rest. " +
+ "Declare the parameter only once.",
+ u.getName(), u.getIn()
+ );
+ return u;
}, LinkedHashMap::new));
for (Entry entry : parametersDocMap.entrySet()) {
@@ -473,6 +490,8 @@ public boolean isParamToIgnore(MethodParameter parameter) {
return false;
if (isRequestBodyWithMapType(parameter))
return false;
+ if (isRequestPartWithMapType(parameter))
+ return false;
return isRequestTypeToIgnore(parameter.getParameterType());
}
@@ -512,6 +531,23 @@ private boolean isRequestBodyWithMapType(MethodParameter parameter) {
return Map.class.isAssignableFrom(parameterType);
}
+ /**
+ * Is request part with map type
+ *
+ * @param parameter the parameter
+ * @return the boolean
+ */
+ private boolean isRequestPartWithMapType(MethodParameter parameter) {
+ // Check for @RequestPart annotation
+ org.springframework.web.bind.annotation.RequestPart requestPart = parameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestPart.class);
+ if (requestPart == null) {
+ return false;
+ }
+ // Check if the parameter type is assignable to Map
+ Class> parameterType = parameter.getParameterType();
+ return Map.class.isAssignableFrom(parameterType);
+ }
+
/**
* Sets params.
*
@@ -617,19 +653,43 @@ public Parameter buildParam(ParameterInfo parameterInfo, Components components,
/**
* Apply bean validator annotations.
*
+ * @param methodParameter the method parameter
* @param parameter the parameter
* @param annotations the annotations
* @param isParameterObject the is parameter object
+ * @param openapiVersion the openapi version
*/
- public void applyBeanValidatorAnnotations(final Parameter parameter, final List annotations, final boolean isParameterObject) {
- Map annos = new HashMap<>();
- if (annotations != null)
- annotations.forEach(annotation -> annos.put(annotation.annotationType().getSimpleName(), annotation));
- boolean annotationExists = hasNotNullAnnotation(annos.keySet());
- if (annotationExists && !isParameterObject)
+ public void applyBeanValidatorAnnotations(final MethodParameter methodParameter, final Parameter parameter, final List annotations, final boolean isParameterObject, String openapiVersion) {
+ boolean annotatedNotNull = annotations != null && SchemaUtils.annotatedNotNull(annotations);
+ if (annotatedNotNull && !isParameterObject) {
parameter.setRequired(true);
- Schema> schema = parameter.getSchema();
- applyValidationsToSchema(annos, schema);
+ }
+ if (annotations != null) {
+ Schema> schema = parameter.getSchema();
+ SchemaUtils.applyValidationsToSchema(schema, annotations, openapiVersion);
+ if (schema instanceof ArraySchema && methodParameter instanceof DelegatingMethodParameter mp) {
+ java.lang.reflect.AnnotatedType annotatedType = null;
+ if (isParameterObject) {
+ Field field = mp.getField();
+ if (field != null) {
+ annotatedType = field.getAnnotatedType();
+ }
+ }
+ else {
+ java.lang.reflect.Parameter param = mp.getParameter();
+ annotatedType = param.getAnnotatedType();
+ }
+ if (annotatedType instanceof AnnotatedParameterizedType paramType) {
+ java.lang.reflect.AnnotatedType[] typeArgs = paramType.getAnnotatedActualTypeArguments();
+ for (java.lang.reflect.AnnotatedType typeArg : typeArgs) {
+ List genericAnnotations = Arrays.stream(typeArg.getAnnotations()).toList();
+ Schema schemaItemsClone = cloneViaJson(schema.getItems(), Schema.class, ObjectMapperProvider.createJson(parameterBuilder.getPropertyResolverUtils().getSpringDocConfigProperties()));
+ schema.items(schemaItemsClone);
+ SchemaUtils.applyValidationsToSchema(schemaItemsClone, genericAnnotations, openapiVersion);
+ }
+ }
+ }
+ }
}
/**
@@ -652,7 +712,7 @@ public void applyBeanValidatorAnnotations(final RequestBody requestBody, final L
.filter(annotation -> io.swagger.v3.oas.annotations.parameters.RequestBody.class.equals(annotation.annotationType()))
.anyMatch(annotation -> ((io.swagger.v3.oas.annotations.parameters.RequestBody) annotation).required());
}
- boolean validationExists = hasNotNullAnnotation(annos.keySet());
+ boolean validationExists = SchemaUtils.annotatedNotNull(annos.values().stream().toList());
if (validationExists || (!isOptional && (springRequestBodyRequired || swaggerRequestBodyRequired)))
requestBody.setRequired(true);
@@ -685,26 +745,6 @@ public GenericParameterService getParameterBuilder() {
return parameterBuilder;
}
- /**
- * Calculate size.
- *
- * @param annos the annos
- * @param schema the schema
- */
- private void calculateSize(Map annos, Schema> schema) {
- if (annos.containsKey(Size.class.getSimpleName())) {
- Size size = (Size) annos.get(Size.class.getSimpleName());
- if (OPENAPI_ARRAY_TYPE.equals(schema.getType())) {
- schema.setMinItems(size.min());
- schema.setMaxItems(size.max());
- }
- else if (OPENAPI_STRING_TYPE.equals(schema.getType())) {
- schema.setMinLength(size.min());
- schema.setMaxLength(size.max());
- }
- }
- }
-
/**
* Gets api parameters.
*
@@ -732,46 +772,6 @@ private Map getApiParamete
return apiParametersMap;
}
- /**
- * Apply validations to schema.
- *
- * @param annos the annos
- * @param schema the schema
- */
- private void applyValidationsToSchema(Map annos, Schema> schema) {
- if (annos.containsKey(Min.class.getSimpleName())) {
- Min min = (Min) annos.get(Min.class.getSimpleName());
- schema.setMinimum(BigDecimal.valueOf(min.value()));
- }
- if (annos.containsKey(Max.class.getSimpleName())) {
- Max max = (Max) annos.get(Max.class.getSimpleName());
- schema.setMaximum(BigDecimal.valueOf(max.value()));
- }
- calculateSize(annos, schema);
- if (annos.containsKey(DecimalMin.class.getSimpleName())) {
- DecimalMin min = (DecimalMin) annos.get(DecimalMin.class.getSimpleName());
- if (min.inclusive())
- schema.setMinimum(BigDecimal.valueOf(Double.parseDouble(min.value())));
- else
- schema.setExclusiveMinimum(true);
- }
- if (annos.containsKey(DecimalMax.class.getSimpleName())) {
- DecimalMax max = (DecimalMax) annos.get(DecimalMax.class.getSimpleName());
- if (max.inclusive())
- schema.setMaximum(BigDecimal.valueOf(Double.parseDouble(max.value())));
- else
- schema.setExclusiveMaximum(true);
- }
- if (annos.containsKey(POSITIVE_OR_ZERO))
- schema.setMinimum(BigDecimal.ZERO);
- if (annos.containsKey(NEGATIVE_OR_ZERO))
- schema.setMaximum(BigDecimal.ZERO);
- if (annos.containsKey(Pattern.class.getSimpleName())) {
- Pattern pattern = (Pattern) annos.get(Pattern.class.getSimpleName());
- schema.setPattern(pattern.regexp());
- }
- }
-
/**
* Is RequestBody param boolean.
*
@@ -851,13 +851,20 @@ else if (requestBody.content().length > 0)
}
/**
- * Check if the parameter has any of the annotations that make it non-optional
+ * Gets optional delegating method parameter customizers.
*
- * @param annotationSimpleNames the annotation simple class named, e.g. NotNull
- * @return whether any of the known NotNull annotations are present
+ * @return the optional delegating method parameter customizers
*/
- public static boolean hasNotNullAnnotation(Collection annotationSimpleNames) {
- return Arrays.stream(ANNOTATIONS_FOR_REQUIRED).anyMatch(annotationSimpleNames::contains);
+ public Optional> getOptionalDelegatingMethodParameterCustomizers() {
+ return optionalDelegatingMethodParameterCustomizers;
}
+ /**
+ * Gets method parameter pojo extractor.
+ *
+ * @return the method parameter pojo extractor
+ */
+ public MethodParameterPojoExtractor getMethodParameterPojoExtractor() {
+ return methodParameterPojoExtractor;
+ }
}
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 f29c91198..fb2ed821a 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
@@ -35,7 +35,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -62,7 +62,6 @@
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
import org.springdoc.core.extractor.DelegatingMethodParameter;
import org.springdoc.core.extractor.MethodParameterPojoExtractor;
import org.springdoc.core.models.ParameterInfo;
@@ -115,11 +114,6 @@ public class GenericParameterService {
FILE_TYPES.add(MultipartRequest.class);
}
- /**
- * The Optional delegating method parameter customizer.
- */
- private final Optional> optionalDelegatingMethodParameterCustomizers;
-
/**
* The Web conversion service.
*/
@@ -154,15 +148,13 @@ public class GenericParameterService {
* Instantiates a new Generic parameter builder.
*
* @param propertyResolverUtils the property resolver utils
- * @param optionalDelegatingMethodParameterCustomizers the optional list delegating method parameter customizer
* @param optionalWebConversionServiceProvider the optional web conversion service provider
* @param objectMapperProvider the object mapper provider
* @param javadocProviderOptional the javadoc provider
*/
- public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional> optionalDelegatingMethodParameterCustomizers,
+ public GenericParameterService(PropertyResolverUtils propertyResolverUtils,
Optional optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, Optional javadocProviderOptional) {
this.propertyResolverUtils = propertyResolverUtils;
- this.optionalDelegatingMethodParameterCustomizers = optionalDelegatingMethodParameterCustomizers;
this.optionalWebConversionServiceProvider = optionalWebConversionServiceProvider;
this.configurableBeanFactory = propertyResolverUtils.getFactory();
this.expressionContext = (configurableBeanFactory != null ? new BeanExpressionContext(configurableBeanFactory, new RequestScope()) : null);
@@ -329,7 +321,7 @@ private void setSchema(io.swagger.v3.oas.annotations.Parameter parameterDoc, Com
else {
Schema schema = null;
try {
- if(StringUtils.isNotEmpty(parameterDoc.schema().type()) || !Void.class.equals(parameterDoc.schema().implementation())){
+ if (StringUtils.isNotEmpty(parameterDoc.schema().type()) || !Void.class.equals(parameterDoc.schema().implementation())) {
schema = AnnotationsUtils.getSchema(parameterDoc.schema(), null, false, parameterDoc.schema().implementation(), components, jsonView, propertyResolverUtils.isOpenapi31()).orElse(null);
}
// Cast default value
@@ -374,7 +366,7 @@ Schema calculateSchema(Components components, ParameterInfo parameterInfo, Reque
MethodParameter methodParameter = parameterInfo.getMethodParameter();
if (parameterInfo.getParameterModel() == null || parameterInfo.getParameterModel().getSchema() == null) {
- Type type = GenericTypeResolver.resolveType( methodParameter.getGenericParameterType(), methodParameter.getContainingClass());
+ Type type = GenericTypeResolver.resolveType(methodParameter.getGenericParameterType(), methodParameter.getContainingClass());
if (type instanceof Class && !((Class>) type).isEnum() && optionalWebConversionServiceProvider.isPresent()) {
WebConversionServiceProvider webConversionServiceProvider = optionalWebConversionServiceProvider.get();
if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class) == null) {
@@ -432,7 +424,7 @@ private Schema calculateRequestBodySchema(Components components, ParameterInfo p
requestBodyInfo.getMergedSchema().addProperty(paramName, schemaN);
schemaN = requestBodyInfo.getMergedSchema();
}
- else if (parameterInfo.isRequestPart() || schemaN instanceof FileSchema || (schemaN!=null && schemaN.getItems() instanceof FileSchema)) {
+ else if (parameterInfo.isRequestPart() || ParameterIn.QUERY.toString().equals(parameterInfo.getParamType()) || schemaN instanceof FileSchema || (schemaN != null && schemaN.getItems() instanceof FileSchema)) {
schemaN = new ObjectSchema().addProperty(paramName, schemaN);
requestBodyInfo.setMergedSchema(schemaN);
}
@@ -452,7 +444,7 @@ else if (parameterInfo.isRequestPart() || schemaN instanceof FileSchema || (sche
* @param parameter the parameter
*/
private void setExamples(io.swagger.v3.oas.annotations.Parameter parameterDoc, Parameter parameter) {
- Map exampleMap = new HashMap<>();
+ Map exampleMap = new LinkedHashMap<>();
if (parameterDoc.examples().length == 1 && StringUtils.isBlank(parameterDoc.examples()[0].name())) {
Optional exampleOptional = AnnotationsUtils.getExample(parameterDoc.examples()[0]);
exampleOptional.ifPresent(parameter::setExample);
@@ -552,15 +544,6 @@ public boolean isFile(MethodParameter methodParameter) {
}
}
- /**
- * Gets optional delegating method parameter customizers.
- *
- * @return the optional delegating method parameter customizers
- */
- public Optional> getOptionalDelegatingMethodParameterCustomizers() {
- return optionalDelegatingMethodParameterCustomizers;
- }
-
/**
* Is file boolean.
*
@@ -715,6 +698,11 @@ public Extension[] extensions() {
public String ref() {
return schema.ref();
}
+
+ @Override
+ public Class>[] validationGroups() {
+ return new Class[0];
+ }
};
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java
index f26f4a90b..2a0542872 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
@@ -40,6 +40,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -50,8 +51,6 @@
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonView;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.Operation;
@@ -72,6 +71,7 @@
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
+import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -80,6 +80,8 @@
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ProblemDetail;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -95,6 +97,7 @@
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.extractSchema;
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.getContent;
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.mergeSchema;
+import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
import static org.springdoc.core.utils.SpringDocUtils.getParameterAnnotations;
/**
@@ -110,6 +113,11 @@ public class GenericResponseService implements ApplicationContextAware {
*/
private static final String EXTENSION_EXCEPTION_CLASSES = "x-exception-class";
+ /**
+ * The constant LOGGER.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(GenericResponseService.class);
+
/**
* The Response entity exception handler class.
*/
@@ -145,11 +153,6 @@ public class GenericResponseService implements ApplicationContextAware {
*/
private final Lock reentrantLock = new ReentrantLock();
- /**
- * The constant LOGGER.
- */
- private static final Logger LOGGER = LoggerFactory.getLogger(GenericResponseService.class);
-
/**
* A list of all beans annotated with {@code @ControllerAdvice}
*/
@@ -261,7 +264,7 @@ public ApiResponses build(Components components, HandlerMethod handlerMethod, Op
apiResponsesFromDoc.forEach(apiResponses::addApiResponse);
// for each one build ApiResponse and add it to existing responses
// Fill api Responses
- computeResponseFromDoc(components, handlerMethod.getReturnType(), apiResponses, methodAttributes, springDocConfigProperties.isOpenapi31(),methodAttributes.getLocale() );
+ computeResponseFromDoc(components, handlerMethod.getReturnType(), apiResponses, methodAttributes, springDocConfigProperties.isOpenapi31(), methodAttributes.getLocale());
buildApiResponses(components, handlerMethod.getReturnType(), apiResponses, methodAttributes);
return apiResponses;
}
@@ -277,7 +280,7 @@ public ApiResponses build(Components components, HandlerMethod handlerMethod, Op
private Map filterAndEnrichGenericMapResponseByDeclarations(HandlerMethod handlerMethod, Map genericMapResponse) {
if (operationService.getJavadocProvider() != null) {
JavadocProvider javadocProvider = operationService.getJavadocProvider();
- for (Map.Entry genericResponse : genericMapResponse.entrySet()) {
+ for (Entry genericResponse : genericMapResponse.entrySet()) {
Map extensions = genericResponse.getValue().getExtensions();
Collection genericExceptions = (Collection) extensions.get(EXTENSION_EXCEPTION_CLASSES);
for (Class> declaredException : handlerMethod.getMethod().getExceptionTypes()) {
@@ -305,13 +308,13 @@ private Map filterAndEnrichGenericMapResponseByDeclarations
*/
public void buildGenericResponse(Components components, Map findControllerAdvice, Locale locale) {
// ControllerAdvice
- for (Map.Entry entry : findControllerAdvice.entrySet()) {
+ for (Entry entry : findControllerAdvice.entrySet()) {
List methods = new ArrayList<>();
Object controllerAdvice = entry.getValue();
// get all methods with annotation @ExceptionHandler
Class> objClz = controllerAdvice.getClass();
- if (org.springframework.aop.support.AopUtils.isAopProxy(controllerAdvice))
- objClz = org.springframework.aop.support.AopUtils.getTargetClass(controllerAdvice);
+ if (AopUtils.isAopProxy(controllerAdvice))
+ objClz = AopUtils.getTargetClass(controllerAdvice);
ControllerAdviceInfo controllerAdviceInfo = new ControllerAdviceInfo(controllerAdvice);
Arrays.stream(ReflectionUtils.getAllDeclaredMethods(objClz))
.filter(m -> m.isAnnotationPresent(ExceptionHandler.class)
@@ -404,7 +407,7 @@ private Map computeResponseFromDoc(Components components, M
apiResponse.extensions(extensions);
}
}
- AnnotationsUtils.getHeaders(apiResponseAnnotations.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31)
+ SpringDocAnnotationsUtils.getHeaders(apiResponseAnnotations.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31)
.ifPresent(apiResponse::headers);
apiResponsesOp.addApiResponse(httpCode, apiResponse);
}
@@ -422,11 +425,12 @@ private Map computeResponseFromDoc(Components components, M
*/
private void buildGenericApiResponses(Components components, MethodParameter methodParameter, ApiResponses apiResponsesOp,
MethodAttributes methodAttributes) {
+ ApiResponse apiResponse = null;
if (!CollectionUtils.isEmpty(apiResponsesOp)) {
// API Responses at operation and @ApiResponse annotation
- for (Map.Entry entry : apiResponsesOp.entrySet()) {
+ for (Entry entry : apiResponsesOp.entrySet()) {
String httpCode = entry.getKey();
- ApiResponse apiResponse = entry.getValue();
+ apiResponse = entry.getValue();
buildApiResponses(components, methodParameter, apiResponsesOp, methodAttributes, httpCode, apiResponse, true);
}
}
@@ -435,11 +439,22 @@ private void buildGenericApiResponses(Components components, MethodParameter met
// available
String httpCode = evaluateResponseStatus(methodParameter.getMethod(), Objects.requireNonNull(methodParameter.getMethod()).getClass(), true);
if (Objects.nonNull(httpCode)) {
- ApiResponse apiResponse = methodAttributes.getGenericMapResponse().containsKey(httpCode) ? methodAttributes.getGenericMapResponse().get(httpCode)
+ apiResponse = methodAttributes.getGenericMapResponse().containsKey(httpCode) ? methodAttributes.getGenericMapResponse().get(httpCode)
: new ApiResponse();
buildApiResponses(components, methodParameter, apiResponsesOp, methodAttributes, httpCode, apiResponse, true);
}
}
+ if (apiResponse != null) {
+ Content content = apiResponse.getContent();
+ if (content != null) {
+ String defaultProducesMediaType = springDocConfigProperties.getDefaultProducesMediaType();
+ io.swagger.v3.oas.models.media.MediaType mediaType = content.get(defaultProducesMediaType);
+ if (mediaType != null && ProblemDetail.class.isAssignableFrom(methodParameter.getParameterType())) {
+ content.addMediaType(MediaType.APPLICATION_PROBLEM_JSON_VALUE, mediaType);
+ content.remove(defaultProducesMediaType);
+ }
+ }
+ }
}
/**
@@ -455,7 +470,7 @@ private void buildApiResponses(Components components, MethodParameter methodPara
Map genericMapResponse = methodAttributes.getGenericMapResponse();
if (!CollectionUtils.isEmpty(apiResponsesOp) && apiResponsesOp.size() > genericMapResponse.size()) {
// API Responses at operation and @ApiResponse annotation
- for (Map.Entry entry : apiResponsesOp.entrySet()) {
+ for (Entry entry : apiResponsesOp.entrySet()) {
String httpCode = entry.getKey();
boolean methodAttributesCondition = !methodAttributes.isMethodOverloaded() || (methodAttributes.isMethodOverloaded() && isValidHttpCode(httpCode, methodParameter));
if (!genericMapResponse.containsKey(httpCode) && methodAttributesCondition) {
@@ -608,10 +623,10 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
}
}
if (apiResponse.getContent() != null && (methodAttributes.isUseReturnTypeSchema() ||
- ((isGeneric || methodAttributes.isMethodOverloaded()) && methodAttributes.isNoApiResponseDoc()))) {
+ ((isGeneric || methodAttributes.isMethodOverloaded()) && methodAttributes.isNoApiResponseDoc()))) {
// Merge with existing schema
Content existingContent = apiResponse.getContent();
- Type type = GenericTypeResolver.resolveType( methodParameter.getGenericParameterType(), methodParameter.getContainingClass());
+ Type type = GenericTypeResolver.resolveType(methodParameter.getGenericParameterType(), methodParameter.getContainingClass());
Schema> schemaN = calculateSchema(components, type,
methodAttributes.getJsonViewAnnotation(), getParameterAnnotations(methodParameter));
if (schemaN != null && ArrayUtils.isNotEmpty(methodAttributes.getMethodProduces()))
@@ -675,7 +690,7 @@ else if (returnType instanceof ParameterizedType parameterizedType) {
*/
private ControllerAdviceBean getControllerAdviceBean(List controllerAdviceBeans, Object controllerAdvice) {
return controllerAdviceBeans.stream()
- .filter(controllerAdviceBean -> (controllerAdviceBean.getBeanType()!=null && controllerAdviceBean.getBeanType().isAssignableFrom(controllerAdvice.getClass())))
+ .filter(controllerAdviceBean -> (controllerAdviceBean.getBeanType() != null && controllerAdviceBean.getBeanType().isAssignableFrom(controllerAdvice.getClass())))
.findFirst()
.orElse(null);
}
@@ -693,8 +708,8 @@ private Map getGenericMapResponse(HandlerMethod handlerMeth
List controllerAdviceInfosInThisBean = localExceptionHandlers.stream()
.filter(controllerInfo -> {
Class> objClz = controllerInfo.getControllerAdvice().getClass();
- if (org.springframework.aop.support.AopUtils.isAopProxy(controllerInfo.getControllerAdvice()))
- objClz = org.springframework.aop.support.AopUtils.getTargetClass(controllerInfo.getControllerAdvice());
+ if (AopUtils.isAopProxy(controllerInfo.getControllerAdvice()))
+ objClz = AopUtils.getTargetClass(controllerInfo.getControllerAdvice());
return beanType.equals(objClz);
})
.toList();
@@ -704,9 +719,9 @@ private Map getGenericMapResponse(HandlerMethod handlerMeth
.collect(LinkedHashMap::new, Map::putAll, Map::putAll);
List controllerAdviceInfosNotInThisBean = controllerAdviceInfos.stream()
- .filter(controllerAdviceInfo ->
+ .filter(controllerAdviceInfo ->
getControllerAdviceBean(controllerAdviceBeans, controllerAdviceInfo.getControllerAdvice())
- .isApplicableToBeanType(beanType))
+ .isApplicableToBeanType(beanType))
.filter(controllerAdviceInfo -> !beanType.equals(controllerAdviceInfo.getControllerAdvice().getClass()))
.toList();
@@ -737,17 +752,7 @@ private Map getGenericMapResponse(HandlerMethod handlerMeth
}
}
}
-
- LinkedHashMap genericApiResponsesClone;
- try {
- ObjectMapper objectMapper = ObjectMapperProvider.createJson(springDocConfigProperties);
- genericApiResponsesClone = objectMapper.readValue(objectMapper.writeValueAsString(genericApiResponseMap), ApiResponses.class);
- return genericApiResponsesClone;
- }
- catch (JsonProcessingException e) {
- LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
- return genericApiResponseMap;
- }
+ return cloneViaJson(genericApiResponseMap, ApiResponses.class, ObjectMapperProvider.createJson(springDocConfigProperties));
}
finally {
reentrantLock.unlock();
@@ -772,7 +777,7 @@ private boolean isValidHttpCode(String httpCode, MethodParameter methodParameter
final io.swagger.v3.oas.annotations.Operation apiOperation = AnnotatedElementUtils.findMergedAnnotation(method,
io.swagger.v3.oas.annotations.Operation.class);
if (apiOperation != null) {
- responseSet = new HashSet<>(Arrays.asList(apiOperation.responses()));
+ responseSet = new HashSet<>(asList(apiOperation.responses()));
if (isHttpCodePresent(httpCode, responseSet))
result = true;
}
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 1d382c7db..2b7504e69 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
@@ -21,11 +21,13 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -45,12 +47,12 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.Webhook;
import io.swagger.v3.oas.annotations.Webhooks;
import io.swagger.v3.oas.annotations.security.SecuritySchemes;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -95,12 +97,14 @@
import static org.springdoc.core.utils.Constants.DEFAULT_SERVER_DESCRIPTION;
import static org.springdoc.core.utils.Constants.DEFAULT_TITLE;
import static org.springdoc.core.utils.Constants.DEFAULT_VERSION;
+import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
import static org.springdoc.core.utils.SpringDocUtils.getConfig;
/**
* The type Open api builder.
*
* @author bnasslahsen
+ * @author zdary
*/
public class OpenAPIService implements ApplicationContextAware {
@@ -174,11 +178,6 @@ public class OpenAPIService implements ApplicationContextAware {
*/
private boolean isServersPresent;
- /**
- * The Server base url.
- */
- private String serverBaseUrl;
-
/**
* Instantiates a new Open api builder.
*
@@ -247,14 +246,7 @@ public OpenAPI build(Locale locale) {
calculatedOpenAPI.setPaths(new Paths());
}
else {
- try {
- ObjectMapper objectMapper = new ObjectMapper();
- calculatedOpenAPI = objectMapper.readValue(objectMapper.writeValueAsString(openAPI), OpenAPI.class);
- }
- catch (JsonProcessingException e) {
- LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
- calculatedOpenAPI = openAPI;
- }
+ calculatedOpenAPI = cloneViaJson(openAPI, OpenAPI.class, new ObjectMapper());
}
if (apiDef.isPresent()) {
@@ -296,9 +288,10 @@ private void initializeHiddenRestController() {
/**
* Update servers open api.
*
- * @param openAPI the open api
+ * @param serverBaseUrl the server base url
+ * @param openAPI the open api
*/
- public void updateServers(OpenAPI openAPI) {
+ public void updateServers(String serverBaseUrl, OpenAPI openAPI) {
if (!isServersPresent && serverBaseUrl != null) // default server value
{
Server server = new Server().url(serverBaseUrl).description(DEFAULT_SERVER_DESCRIPTION);
@@ -477,7 +470,7 @@ 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().getItems() !=null ) {
+ if (es.getValue().getItems() != null) {
resolveProperties(es.getValue().getItems(), locale);
}
return es;
@@ -493,18 +486,17 @@ public Schema resolveProperties(Schema schema, Locale locale) {
*
* @param serverBaseUrl the server base url
* @param httpRequest the http request
+ * @return the string
*/
- public void setServerBaseUrl(String serverBaseUrl, HttpRequest httpRequest) {
+ public String calculateServerBaseUrl(String serverBaseUrl, HttpRequest httpRequest) {
String customServerBaseUrl = serverBaseUrl;
-
if (serverBaseUrlCustomizers.isPresent()) {
for (ServerBaseUrlCustomizer customizer : serverBaseUrlCustomizers.get()) {
customServerBaseUrl = customizer.customize(customServerBaseUrl, httpRequest);
}
}
- this.serverBaseUrl = customServerBaseUrl;
-
+ return customServerBaseUrl;
}
/**
@@ -539,64 +531,105 @@ private Optional getOpenAPIDefinition() {
return Optional.ofNullable(apiDef);
}
+
/**
- * Get webhooks webhooks [ ].
+ * Gets webhooks from given classes.
*
- * @return the webhooks [ ]
+ * @param classes Array of classes to scan for webhooks.
+ * @return An array of {@link Webhooks} annotations found in the given classes.
*/
- public Webhooks[] getWebhooks() {
- // List to collect all Webhooks annotations
+ public Webhooks[] getWebhooks(Class>[] classes) {
List allWebhooks = new ArrayList<>();
- // Get beans with @Webhooks annotation managed by Spring
- Map beansWithWebhooksAnnotation = context.getBeansWithAnnotation(Webhooks.class);
+ for (Class> clazz : classes) {
+ // Class-level annotations
+ collectWebhooksFromElement(clazz, allWebhooks);
- // Process Spring-managed beans
- if (!beansWithWebhooksAnnotation.isEmpty()) {
- beansWithWebhooksAnnotation.values().forEach(controller -> {
- // Get the @Webhooks annotation(s) from each bean
- Webhooks[] webhooksAnnotations = controller.getClass().getAnnotationsByType(Webhooks.class);
- allWebhooks.addAll(Arrays.asList(webhooksAnnotations));
- });
+ // Method-level annotations
+ for (Method method : clazz.getDeclaredMethods()) {
+ collectWebhooksFromElement(method, allWebhooks);
+ }
}
- // If no beans with @Webhooks annotation found, perform classpath scanning
- if (allWebhooks.isEmpty()) {
- ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
- scanner.addIncludeFilter(new AnnotationTypeFilter(Webhooks.class));
+ return allWebhooks.toArray(new Webhooks[0]);
+ }
- // Scan base packages if available
- if (AutoConfigurationPackages.has(context)) {
- List packagesToScan = AutoConfigurationPackages.get(context);
- for (String basePackage : packagesToScan) {
- // Perform the scan and get candidate components
- Set components = scanner.findCandidateComponents(basePackage);
-
- // Loop through the components
- for (BeanDefinition beanDefinition : components) {
- try {
- // Get the class name and load the class
- String className = beanDefinition.getBeanClassName();
- Class> clazz = Class.forName(className);
-
- // Get @Webhooks annotation from the class
- Webhooks[] webhooksAnnotations = clazz.getAnnotationsByType(Webhooks.class);
- allWebhooks.addAll(Arrays.asList(webhooksAnnotations));
-
- } catch (ClassNotFoundException e) {
- // Log the error if the class is not found
- LOGGER.error("Class not found in classpath: {}", e.getMessage());
- }
+ /**
+ * Retrieves all classes related to webhooks.
+ * This method scans for classes annotated with {@link Webhooks} or {@link Webhook},
+ * first checking Spring-managed beans and then falling back to classpath scanning
+ * if no annotated beans are found.
+ *
+ * @return An array of classes related to webhooks.
+ */
+ public Class>[] getWebhooksClasses() {
+ Set> allWebhookClassesToScan = new HashSet<>();
+
+ // First: scan Spring-managed beans
+ Map beans = context.getBeansWithAnnotation(Webhooks.class);
+
+ for (Object bean : beans.values()) {
+ Class> beanClass = bean.getClass();
+ allWebhookClassesToScan.add(beanClass);
+ }
+
+ // Fallback: classpath scanning
+ ClassPathScanningCandidateComponentProvider scanner =
+ new ClassPathScanningCandidateComponentProvider(false);
+ scanner.addIncludeFilter(new AnnotationTypeFilter(Webhooks.class));
+ scanner.addIncludeFilter(new AnnotationTypeFilter(Webhook.class));
+
+ if (AutoConfigurationPackages.has(context)) {
+ for (String basePackage : AutoConfigurationPackages.get(context)) {
+ Set candidates = scanner.findCandidateComponents(basePackage);
+ for (BeanDefinition bd : candidates) {
+ try {
+ Class> clazz = Class.forName(bd.getBeanClassName());
+ allWebhookClassesToScan.add(clazz);
+ }
+ catch (ClassNotFoundException e) {
+ LOGGER.error("Class not found in classpath: {}", e.getMessage());
}
}
}
}
- // Convert the list of Webhooks annotations to an array and return
- return allWebhooks.toArray(new Webhooks[0]);
+ return allWebhookClassesToScan.toArray(new Class>[0]);
+ }
+
+
+ /**
+ * Collect webhooks from element.
+ *
+ * @param element the element
+ * @param collector the collector
+ */
+ private void collectWebhooksFromElement(AnnotatedElement element, List collector) {
+ // If @Webhooks is present (container)
+ Webhooks container = element.getAnnotation(Webhooks.class);
+ if (container != null) {
+ collector.add(container);
+ }
+
+ // If individual @Webhook annotations are present
+ Webhook[] individualWebhooks = element.getAnnotationsByType(Webhook.class);
+ if (individualWebhooks.length > 0) {
+ collector.add(new Webhooks() {
+ @Override
+ public Webhook[] value() {
+ return individualWebhooks;
+ }
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return Webhooks.class;
+ }
+ });
+ }
}
+
/**
* Build open api with open api definition.
*
@@ -606,9 +639,11 @@ public Webhooks[] getWebhooks() {
*/
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(),true).map(info -> resolveProperties(info, extensions, locale)).ifPresent(openAPI::setInfo);
+ AnnotationsUtils.getInfo(apiDef.info(), true).map(info -> {
+ Map extensions = AnnotationsUtils.getExtensions(isOpenapi3, apiDef.info().extensions());
+ return resolveProperties(info, extensions, locale);
+ }).ifPresent(openAPI::setInfo);
// OpenApiDefinition security requirements
securityParser.getSecurityRequirements(apiDef.security()).ifPresent(openAPI::setSecurity);
// OpenApiDefinition external docs
@@ -672,10 +707,12 @@ private Info resolveProperties(Info info, Map extensions, Locale
resolveProperty(contact::getUrl, contact::url, propertyResolverUtils, locale);
}
- if (propertyResolverUtils.isResolveExtensionsProperties() && extensions != null) {
+ if (extensions != null) {
Map extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
- if(propertyResolverUtils.isOpenapi31())
+ if (propertyResolverUtils.isOpenapi31()) {
extensionsResolved.forEach(info::addExtension31);
+ info.setExtensions(extensionsResolved);
+ }
else
info.setExtensions(extensionsResolved);
}
@@ -683,7 +720,6 @@ private Info resolveProperties(Info info, Map extensions, Locale
return info;
}
-
/**
* Resolve property.
*
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java
index f4728a818..ebca547e6 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
@@ -337,7 +337,7 @@ private Optional getApiResponses(
buildResponseContent(methodAttributes, components, classProduces, methodProduces, apiResponsesOp, response, apiResponseObject);
- AnnotationsUtils.getHeaders(response.headers(), components, null, propertyResolverUtils.isOpenapi31()).ifPresent(apiResponseObject::headers);
+ SpringDocAnnotationsUtils.getHeaders(response.headers(), components, null, propertyResolverUtils.isOpenapi31()).ifPresent(apiResponseObject::headers);
// Make schema as string if empty
calculateHeader(apiResponseObject);
if (isResponseObject(apiResponseObject)) {
@@ -562,7 +562,7 @@ public JavadocProvider getJavadocProvider() {
*
* @return propertyResolverUtils property resolver utils
*/
- public PropertyResolverUtils getPropertyResolverUtils(){
+ public PropertyResolverUtils getPropertyResolverUtils() {
return parameterBuilder.getPropertyResolverUtils();
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/RequestBodyService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/RequestBodyService.java
index ee91bc01b..0f48e5f8e 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/RequestBodyService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/RequestBodyService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
@@ -35,9 +35,11 @@
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.media.Content;
+import io.swagger.v3.oas.models.media.Encoding;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.RequestBody;
+import jakarta.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.models.MethodAttributes;
import org.springdoc.core.models.ParameterInfo;
@@ -46,6 +48,7 @@
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
import org.springframework.core.MethodParameter;
+import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestPart;
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.mergeSchema;
@@ -113,7 +116,7 @@ public Optional buildRequestBodyFromDoc(
}
if (requestBody.required()) {
- requestBodyObject.setRequired(requestBody.required());
+ requestBodyObject.setRequired(true);
isEmpty = false;
}
if (requestBody.extensions().length > 0) {
@@ -128,7 +131,7 @@ public Optional buildRequestBodyFromDoc(
if (isEmpty)
return Optional.empty();
- buildResquestBodyContent(requestBody, requestBodyOp, methodAttributes, components, jsonViewAnnotation, classConsumes, methodConsumes, requestBodyObject);
+ buildRequestBodyContent(requestBody, requestBodyOp, methodAttributes, components, jsonViewAnnotation, classConsumes, methodConsumes, requestBodyObject);
return Optional.of(requestBodyObject);
}
@@ -145,7 +148,10 @@ public Optional buildRequestBodyFromDoc(
* @param methodConsumes the method consumes
* @param requestBodyObject the request body object
*/
- private void buildResquestBodyContent(io.swagger.v3.oas.annotations.parameters.RequestBody requestBody, RequestBody requestBodyOp, MethodAttributes methodAttributes, Components components, JsonView jsonViewAnnotation, String[] classConsumes, String[] methodConsumes, RequestBody requestBodyObject) {
+ private void buildRequestBodyContent(io.swagger.v3.oas.annotations.parameters.RequestBody requestBody,
+ RequestBody requestBodyOp, MethodAttributes methodAttributes,
+ Components components, JsonView jsonViewAnnotation, String[] classConsumes,
+ String[] methodConsumes, RequestBody requestBodyObject) {
Optional optionalContent = SpringDocAnnotationsUtils
.getContent(requestBody.content(), getConsumes(classConsumes),
getConsumes(methodConsumes), null, components, jsonViewAnnotation, parameterBuilder.isOpenapi31());
@@ -251,11 +257,7 @@ public void calculateRequestBodyInfo(Components components, MethodAttributes met
ParameterInfo parameterInfo, RequestBodyInfo requestBodyInfo) {
RequestBody requestBody = requestBodyInfo.getRequestBody();
MethodParameter methodParameter = parameterInfo.getMethodParameter();
- // Get it from parameter level, if not present
- if (requestBody == null) {
- io.swagger.v3.oas.annotations.parameters.RequestBody requestBodyDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class);
- requestBody = this.buildRequestBodyFromDoc(requestBodyDoc, methodAttributes, components).orElse(null);
- }
+
RequestPart requestPart = methodParameter.getParameterAnnotation(RequestPart.class);
String paramName = null;
@@ -267,7 +269,8 @@ public void calculateRequestBodyInfo(Components components, MethodAttributes met
paramName = StringUtils.defaultIfEmpty(paramName, parameterInfo.getpName());
parameterInfo.setpName(paramName);
- requestBody = buildRequestBody(requestBody, components, methodAttributes, parameterInfo,
+ io.swagger.v3.oas.annotations.parameters.RequestBody requestBodyDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class);
+ requestBody = buildRequestBody(requestBodyDoc, components, methodAttributes, parameterInfo,
requestBodyInfo);
requestBodyInfo.setRequestBody(requestBody);
}
@@ -275,31 +278,29 @@ public void calculateRequestBodyInfo(Components components, MethodAttributes met
/**
* Build request body.
*
- * @param requestBody the request body
+ * @param requestBodyDoc the request body doc
* @param components the components
* @param methodAttributes the method attributes
* @param parameterInfo the parameter info
* @param requestBodyInfo the request body info
* @return the request body
*/
- private RequestBody buildRequestBody(RequestBody requestBody, Components components,
+ private RequestBody buildRequestBody(io.swagger.v3.oas.annotations.parameters.RequestBody requestBodyDoc, Components components,
MethodAttributes methodAttributes,
ParameterInfo parameterInfo, RequestBodyInfo requestBodyInfo) {
+ RequestBody requestBody = requestBodyInfo.getRequestBody();
if (requestBody == null) {
requestBody = new RequestBody();
requestBodyInfo.setRequestBody(requestBody);
}
- if (requestBody.getContent() == null) {
- Schema> schema = parameterBuilder.calculateSchema(components, parameterInfo, requestBodyInfo,
- methodAttributes.getJsonViewAnnotationForRequestBody());
- buildContent(requestBody, methodAttributes, schema);
- }
- else if (!methodAttributes.isWithResponseBodySchemaDoc()) {
- Schema> schema = parameterBuilder.calculateSchema(components, parameterInfo, requestBodyInfo,
- methodAttributes.getJsonViewAnnotationForRequestBody());
- mergeContent(requestBody, methodAttributes, schema);
- }
+ if (requestBodyDoc != null)
+ requestBody = this.buildRequestBodyFromDoc(requestBodyDoc, methodAttributes, components).orElse(requestBody);
+
+ Schema> schema = parameterBuilder.calculateSchema(components, parameterInfo, requestBodyInfo,
+ methodAttributes.getJsonViewAnnotationForRequestBody());
+ Map parameterEncoding = getParameterEncoding(parameterInfo);
+ buildContent(requestBody, methodAttributes, schema, parameterEncoding);
// Add requestBody javadoc
if (StringUtils.isBlank(requestBody.getDescription()) && parameterBuilder.getJavadocProvider() != null
@@ -312,43 +313,27 @@ else if (!methodAttributes.isWithResponseBodySchemaDoc()) {
return requestBody;
}
- /**
- * Merge content.
- *
- * @param requestBody the request body
- * @param methodAttributes the method attributes
- * @param schema the schema
- */
- private void mergeContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema> schema) {
- Content content = requestBody.getContent();
- buildContent(requestBody, methodAttributes, schema, content);
- }
-
/**
* Build content.
*
- * @param requestBody the request body
- * @param methodAttributes the method attributes
- * @param schema the schema
+ * @param requestBody the request body
+ * @param methodAttributes the method attributes
+ * @param schema the schema
+ * @param parameterEncoding the parameter encoding
*/
- private void buildContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema> schema) {
- Content content = new Content();
- buildContent(requestBody, methodAttributes, schema, content);
- }
+ private void buildContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema> schema, Map parameterEncoding) {
+ Content content;
+ if (requestBody.getContent() == null) {
+ content = new Content();
+ }
+ else {
+ content = requestBody.getContent();
+ }
- /**
- * Build content.
- *
- * @param requestBody the request body
- * @param methodAttributes the method attributes
- * @param schema the schema
- * @param content the content
- */
- private void buildContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema> schema, Content content) {
for (String value : methodAttributes.getMethodConsumes()) {
- io.swagger.v3.oas.models.media.MediaType mediaTypeObject = new io.swagger.v3.oas.models.media.MediaType();
- mediaTypeObject.setSchema(schema);
+ MediaType mediaTypeObject = new MediaType();
MediaType mediaType = content.get(value);
+ mediaTypeObject.setSchema(schema);
if (mediaType != null) {
if (mediaType.getExample() != null)
mediaTypeObject.setExample(mediaType.getExample());
@@ -356,9 +341,40 @@ private void buildContent(RequestBody requestBody, MethodAttributes methodAttrib
mediaTypeObject.setExamples(mediaType.getExamples());
if (mediaType.getEncoding() != null)
mediaTypeObject.setEncoding(mediaType.getEncoding());
+ if (mediaType.getSchema() != null)
+ mediaTypeObject.setSchema(mediaType.getSchema());
+ }
+ else if (!CollectionUtils.isEmpty(parameterEncoding)) {
+ mediaTypeObject.setEncoding(parameterEncoding);
}
content.addMediaType(value, mediaTypeObject);
}
requestBody.setContent(content);
}
+
+ /**
+ * Gets parameter encoding.
+ *
+ * @param parameterInfo the parameter info
+ * @return the parameter encoding
+ */
+ @NotNull
+ private Map getParameterEncoding(ParameterInfo parameterInfo) {
+ if (parameterInfo.getParameterModel() != null) {
+ Content parameterContent = parameterInfo.getParameterModel().getContent();
+ if (parameterContent != null && parameterContent.size() == 1) {
+ Map encoding = parameterContent.values().iterator().next().getEncoding();
+ if (!CollectionUtils.isEmpty(encoding)) {
+ return encoding;
+ }
+ else {
+ String encodingContentType = parameterContent.keySet().iterator().next();
+ if (StringUtils.isNotBlank(encodingContentType)) {
+ return Map.of(parameterInfo.getpName(), new Encoding().contentType(encodingContentType));
+ }
+ }
+ }
+ }
+ return Map.of();
+ }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecuritySchemePair.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecuritySchemePair.java
index fa98e480c..7b2928281 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecuritySchemePair.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecuritySchemePair.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecurityService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecurityService.java
index d664bbfd7..a06260149 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecurityService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/SecurityService.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.service;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java
index 9453a832d..3e0b065ae 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.utils;
@@ -45,8 +45,8 @@ public final class Constants {
/**
* The constant SPRINGDOC_SWAGGER_PREFIX.
*/
- public static final String SPRINGDOC_SWAGGER_PREFIX =SPRINGDOC_PREFIX+".swagger-ui";
-
+ public static final String SPRINGDOC_SWAGGER_PREFIX = SPRINGDOC_PREFIX + ".swagger-ui";
+
/**
* The constant DEFAULT_API_DOCS_URL.
*/
@@ -424,6 +424,21 @@ public final class Constants {
*/
public static final String SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED = "springdoc.nullable-request-parameter-enabled";
+ /**
+ * The constant SPRINGDOC_DEFAULT_FLAT_PARAM_OBJECT.
+ */
+ public static final String SPRINGDOC_DEFAULT_FLAT_PARAM_OBJECT = "springdoc.default-flat-param-object";
+
+ /**
+ * The constant SPRINGDOC_ENABLE_ADDITIONAL_SCHEMAS_RESOLUTION.
+ */
+ public static final String SPRINGDOC_ENABLE_EXTRA_SCHEMAS = "springdoc.enable-extra-schemas";
+
+ /**
+ * The constant SPRINGDOC_EXPLICIT_OBJECT_SCHEMA.
+ */
+ public static final String SPRINGDOC_EXPLICIT_OBJECT_SCHEMA = "springdoc.explicit-object-schema";
+
/**
* Instantiates a new Constants.
*/
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/EntityInfo.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/EntityInfo.java
index 77b2fb58f..dfdf32622 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/EntityInfo.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/EntityInfo.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.utils;
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java
index a98d7a5e3..d8f3149d7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java
@@ -21,7 +21,7 @@
* * * *
* * *
* *
- *
+ *
*/
package org.springdoc.core.utils;
@@ -30,10 +30,9 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
-
-import io.swagger.v3.oas.models.SpecVersion;
import java.util.stream.Collectors;
+import io.swagger.v3.oas.models.SpecVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.properties.SpringDocConfigProperties;
@@ -117,9 +116,9 @@ public String resolve(String parameterProperty, Locale locale) {
* and removes it.
* If the input text is {@code null}, the method returns {@code null}.
*
- * @param text The original string with possible leading indentation.
- * @return The string with the smallest common leading indentation removed from each line,
- * or {@code null} if the input text is {@code null}.
+ * @param text The original string with possible leading indentation.
+ * @return The string with the smallest common leading indentation removed from each line,
+ * or {@code null} if the input text is {@code null}.
*/
public String trimIndent(String text) {
if (text == null) {
@@ -132,7 +131,8 @@ public String trimIndent(String text) {
return Arrays.stream(lines)
.map(line -> line.substring(Math.min(line.length(), minIndent)))
.collect(Collectors.joining(newLine));
- } catch (Exception ex) {
+ }
+ catch (Exception ex) {
LOGGER.warn(ex.getMessage());
return text;
}
@@ -146,10 +146,10 @@ public String trimIndent(String text) {
*/
private int resolveMinIndent(String[] lines) {
return Arrays.stream(lines)
- .filter(line -> !line.trim().isEmpty())
- .mapToInt(this::countLeadingSpaces)
- .min()
- .orElse(0);
+ .filter(line -> !line.trim().isEmpty())
+ .mapToInt(this::countLeadingSpaces)
+ .min()
+ .orElse(0);
}
/**
@@ -159,13 +159,13 @@ private int resolveMinIndent(String[] lines) {
* @return the int
*/
private int countLeadingSpaces(String line) {
- int count = 0;
- for (char ch : line.toCharArray()) {
- if (ch != ' ' && ch != '\t') break;
- count++;
- }
- return count;
- }
+ int count = 0;
+ for (char ch : line.toCharArray()) {
+ if (ch != ' ' && ch != '\t') break;
+ count++;
+ }
+ return count;
+ }
/**
* Gets factory.
@@ -224,21 +224,27 @@ public Map resolveExtensions(Locale locale, Map
Map extensionsResolved = new HashMap<>();
extensions.forEach((key, value) -> {
String keyResolved = resolve(key, locale);
- if (value instanceof HashMap, ?>) {
+ if (value instanceof HashMap, ?> valueAsMap) {
Map valueResolved = new HashMap<>();
- ((HashMap, ?>) value).forEach((key1, value1) -> {
+ valueAsMap.forEach((key1, value1) -> {
String key1Resolved = resolve(key1.toString(), locale);
String value1Resolved = resolve(value1.toString(), locale);
valueResolved.put(key1Resolved, value1Resolved);
});
extensionsResolved.put(keyResolved, valueResolved);
}
- else
+ else if (value instanceof String valueAsString) {
+ String valueResolved = resolve(valueAsString, locale);
+ extensionsResolved.put(keyResolved, valueResolved);
+ }
+ else {
extensionsResolved.put(keyResolved, value);
+ }
});
return extensionsResolved;
}
- else
+ else {
return extensions;
+ }
}
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java
new file mode 100644
index 000000000..70f641e32
--- /dev/null
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java
@@ -0,0 +1,501 @@
+package org.springdoc.core.utils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
+import io.swagger.v3.oas.models.media.Schema;
+import jakarta.validation.constraints.DecimalMax;
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.Negative;
+import jakarta.validation.constraints.NegativeOrZero;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import jakarta.validation.constraints.Size;
+import org.springdoc.core.properties.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
+
+import org.springframework.lang.Nullable;
+
+import static org.springdoc.core.utils.Constants.OPENAPI_ARRAY_TYPE;
+import static org.springdoc.core.utils.Constants.OPENAPI_STRING_TYPE;
+import static org.springdoc.core.utils.SpringDocKotlinUtils.isKotlinDeclaringClass;
+import static org.springdoc.core.utils.SpringDocKotlinUtils.kotlinConstructorParamIsOptional;
+import static org.springdoc.core.utils.SpringDocKotlinUtils.kotlinNullability;
+
+/**
+ * The type Validation utils.
+ *
+ * @author dyun
+ */
+public class SchemaUtils {
+
+ /**
+ * The constant JAVA_FIELD_NULLABLE_DEFAULT.
+ */
+ public static final Boolean JAVA_FIELD_NULLABLE_DEFAULT = true;
+
+ /**
+ * The constant ANNOTATIONS_FOR_REQUIRED.
+ */
+ // using string litterals to support both validation-api v1 and v2
+ public static final List ANNOTATIONS_FOR_REQUIRED = Arrays.asList("NotNull", "NonNull", "NotBlank",
+ "NotEmpty");
+
+ /**
+ * The constant ANNOTATIONS_FOR_NULLABLE.
+ */
+ public static final List ANNOTATIONS_FOR_NULLABLE =
+ Arrays.asList("Nullable");
+ /**
+ * The constant OPTIONAL_TYPES.
+ */
+ private static final Set> OPTIONAL_TYPES = new HashSet<>();
+
+ static {
+ OPTIONAL_TYPES.add(Optional.class);
+ OPTIONAL_TYPES.add(OptionalInt.class);
+ OPTIONAL_TYPES.add(OptionalLong.class);
+ OPTIONAL_TYPES.add(OptionalDouble.class);
+ }
+
+ /**
+ * The Kotlin utils optional.
+ */
+ private final Optional kotlinUtilsOptional;
+
+ /**
+ * The constructor.
+ *
+ * @param kotlinUtilsOptional the kotlin utils optional
+ */
+ public SchemaUtils(Optional kotlinUtilsOptional) {
+ this.kotlinUtilsOptional = kotlinUtilsOptional;
+ }
+
+ /**
+ * Is swagger visible.
+ *
+ * @param schema the schema
+ * @param parameter the parameter
+ * @return the boolean
+ */
+ public static boolean swaggerVisible(@Nullable io.swagger.v3.oas.annotations.media.Schema schema,
+ @Nullable Parameter parameter) {
+ if (parameter != null) {
+ return !parameter.hidden();
+ }
+ if (schema != null) {
+ return !schema.hidden();
+ }
+ return true;
+ }
+
+ /**
+ * Is swagger required. it may return {@code null} if not specified require value or
+ * mode
+ *
+ * @param schema the schema
+ * @param parameter the parameter
+ * @return the boolean or {@code null}
+ * @see Parameter#required()
+ * @see io.swagger.v3.oas.annotations.media.Schema#required()
+ * @see io.swagger.v3.oas.annotations.media.Schema#requiredMode()
+ */
+ @Nullable
+ public static Boolean swaggerRequired(@Nullable io.swagger.v3.oas.annotations.media.Schema schema,
+ @Nullable Parameter parameter) {
+ if (parameter != null && parameter.required()) {
+ return true;
+ }
+ if (schema != null) {
+ if (schema.required()
+ || schema.requiredMode() == RequiredMode.REQUIRED) {
+ return true;
+ }
+ if (schema.requiredMode() == RequiredMode.NOT_REQUIRED) {
+ return false;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Is annotated notnull.
+ *
+ * @param annotations the field annotations
+ * @return the boolean
+ */
+ public static boolean annotatedNotNull(List annotations) {
+ Collection annotationSimpleNames = annotations.stream()
+ .map(annotation -> annotation.annotationType().getSimpleName())
+ .collect(Collectors.toSet());
+ return ANNOTATIONS_FOR_REQUIRED.stream().anyMatch(annotationSimpleNames::contains);
+ }
+
+ /**
+ * Is field nullable. java default is nullable
+ * {@link SchemaUtils#JAVA_FIELD_NULLABLE_DEFAULT}
+ *
+ * @param field the field
+ * @return the boolean
+ */
+ public boolean fieldNullable(Field field) {
+ // primitives cannot be null
+ if (field.getType().isPrimitive()) return false;
+
+ // Optional-like wrappers
+ if (OPTIONAL_TYPES.stream().anyMatch(c -> c.isAssignableFrom(field.getType()))) {
+ return true;
+ }
+
+ // @Nullable/@NotNull
+ Boolean ann = nullableFromAnnotations(field);
+ if (ann != null) return ann;
+
+ // Kotlin nullability
+ if (kotlinUtilsOptional.isPresent() && isKotlinDeclaringClass(field)) {
+ Boolean kotlin = kotlinNullability(field);
+ if (kotlin != null) return kotlin;
+ }
+
+ return JAVA_FIELD_NULLABLE_DEFAULT;
+ }
+
+ /**
+ * Is field required.
+ *
+ * @param field the field
+ * @param schema the schema
+ * @param parameter the parameter
+ * @return the boolean
+ * @see Parameter#required()
+ * @see io.swagger.v3.oas.annotations.media.Schema#required()
+ * @see io.swagger.v3.oas.annotations.media.Schema#requiredMode()
+ */
+ public boolean fieldRequired(Field field, io.swagger.v3.oas.annotations.media.Schema schema, Parameter parameter) {
+ Boolean swagger = swaggerRequired(schema, parameter);
+ if (swagger != null) return swagger;
+
+ // Optional-like wrapper → not required
+ if (OPTIONAL_TYPES.stream().anyMatch(c -> c.isAssignableFrom(field.getType()))) return false;
+
+ // @Nullable/@NotNull
+ if (hasNullableAnnotation(field) || hasNullableOnGetter(field) || hasNullableOnCtorParam(field)) {
+ return false;
+ }
+ if (hasNotNullAnnotation(field) || hasNotNullOnGetter(field) || hasNotNullOnCtorParam(field)) {
+ return true;
+ }
+
+ // Kotlin logic
+ if (kotlinUtilsOptional.isPresent() && isKotlinDeclaringClass(field)) {
+ if (fieldNullable(field)) return false;
+ Boolean hasDefault = kotlinConstructorParamIsOptional(field);
+ if (Boolean.TRUE.equals(hasDefault)) return false;
+ return true;
+ }
+
+ // Jackson @JsonProperty(required = true)
+ JsonProperty jp = getJsonProperty(field);
+ if (jp != null && jp.required()) return true;
+
+ return false;
+ }
+
+ /**
+ * Apply validations to schema. the annotation order effects the result of the
+ * validation.
+ *
+ * @param schema the schema
+ * @param annotations the annotations
+ * @param openapiVersion the openapi version
+ */
+ public static void applyValidationsToSchema(Schema> schema, List annotations, String openapiVersion) {
+ annotations.forEach(anno -> {
+ String annotationName = anno.annotationType().getSimpleName();
+ if (annotationName.equals(Positive.class.getSimpleName())) {
+ if (OpenApiVersion.OPENAPI_3_1.getVersion().equals(openapiVersion)) {
+ schema.setExclusiveMinimumValue(BigDecimal.ZERO);
+ }
+ else {
+ schema.setMinimum(BigDecimal.ZERO);
+ schema.setExclusiveMinimum(true);
+ }
+ }
+ if (annotationName.equals(PositiveOrZero.class.getSimpleName())) {
+ schema.setMinimum(BigDecimal.ZERO);
+ }
+ if (annotationName.equals(NegativeOrZero.class.getSimpleName())) {
+ schema.setMaximum(BigDecimal.ZERO);
+ }
+ if (annotationName.equals(Negative.class.getSimpleName())) {
+ if (OpenApiVersion.OPENAPI_3_1.getVersion().equals(openapiVersion)) {
+ schema.setExclusiveMaximumValue(BigDecimal.ZERO);
+ }
+ else {
+ schema.setMaximum(BigDecimal.ZERO);
+ schema.setExclusiveMaximum(true);
+ }
+ }
+ if (annotationName.equals(Min.class.getSimpleName())) {
+ schema.setMinimum(BigDecimal.valueOf(((Min) anno).value()));
+ }
+ if (annotationName.equals(Max.class.getSimpleName())) {
+ schema.setMaximum(BigDecimal.valueOf(((Max) anno).value()));
+ }
+ if (annotationName.equals(DecimalMin.class.getSimpleName())) {
+ DecimalMin min = (DecimalMin) anno;
+ if (min.inclusive()) {
+ schema.setMinimum(BigDecimal.valueOf(Double.parseDouble(min.value())));
+ }
+ else {
+ schema.setExclusiveMinimum(true);
+ }
+ }
+ if (annotationName.equals(DecimalMax.class.getSimpleName())) {
+ DecimalMax max = (DecimalMax) anno;
+ if (max.inclusive()) {
+ schema.setMaximum(BigDecimal.valueOf(Double.parseDouble(max.value())));
+ }
+ else {
+ schema.setExclusiveMaximum(true);
+ }
+ }
+ if (annotationName.equals(Size.class.getSimpleName())) {
+ String type = schema.getType();
+ if (type == null && schema.getTypes() != null && schema.getTypes().size() == 1)
+ type = schema.getTypes().iterator().next();
+ if (OPENAPI_ARRAY_TYPE.equals(type)) {
+ schema.setMinItems(((Size) anno).min());
+ schema.setMaxItems(((Size) anno).max());
+ }
+ else if (OPENAPI_STRING_TYPE.equals(type)) {
+ schema.setMinLength(((Size) anno).min());
+ schema.setMaxLength(((Size) anno).max());
+ }
+ }
+ if (annotationName.equals(Pattern.class.getSimpleName())) {
+ schema.setPattern(((Pattern) anno).regexp());
+ }
+ });
+ if (schema!=null && annotatedNotNull(annotations)) {
+ String specVersion = schema.getSpecVersion().name();
+ if (!"V30".equals(specVersion)) {
+ schema.setNullable(false);
+ }
+ }
+ }
+
+ /**
+ * Nullable from annotations boolean.
+ *
+ * @param field the field
+ * @return the boolean
+ */
+ private static Boolean nullableFromAnnotations(Field field) {
+ if (hasNullableAnnotation(field)) return true;
+ if (hasNotNullAnnotation(field)) return false;
+ Method getter = findGetter(field);
+ if (getter != null) {
+ if (hasNullableAnnotation(getter)) return true;
+ if (hasNotNullAnnotation(getter)) return false;
+ }
+ return null;
+ }
+
+ /**
+ * Has nullable annotation boolean.
+ *
+ * @param el the el
+ * @return the boolean
+ */
+ private static boolean hasNullableAnnotation(AnnotatedElement el) {
+ if (el == null) return false;
+ for (Annotation ann : el.getAnnotations()) {
+ if (ANNOTATIONS_FOR_NULLABLE.contains(ann.annotationType().getSimpleName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Has not null annotation boolean.
+ *
+ * @param el the el
+ * @return the boolean
+ */
+ private static boolean hasNotNullAnnotation(AnnotatedElement el) {
+ if (el == null) {
+ return false;
+ }
+ for (Annotation ann : el.getAnnotations()) {
+ String simpleName = ann.annotationType().getSimpleName();
+ if (ANNOTATIONS_FOR_REQUIRED.contains(simpleName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Has nullable on getter boolean.
+ *
+ * @param f the f
+ * @return the boolean
+ */
+ private static boolean hasNullableOnGetter(Field f) {
+ Method g = findGetter(f);
+ return g != null && hasNullableAnnotation(g);
+ }
+
+ /**
+ * Has not null on getter boolean.
+ *
+ * @param f the f
+ * @return the boolean
+ */
+ private static boolean hasNotNullOnGetter(Field f) {
+ Method g = findGetter(f);
+ return g != null && hasNotNullAnnotation(g);
+ }
+
+ /**
+ * Has nullable on ctor param boolean.
+ *
+ * @param f the f
+ * @return the boolean
+ */
+ private static boolean hasNullableOnCtorParam(Field f) {
+ return ctorParamHasAnyAnnotationSimpleName(f, ANNOTATIONS_FOR_NULLABLE);
+ }
+
+ /**
+ * Has not null on ctor param boolean.
+ *
+ * @param f the f
+ * @return the boolean
+ */
+ private static boolean hasNotNullOnCtorParam(Field f) {
+ return ctorParamHasAnyAnnotationSimpleName(f, ANNOTATIONS_FOR_REQUIRED);
+ }
+
+ /**
+ * Ctor param has any annotation simple name boolean.
+ *
+ * @param f the f
+ * @param simpleNames the simple names
+ * @return the boolean
+ */
+ private static boolean ctorParamHasAnyAnnotationSimpleName(Field f, Collection simpleNames) {
+ if (f == null || simpleNames == null || simpleNames.isEmpty()) return false;
+
+ final String fieldName = f.getName();
+ final Class> declaring = f.getDeclaringClass();
+
+ try {
+ for (Constructor> ctor : declaring.getDeclaredConstructors()) {
+ java.lang.reflect.Parameter[] params = ctor.getParameters();
+ for (java.lang.reflect.Parameter p : params) {
+ // A) compiled with -parameters
+ if (fieldName.equals(p.getName()) && paramHasAnyAnnotationSimpleName(p, simpleNames)) {
+ return true;
+ }
+ // B) fallback: @JsonProperty("fieldName") on the parameter
+ if (hasJsonPropertyName(p, fieldName) && paramHasAnyAnnotationSimpleName(p, simpleNames)) {
+ return true;
+ }
+ }
+ }
+ } catch (Throwable ignored) {
+ // best-effort only
+ }
+ return false;
+ }
+
+ /**
+ * Param has any annotation simple name boolean.
+ *
+ * @param p the p
+ * @param simpleNames the simple names
+ * @return the boolean
+ */
+ private static boolean paramHasAnyAnnotationSimpleName(java.lang.reflect.Parameter p, Collection simpleNames) {
+ for (Annotation ann : p.getAnnotations()) {
+ if (simpleNames.contains(ann.annotationType().getSimpleName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Has json property name boolean.
+ *
+ * @param p the p
+ * @param expected the expected
+ * @return the boolean
+ */
+ private static boolean hasJsonPropertyName(java.lang.reflect.Parameter p, String expected) {
+ try {
+ JsonProperty jp = p.getAnnotation(JsonProperty.class);
+ return jp != null && expected.equals(jp.value());
+ } catch (Throwable ignored) {
+ return false;
+ }
+ }
+
+ /**
+ * Find getter method.
+ *
+ * @param f the f
+ * @return the method
+ */
+ private static Method findGetter(Field f) {
+ String n = f.getName();
+ String cap = Character.toUpperCase(n.charAt(0)) + n.substring(1);
+ String[] names = (f.getType() == boolean.class || f.getType() == Boolean.class)
+ ? new String[] { "is" + cap, "get" + cap }
+ : new String[] { "get" + cap };
+ for (String m : names) {
+ try {
+ return f.getDeclaringClass().getMethod(m);
+ } catch (NoSuchMethodException ignored) {}
+ }
+ return null;
+ }
+
+ /**
+ * Gets json property.
+ *
+ * @param f the f
+ * @return the json property
+ */
+ private static JsonProperty getJsonProperty(Field f) {
+ JsonProperty jp = f.getAnnotation(JsonProperty.class);
+ if (jp != null) return jp;
+ Method g = findGetter(f);
+ if (g != null) return g.getAnnotation(JsonProperty.class);
+ return null;
+ }
+}
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 0fe880c91..7aeeceb93 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
@@ -32,6 +32,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -44,6 +45,8 @@
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.converter.AnnotatedType;
+import io.swagger.v3.core.converter.ModelConverterContext;
+import io.swagger.v3.core.converter.ModelConverterContextImpl;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.core.util.AnnotationsUtils;
@@ -53,6 +56,7 @@
import io.swagger.v3.oas.annotations.media.SchemaProperty;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.SpecVersion;
+import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Content;
@@ -64,6 +68,7 @@
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springdoc.core.providers.JavadocProvider;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
@@ -90,6 +95,11 @@ public class SpringDocAnnotationsUtils extends AnnotationsUtils {
*/
private static final List ANNOTATIONS_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
+ /**
+ * The reusable context
+ */
+ private static final ThreadLocal> MODEL_CONVERTER_CONTEXT_MAP = ThreadLocal.withInitial(HashMap::new);
+
static {
ANNOTATIONS_TO_IGNORE.add(Hidden.class);
ANNOTATIONS_TO_IGNORE.add(JsonIgnore.class);
@@ -130,18 +140,16 @@ public static Schema resolveSchemaFromType(Class> schemaImplementation, Compon
public static Schema extractSchema(Components components, Type returnType, JsonView jsonView, Annotation[] annotations, SpecVersion specVersion) {
if (returnType == null) return null;
Schema schemaN = null;
- ResolvedSchema resolvedSchema;
boolean openapi31 = SpecVersion.V31 == specVersion;
- try {
- resolvedSchema = ModelConverters.getInstance(openapi31)
- .resolveAsResolvedSchema(
- new AnnotatedType(returnType)
- .resolveAsRef(true).jsonViewAnnotation(jsonView).ctxAnnotations(annotations));
- }
- catch (Exception e) {
- LOGGER.warn(Constants.GRACEFUL_EXCEPTION_OCCURRED, e);
- return null;
- }
+ if (jsonView != null)
+ annotations = ArrayUtils.addAll(annotations, jsonView);
+
+ AnnotatedType annotatedType = new AnnotatedType(returnType)
+ .resolveAsRef(true)
+ .jsonViewAnnotation(jsonView)
+ .ctxAnnotations(annotations);
+ ResolvedSchema resolvedSchema = resolveAsResolvedSchema(openapi31, annotatedType);
+
if (resolvedSchema != null) {
Map schemaMap = resolvedSchema.referencedSchemas;
if (!CollectionUtils.isEmpty(schemaMap) && components != null) {
@@ -481,4 +489,72 @@ public static Object resolveDefaultValue(String defaultValueStr, ObjectMapper ob
}
return defaultValue;
}
+
+ /**
+ * Gets headers.
+ *
+ * @param annotationHeaders the annotation headers
+ * @param components the components
+ * @param jsonViewAnnotation the json view annotation
+ * @param openapi31 the openapi 31
+ * @return the headers
+ */
+ public static Optional> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
+ Optional> headerMap = AnnotationsUtils.getHeaders(annotationHeaders, components, jsonViewAnnotation, openapi31);
+ if (openapi31) {
+ headerMap.ifPresent(map -> {
+ for (Entry entry : map.entrySet()) {
+ Header header = entry.getValue();
+ Schema schema = header.getSchema();
+ handleSchemaTypes(schema);
+ }
+ });
+ }
+ return headerMap;
+ }
+
+ /**
+ * Clear cache.
+ *
+ * @param javadocProvider the javadoc provider
+ */
+ public static void clearCache(JavadocProvider javadocProvider) {
+ if (javadocProvider != null)
+ javadocProvider.clearCache();
+ MODEL_CONVERTER_CONTEXT_MAP.remove();;
+ }
+
+ /**
+ * Resolve as resolved schema resolved schema.
+ *
+ * @param openapi31 the openapi 31
+ * @param type the type
+ * @return the resolved schema
+ */
+ private static ResolvedSchema resolveAsResolvedSchema(boolean openapi31, AnnotatedType type) {
+ try {
+ ModelConverterContext modelConverterContext = getModelConverterContext(openapi31);
+ ResolvedSchema resolvedSchema = new ResolvedSchema();
+ resolvedSchema.schema = modelConverterContext.resolve(type);
+ resolvedSchema.referencedSchemas = modelConverterContext.getDefinedModels();
+ return resolvedSchema;
+ }
+ catch (Exception e) {
+ LOGGER.warn(Constants.GRACEFUL_EXCEPTION_OCCURRED, e);
+ return null;
+ }
+ }
+
+ /**
+ * Gets model converter context.
+ *
+ * @param openapi31 the openapi 31
+ * @return the model converter context
+ */
+ private static ModelConverterContext getModelConverterContext(boolean openapi31) {
+ Map perThread = MODEL_CONVERTER_CONTEXT_MAP.get();
+ return perThread.computeIfAbsent(openapi31, key ->
+ new ModelConverterContextImpl(ModelConverters.getInstance(openapi31).getConverters()));
+ }
+
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java
index 281fd4eda..6ffef2802 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java
@@ -138,7 +138,7 @@ public void customise(OpenAPI openAPI, ResourceMappings mappings, PersistentEnti
entityInfo.setAssociationsFields(associationsFields);
entityInoMap.put(domainType.getSimpleName(), entityInfo);
}
-
+
openAPI.getPaths().entrySet().stream()
.forEach(stringPathItemEntry -> {
PathItem pathItem = stringPathItemEntry.getValue();
@@ -246,7 +246,7 @@ private void updateRequestBodySchemaProperties(String key, Schema referencedSche
Entry