diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java b/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java index aa94bdc19..b4dac031a 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java @@ -4,6 +4,7 @@ import java.time.Instant; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,10 +43,11 @@ public abstract class AbstractOpenApiResource { protected OperationBuilder operationParser; protected RequestBodyBuilder requestBodyBuilder; protected GeneralInfoBuilder generalInfoBuilder; + protected Optional> openApiCustomisers; protected AbstractOpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder, AbstractResponseBuilder responseBuilder, OperationBuilder operationParser, - RequestBodyBuilder requestBodyBuilder, GeneralInfoBuilder generalInfoBuilder) { + RequestBodyBuilder requestBodyBuilder, GeneralInfoBuilder generalInfoBuilder, Optional> openApiCustomisers) { super(); this.openAPIBuilder = openAPIBuilder; this.requestBuilder = requestBuilder; @@ -53,6 +55,7 @@ protected AbstractOpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequest this.operationParser = operationParser; this.requestBodyBuilder = requestBodyBuilder; this.generalInfoBuilder = generalInfoBuilder; + this.openApiCustomisers = openApiCustomisers; } protected OpenAPI getOpenApi() { @@ -71,8 +74,15 @@ protected OpenAPI getOpenApi() { responseBuilder.buildGenericResponse(openAPIBuilder.getComponents(), findControllerAdvice); getPaths(restControllers); + OpenAPI openApi = openAPIBuilder.getOpenAPI(); + + // run the optional customisers + if (openApiCustomisers.isPresent()) { + openApiCustomisers.get().stream().forEach(openApiCustomiser -> openApiCustomiser.customise(openApi)); + } + LOGGER.info("Init duration for springdoc-openapi is: {} ms", Duration.between(start, Instant.now()).toMillis()); - return openAPIBuilder.getOpenAPI(); + return openApi; } protected abstract void getPaths(Map findRestControllers); diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/api/OpenApiCustomiser.java b/springdoc-openapi-common/src/main/java/org/springdoc/api/OpenApiCustomiser.java new file mode 100644 index 000000000..4b685e3e5 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/api/OpenApiCustomiser.java @@ -0,0 +1,7 @@ +package org.springdoc.api; + +import io.swagger.v3.oas.models.OpenAPI; + +public interface OpenApiCustomiser { + public void customise(OpenAPI openApi); +} diff --git a/springdoc-openapi-core/src/main/java/org/springdoc/api/OpenApiResource.java b/springdoc-openapi-core/src/main/java/org/springdoc/api/OpenApiResource.java index 3c6bbf9f8..2313bdb3e 100644 --- a/springdoc-openapi-core/src/main/java/org/springdoc/api/OpenApiResource.java +++ b/springdoc-openapi-core/src/main/java/org/springdoc/api/OpenApiResource.java @@ -4,7 +4,9 @@ import static org.springframework.util.AntPathMatcher.*; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import javax.servlet.http.HttpServletRequest; @@ -48,8 +50,9 @@ public class OpenApiResource extends AbstractOpenApiResource { public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder, AbstractResponseBuilder responseBuilder, OperationBuilder operationParser, GeneralInfoBuilder infoBuilder, - RequestBodyBuilder requestBodyBuilder, RequestMappingInfoHandlerMapping requestMappingHandlerMapping) { - super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, requestBodyBuilder, infoBuilder); + RequestBodyBuilder requestBodyBuilder, RequestMappingInfoHandlerMapping requestMappingHandlerMapping, + Optional> openApiCustomisers) { + super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, requestBodyBuilder, infoBuilder, openApiCustomisers); this.requestMappingHandlerMapping = requestMappingHandlerMapping; } diff --git a/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/HelloController.java b/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/HelloController.java new file mode 100644 index 000000000..0df91ae7a --- /dev/null +++ b/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/HelloController.java @@ -0,0 +1,25 @@ +package test.org.springdoc.api.app39; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; + +@RestController +public class HelloController { + + @Operation(summary = "test Request") + @RequestBody(description = "test value", required = true, content = @Content(schema = @Schema(implementation = String.class))) + @PostMapping("/test") + public void searchEmployee(String test) { + } + + @GetMapping("/hello") + public String hello() { + return "hello"; + } +} diff --git a/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/SpringDocApp39Test.java b/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/SpringDocApp39Test.java new file mode 100644 index 000000000..f04087f19 --- /dev/null +++ b/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/SpringDocApp39Test.java @@ -0,0 +1,8 @@ +package test.org.springdoc.api.app39; + +import test.org.springdoc.api.AbstractSpringDocTest; + +public class SpringDocApp39Test extends AbstractSpringDocTest { + + +} \ No newline at end of file diff --git a/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/SpringDocTestApp.java b/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/SpringDocTestApp.java new file mode 100644 index 000000000..0d348f4db --- /dev/null +++ b/springdoc-openapi-core/src/test/java/test/org/springdoc/api/app39/SpringDocTestApp.java @@ -0,0 +1,32 @@ +package test.org.springdoc.api.app39; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import org.springdoc.api.OpenApiCustomiser; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class SpringDocTestApp { + + public static void main(String[] args) { + SpringApplication.run(SpringDocTestApp.class, args); + } + + @Bean + public OpenAPI customOpenAPI() { + StringSchema schema = new StringSchema(); + return new OpenAPI() + .components(new Components().addParameters("myGlobalHeader", new HeaderParameter().required(true).name("My-Global-Header").description("My Global Header").schema(schema))); + } + + @Bean + public OpenApiCustomiser customerGlobalHeaderOpenApiCustomiser() { + return openApi -> openApi.getPaths().values().stream().flatMap(pathItem -> pathItem.readOperations().stream()) + .forEach(operation -> operation.addParametersItem(new HeaderParameter().$ref("#/components/parameters/myGlobalHeader"))); + } +} diff --git a/springdoc-openapi-core/src/test/resources/results/app39.json b/springdoc-openapi-core/src/test/resources/results/app39.json new file mode 100644 index 000000000..d9b7af958 --- /dev/null +++ b/springdoc-openapi-core/src/test/resources/results/app39.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/test": { + "post": { + "summary": "test Request", + "operationId": "searchEmployee", + "parameters": [ + { + "in": "header", + "$ref": "#/components/parameters/myGlobalHeader" + } + ], + "requestBody": { + "description": "test value", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "default response" + } + } + } + }, + "/hello": { + "get": { + "operationId": "hello", + "parameters": [ + { + "in": "header", + "$ref": "#/components/parameters/myGlobalHeader" + } + ], + "responses": { + "200": { + "description": "default response", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "parameters": { + "myGlobalHeader": { + "name": "My-Global-Header", + "in": "header", + "description": "My Global Header", + "required": true, + "schema": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/api/OpenApiResource.java b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/api/OpenApiResource.java index f831c7a71..8c3b06c20 100644 --- a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/api/OpenApiResource.java +++ b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/api/OpenApiResource.java @@ -3,7 +3,9 @@ import static org.springdoc.core.Constants.*; import static org.springframework.util.AntPathMatcher.*; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.springdoc.core.AbstractRequestBuilder; @@ -42,9 +44,10 @@ public class OpenApiResource extends AbstractOpenApiResource { public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder, AbstractResponseBuilder responseBuilder, OperationBuilder operationParser, GeneralInfoBuilder infoBuilder, RequestBodyBuilder requestBodyBuilder, - RequestMappingInfoHandlerMapping requestMappingHandlerMapping) { + RequestMappingInfoHandlerMapping requestMappingHandlerMapping, + Optional> openApiCustomisers) { super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, requestBodyBuilder, - infoBuilder); + infoBuilder, openApiCustomisers); this.requestMappingHandlerMapping = requestMappingHandlerMapping; } diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/HelloController.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/HelloController.java new file mode 100644 index 000000000..c437d2968 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/HelloController.java @@ -0,0 +1,26 @@ +package test.org.springdoc.api.app39; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; + +@RestController +public class HelloController { + + @Operation(summary = "test Request") + @RequestBody(description = "test value", required = true, content = @Content(schema = @Schema(implementation = String.class))) + @PostMapping("/test") + public Mono searchEmployee(String test) { + return Mono.empty(); + } + + @GetMapping("/hello") + public Mono hello() { + return Mono.just("hello"); + } +} diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/SpringDocApp39Test.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/SpringDocApp39Test.java new file mode 100644 index 000000000..f04087f19 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/SpringDocApp39Test.java @@ -0,0 +1,8 @@ +package test.org.springdoc.api.app39; + +import test.org.springdoc.api.AbstractSpringDocTest; + +public class SpringDocApp39Test extends AbstractSpringDocTest { + + +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/SpringDocTestApp.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/SpringDocTestApp.java new file mode 100644 index 000000000..2e58cb019 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app39/SpringDocTestApp.java @@ -0,0 +1,34 @@ +package test.org.springdoc.api.app39; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import org.springdoc.api.OpenApiCustomiser; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = { "org.springdoc", "test.org.springdoc.api.app39" }) +public class SpringDocTestApp { + + public static void main(String[] args) { + SpringApplication.run(SpringDocTestApp.class, args); + } + + @Bean + public OpenAPI customOpenAPI() { + StringSchema schema = new StringSchema(); + return new OpenAPI() + .components(new Components().addParameters("myGlobalHeader", new HeaderParameter().required(true).name("My-Global-Header").description("My Global Header").schema(schema))); + } + + @Bean + public OpenApiCustomiser customerGlobalHeaderOpenApiCustomiser() { + return openApi -> openApi.getPaths().values().stream().flatMap(pathItem -> pathItem.readOperations().stream()) + .forEach(operation -> operation.addParametersItem(new HeaderParameter().$ref("#/components/parameters/myGlobalHeader"))); + } +} diff --git a/springdoc-openapi-webflux-core/src/test/resources/results/app39.json b/springdoc-openapi-webflux-core/src/test/resources/results/app39.json new file mode 100644 index 000000000..4d24de727 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/resources/results/app39.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + } + ], + "paths": { + "/test": { + "post": { + "summary": "test Request", + "operationId": "searchEmployee", + "parameters": [ + { + "in": "header", + "$ref": "#/components/parameters/myGlobalHeader" + } + ], + "requestBody": { + "description": "test value", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "default response" + } + } + } + }, + "/hello": { + "get": { + "operationId": "hello", + "parameters": [ + { + "in": "header", + "$ref": "#/components/parameters/myGlobalHeader" + } + ], + "responses": { + "200": { + "description": "default response", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "parameters": { + "myGlobalHeader": { + "name": "My-Global-Header", + "in": "header", + "description": "My Global Header", + "required": true, + "schema": { + "type": "string" + } + } + } + } +} \ No newline at end of file