From 22990c94a9220ff4a0873e272b89cd862ebfdb41 Mon Sep 17 00:00:00 2001 From: christophejan <69955393+christophejan@users.noreply.github.com> Date: Mon, 18 Apr 2022 14:02:11 +0200 Subject: [PATCH 1/4] Extra assertions on default group (on current behavior) --- .../api/app145/SpringDocApp145Test.java | 22 +++++++++++++++++- .../api/app147/SpringDocApp147Test.java | 7 ++++++ .../api/app148/SpringDocApp148Test.java | 20 ++++++++++++++++ .../api/app145/SpringDocApp145Test.java | 17 +++++++++++++- .../api/app147/SpringDocApp147Test.java | 8 ++++++- .../api/app148/SpringDocApp148Test.java | 23 ++++++++++++++++++- 6 files changed, 93 insertions(+), 4 deletions(-) diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java index 562aea29e..e902c09cd 100644 --- a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2019-2020 the original author or authors. + * * Copyright 2019-2022 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. @@ -18,6 +18,9 @@ package test.org.springdoc.api.app145; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import org.junit.jupiter.api.Test; import org.springdoc.core.Constants; import test.org.springdoc.api.AbstractSpringDocActuatorTest; @@ -25,6 +28,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClientResponseException; @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, @@ -47,4 +52,19 @@ public void testApp() { .expectStatus().isNotFound(); } + @Test + public void testApp3() throws Exception { + try { + webClient.get().uri("/application/openapi"+ "/"+Constants.DEFAULT_GROUP_NAME).retrieve() + .bodyToMono(String.class).block(); + fail(); + } + catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.NOT_FOUND) + assertTrue(true); + else + fail(); + } + } + } diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java index c2119a616..238402a31 100644 --- a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java @@ -68,4 +68,11 @@ public void testApp1() throws Exception { assertEquals(expected, result, true); } + @Test + public void testApp2() throws Exception { + webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/"+Constants.DEFAULT_GROUP_NAME) + .exchange() + .expectStatus().isNotFound(); + } + } diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java index e1df88b08..83e14f980 100644 --- a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java @@ -19,12 +19,18 @@ package test.org.springdoc.api.app148; import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; + import test.org.springdoc.api.AbstractSpringDocActuatorTest; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @@ -59,4 +65,18 @@ public void testApp2() throws Exception { assertEquals(expected, result, true); } + @Test + public void testApp3() throws Exception { + try { + webClient.get().uri("/test/application/openapi" + "/"+Constants.DEFAULT_GROUP_NAME).retrieve() + .bodyToMono(String.class).block(); + fail(); + } + catch (WebClientResponseException ex) { + if (ex.getStatusCode() == HttpStatus.NOT_FOUND) + assertTrue(true); + else + fail(); + } + } } diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java index f103e27d7..837edc153 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app145/SpringDocApp145Test.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2019-2020 the original author or authors. + * * Copyright 2019-2022 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. @@ -27,6 +27,7 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.http.HttpStatus; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpStatusCodeException; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -75,4 +76,18 @@ public void testApp2() throws Exception { assertEquals(expected, result, true); } + @Test + public void testApp3() throws Exception { + try { + actuatorRestTemplate.getForObject("/application/openapi"+ "/"+Constants.DEFAULT_GROUP_NAME, String.class); + fail(); + } + catch (HttpStatusCodeException ex) { + // TODO: Currently obtain status 500 on MVC... Webflux obtain 404... + if (ex.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) + assertTrue(true); + else + fail(); + } + } } diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java index 5cebf46f6..9a574cb27 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2019-2020 the original author or authors. + * * Copyright 2019-2022 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. @@ -61,4 +61,10 @@ public void testApp1() throws Exception { .andExpect(content().json(getContent("results/app147-2.json"), true)); } + @Test + public void testApp2() throws Exception { + mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL + "/"+Constants.DEFAULT_GROUP_NAME)) + .andExpect(status().isNotFound()); + } + } diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java index 50f51e4de..1a717a460 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2019-2020 the original author or authors. + * * Copyright 2019-2022 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. @@ -19,12 +19,18 @@ package test.org.springdoc.api.app148; import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; + import test.org.springdoc.api.AbstractSpringDocActuatorTest; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.HttpStatusCodeException; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @@ -59,4 +65,19 @@ public void testApp2() throws Exception { assertEquals(expected, result, true); } + @Test + public void testApp3() throws Exception { + try { + actuatorRestTemplate.getForObject("/test/application/openapi" + "/"+Constants.DEFAULT_GROUP_NAME, String.class); + fail(); + } + catch (HttpStatusCodeException ex) { + // TODO: Currently obtain status 500 on MVC... Webflux obtain 404... + if (ex.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) + assertTrue(true); + else + fail(); + } + } + } From 76b1abb8b3fe5121d6c95573c3835c20f9942447 Mon Sep 17 00:00:00 2001 From: christophejan <69955393+christophejan@users.noreply.github.com> Date: Mon, 18 Apr 2022 14:02:11 +0200 Subject: [PATCH 2/4] Declare the most specific return type possible on some bean declaration This is safer for components that implement several interfaces or for components potentially referred to by their implementation type (see 'Declaring a bean' on spring core documentation) --- .../org/springdoc/core/SpringDocConfiguration.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java index 85296fc8b..1ac1d620e 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java @@ -51,7 +51,6 @@ import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.OpenApiCustomiser; -import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.customizers.PropertyCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.providers.ActuatorProvider; @@ -109,6 +108,7 @@ /** * The type Spring doc configuration. * @author bnasslahsen + * @author christophejan */ @Lazy(false) @Configuration(proxyBeanMethods = false) @@ -359,7 +359,7 @@ OpenApiCustomiser propertiesResolverForSchema(PropertyResolverUtils propertyReso @Conditional(CacheOrGroupedOpenApiCondition.class) @ConditionalOnClass(name = BINDRESULT_CLASS) @Lazy(false) - static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor() { + static SpringdocBeanFactoryConfigurer springdocBeanFactoryPostProcessor() { return new SpringdocBeanFactoryConfigurer(); } @@ -435,7 +435,7 @@ static class SpringDocActuatorConfiguration { @Lazy(false) @ConditionalOnManagementPort(ManagementPortType.DIFFERENT) @Conditional(MultipleOpenApiSupportCondition.class) - static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor3(List groupedOpenApis) { + static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3(List groupedOpenApis) { return new SpringdocActuatorBeanFactoryConfigurer(groupedOpenApis); } @@ -447,7 +447,7 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor3(List Date: Mon, 18 Apr 2022 14:02:11 +0200 Subject: [PATCH 3/4] Interact only with bean definition during bean registration phase - register bean definition rather than bean instance - do not require GroupedOpenApi beans instances anymore for SpringdocActuatorBeanFactoryConfigurer bean registrations This will prevent any premature bean instantiation, violating the container and causing unintended side-effects (see BeanFactoryPostProcessor documentation) --- .../core/SpringDocConfiguration.java | 5 +- ...pringdocActuatorBeanFactoryConfigurer.java | 128 ++++++++++++------ .../core/SpringdocBeanFactoryConfigurer.java | 72 ++++++---- 3 files changed, 130 insertions(+), 75 deletions(-) diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java index 1ac1d620e..66f2d9cc5 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java @@ -428,15 +428,14 @@ static class SpringDocActuatorConfiguration { /** * Springdoc bean factory post processor 3 bean factory post processor. * - * @param groupedOpenApis the grouped open apis * @return the bean factory post processor */ @Bean @Lazy(false) @ConditionalOnManagementPort(ManagementPortType.DIFFERENT) @Conditional(MultipleOpenApiSupportCondition.class) - static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3(List groupedOpenApis) { - return new SpringdocActuatorBeanFactoryConfigurer(groupedOpenApis); + static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3() { + return new SpringdocActuatorBeanFactoryConfigurer(); } /** diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java index 522d391ac..b9ada982a 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java @@ -2,7 +2,7 @@ * * * * * * - * * * * Copyright 2019-2020 the original author or authors. + * * * * Copyright 2019-2022 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. @@ -23,17 +23,20 @@ package org.springdoc.core; -import java.util.ArrayList; -import java.util.List; - import org.springdoc.core.customizers.ActuatorOpenApiCustomizer; import org.springdoc.core.customizers.ActuatorOperationCustomizer; - +import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.context.properties.bind.BindResult; import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.util.CollectionUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.util.ObjectUtils; import static org.springdoc.core.Constants.ACTUATOR_DEFAULT_GROUP; import static org.springdoc.core.Constants.ALL_PATTERN; @@ -44,58 +47,95 @@ /** * The type Springdoc bean factory configurer. * @author bnasslahsen + * @author christophejan */ -public class SpringdocActuatorBeanFactoryConfigurer extends SpringdocBeanFactoryConfigurer{ +public class SpringdocActuatorBeanFactoryConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor { /** - * The Grouped open apis. + * The ApplicationContext. */ - private List groupedOpenApis; + protected ApplicationContext applicationContext; - /** - * Instantiates a new Springdoc actuator bean factory configurer. - * - * @param groupedOpenApis the grouped open apis - */ - public SpringdocActuatorBeanFactoryConfigurer(List groupedOpenApis) { - this.groupedOpenApis = groupedOpenApis; + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; } @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { - final BindResult result = Binder.get(environment) + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { + final BindResult result = Binder.get(applicationContext.getEnvironment()) .bind(MANAGEMENT_ENDPOINTS_WEB, WebEndpointProperties.class); if (result.isBound()) { WebEndpointProperties webEndpointProperties = result.get(); - List newGroups = new ArrayList<>(); - - ActuatorOpenApiCustomizer actuatorOpenApiCustomiser = new ActuatorOpenApiCustomizer(webEndpointProperties); - beanFactory.registerSingleton("actuatorOpenApiCustomiser", actuatorOpenApiCustomiser); - ActuatorOperationCustomizer actuatorCustomizer = new ActuatorOperationCustomizer(); - beanFactory.registerSingleton("actuatorCustomizer", actuatorCustomizer); - - GroupedOpenApi actuatorGroup = GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP) - .pathsToMatch(webEndpointProperties.getBasePath() + ALL_PATTERN) - .pathsToExclude(webEndpointProperties.getBasePath() + HEALTH_PATTERN) - .addOperationCustomizer(actuatorCustomizer) - .addOpenApiCustomiser(actuatorOpenApiCustomiser) - .build(); - // Add the actuator group - newGroups.add(actuatorGroup); - - if (CollectionUtils.isEmpty(groupedOpenApis)) { - GroupedOpenApi defaultGroup = GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME) - .pathsToMatch(ALL_PATTERN) - .pathsToExclude(webEndpointProperties.getBasePath() + ALL_PATTERN) - .build(); - // Register the default group - newGroups.add(defaultGroup); - } + boolean addDefaultGroup = ObjectUtils.isEmpty(applicationContext + .getBeanNamesForType(GroupedOpenApi.class, true, false)); + + registry.registerBeanDefinition("actuatorOpenApiCustomiser", BeanDefinitionBuilder + .genericBeanDefinition(ActuatorOpenApiCustomizer.class) + .addConstructorArgValue(webEndpointProperties) + .getBeanDefinition()); + + registry.registerBeanDefinition("actuatorCustomizer", BeanDefinitionBuilder + .genericBeanDefinition(ActuatorOperationCustomizer.class) + .getBeanDefinition()); - newGroups.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt)); + // register the actuator group bean definition + registry.registerBeanDefinition(ACTUATOR_DEFAULT_GROUP, BeanDefinitionBuilder + .genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class) + .setFactoryMethod("actuatorGroupFactoryMethod") + .addConstructorArgValue(webEndpointProperties.getBasePath()) + .addConstructorArgValue(new RuntimeBeanReference(ActuatorOpenApiCustomizer.class)) + .addConstructorArgValue(new RuntimeBeanReference(ActuatorOperationCustomizer.class)) + .getBeanDefinition()); + + if (addDefaultGroup) { + // register the default group bean definition + registry.registerBeanDefinition(DEFAULT_GROUP_NAME, BeanDefinitionBuilder + .genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class) + .setFactoryMethod("defaultGroupFactoryMethod") + .addConstructorArgValue(webEndpointProperties.getBasePath()) + .getBeanDefinition()); + } } - initBeanFactoryPostProcessor(beanFactory); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + SpringdocBeanFactoryConfigurer.initBeanFactoryPostProcessor(beanFactory); + } + + /** + * Actuator {@link GroupedOpenApi} factory method. + * + * @param actuatorBasePath the actuator base path + * @param actuatorOpenApiCustomiser the {@link ActuatorOpenApiCustomizer} + * @param actuatorOperationCustomizer the {@link ActuatorOperationCustomizer} + * + * @return the actuator {@link GroupedOpenApi} + */ + public static GroupedOpenApi actuatorGroupFactoryMethod(String actuatorBasePath, + ActuatorOpenApiCustomizer actuatorOpenApiCustomiser, ActuatorOperationCustomizer actuatorOperationCustomizer) { + return GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP) + .pathsToMatch(actuatorBasePath + ALL_PATTERN) + .pathsToExclude(actuatorBasePath + HEALTH_PATTERN) + .addOpenApiCustomiser(actuatorOpenApiCustomiser) + .addOperationCustomizer(actuatorOperationCustomizer) + .build(); + } + + /** + * Default {@link GroupedOpenApi} factory method. + * + * @param actuatorBasePath the actuator base path + * + * @return the default {@link GroupedOpenApi} + */ + public static GroupedOpenApi defaultGroupFactoryMethod(String actuatorBasePath) { + return GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME) + .pathsToMatch(ALL_PATTERN) + .pathsToExclude(actuatorBasePath + ALL_PATTERN) + .build(); } } diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java index 353978ccb..1f2c2d062 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java @@ -2,7 +2,7 @@ * * * * * * - * * * * Copyright 2019-2020 the original author or authors. + * * * * Copyright 2019-2022 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. @@ -23,18 +23,18 @@ package org.springdoc.core; -import java.util.List; -import java.util.stream.Collectors; - import io.swagger.v3.oas.models.OpenAPI; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springdoc.core.SpringDocConfigProperties.GroupConfig; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.boot.context.properties.bind.BindResult; import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; -import org.springframework.lang.Nullable; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.util.CollectionUtils; import static org.springdoc.core.Constants.SPRINGDOC_PREFIX; @@ -43,41 +43,57 @@ /** * The type Springdoc bean factory configurer. * @author bnasslahsen + * @author christophejan */ -public class SpringdocBeanFactoryConfigurer implements EnvironmentAware, BeanFactoryPostProcessor { +public class SpringdocBeanFactoryConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor { /** - * The Environment. + * The ApplicationContext. */ - @Nullable - protected Environment environment; + protected ApplicationContext applicationContext; @Override - public void setEnvironment(Environment environment) { - this.environment = environment; + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; } @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { - final BindResult result = Binder.get(environment) + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { + final BindResult result = Binder.get(applicationContext.getEnvironment()) .bind(SPRINGDOC_PREFIX, SpringDocConfigProperties.class); if (result.isBound()) { - SpringDocConfigProperties springDocGroupConfig = result.get(); - List groupedOpenApis = springDocGroupConfig.getGroupConfigs().stream() - .map(elt -> { - GroupedOpenApi.Builder builder = GroupedOpenApi.builder(); - if (!CollectionUtils.isEmpty(elt.getPackagesToScan())) - builder.packagesToScan(elt.getPackagesToScan().toArray(new String[0])); - if (!CollectionUtils.isEmpty(elt.getPathsToMatch())) - builder.pathsToMatch(elt.getPathsToMatch().toArray(new String[0])); - return builder.group(elt.getGroup()).build(); - }) - .collect(Collectors.toList()); - groupedOpenApis.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt)); + result.get().getGroupConfigs().stream().forEach(groupConfig -> + // register bean definitions + registry.registerBeanDefinition(groupConfig.getGroup(), BeanDefinitionBuilder + .genericBeanDefinition(SpringdocBeanFactoryConfigurer.class) + .setFactoryMethod("groupedOpenApisFactoryMethod") + .addConstructorArgValue(groupConfig) + .getBeanDefinition()) + ); } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { initBeanFactoryPostProcessor(beanFactory); } + /** + * {@link GroupedOpenApi} factory method from {@link GroupConfig}. + * + * @param groupConfig the {@link GroupConfig} + * + * @return the {@link GroupedOpenApi} + */ + public static GroupedOpenApi groupedOpenApisFactoryMethod(GroupConfig groupConfig) { + GroupedOpenApi.Builder builder = GroupedOpenApi.builder(); + if (!CollectionUtils.isEmpty(groupConfig.getPackagesToScan())) + builder.packagesToScan(groupConfig.getPackagesToScan().toArray(new String[0])); + if (!CollectionUtils.isEmpty(groupConfig.getPathsToMatch())) + builder.pathsToMatch(groupConfig.getPathsToMatch().toArray(new String[0])); + return builder.group(groupConfig.getGroup()).build(); + } + /** * Init bean factory post processor. * From 7d4b5f52ffa0cb1db0814bdd21d5fef4828f70af Mon Sep 17 00:00:00 2001 From: christophejan <69955393+christophejan@users.noreply.github.com> Date: Mon, 18 Apr 2022 14:02:11 +0200 Subject: [PATCH 4/4] Add global customizers and filters --- .../core/SpringDocConfiguration.java | 16 +++ ...pringdocActuatorBeanFactoryConfigurer.java | 12 +- .../core/SpringdocBeanFactoryConfigurer.java | 6 +- .../customizers/GlobalOpenApiCustomiser.java | 32 +++++ .../GlobalOperationCustomizer.java | 33 +++++ .../core/customizers/OpenApiCustomiser.java | 8 +- .../core/customizers/OperationCustomizer.java | 8 +- .../filters/GlobalOpenApiMethodFilter.java | 34 +++++ .../core/filters/OpenApiMethodFilter.java | 9 +- .../springdoc/api/app184/HelloController.java | 53 ++++++++ .../api/app184/SpringDocAppapp184Test.java | 126 ++++++++++++++++++ .../src/test/resources/results/app184-1.json | 97 ++++++++++++++ .../src/test/resources/results/app184-2.json | 112 ++++++++++++++++ .../src/test/resources/results/app184-3.json | 112 ++++++++++++++++ .../src/test/resources/results/app184.json | 112 ++++++++++++++++ .../springdoc/api/app184/HelloController.java | 53 ++++++++ .../api/app184/SpringDocAppapp184Test.java | 126 ++++++++++++++++++ .../src/test/resources/results/app184-1.json | 97 ++++++++++++++ .../src/test/resources/results/app184-2.json | 112 ++++++++++++++++ .../src/test/resources/results/app184-3.json | 112 ++++++++++++++++ .../src/test/resources/results/app184.json | 112 ++++++++++++++++ 21 files changed, 1370 insertions(+), 12 deletions(-) create mode 100644 springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomiser.java create mode 100644 springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java create mode 100644 springdoc-openapi-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java create mode 100644 springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/HelloController.java create mode 100644 springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java create mode 100644 springdoc-openapi-webflux-core/src/test/resources/results/app184-1.json create mode 100644 springdoc-openapi-webflux-core/src/test/resources/results/app184-2.json create mode 100644 springdoc-openapi-webflux-core/src/test/resources/results/app184-3.json create mode 100644 springdoc-openapi-webflux-core/src/test/resources/results/app184.json create mode 100644 springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/HelloController.java create mode 100644 springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java create mode 100644 springdoc-openapi-webmvc-core/src/test/resources/results/app184-1.json create mode 100644 springdoc-openapi-webmvc-core/src/test/resources/results/app184-2.json create mode 100644 springdoc-openapi-webmvc-core/src/test/resources/results/app184-3.json create mode 100644 springdoc-openapi-webmvc-core/src/test/resources/results/app184.json diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java index 66f2d9cc5..183a8c0b2 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java @@ -49,10 +49,13 @@ import org.springdoc.core.customizers.ActuatorOperationCustomizer; import org.springdoc.core.customizers.DataRestDelegatingMethodParameterCustomizer; import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer; +import org.springdoc.core.customizers.GlobalOpenApiCustomiser; +import org.springdoc.core.customizers.GlobalOperationCustomizer; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.PropertyCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.filters.GlobalOpenApiMethodFilter; import org.springdoc.core.providers.ActuatorProvider; import org.springdoc.core.providers.CloudFunctionProvider; import org.springdoc.core.providers.JavadocProvider; @@ -84,6 +87,7 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Scope; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.domain.Pageable; @@ -397,6 +401,18 @@ SpringDocProviders springDocProviders(Optional actuatorProvide return new SpringDocProviders(actuatorProvider, springCloudFunctionProvider, springSecurityOAuth2Provider, repositoryRestResourceProvider, routerFunctionProvider, springWebProvider); } + @Bean + @Scope("prototype") + @ConditionalOnMissingBean + public GroupedOpenApi.Builder groupedOpenApiBuilder(List globalOpenApiCustomisers, List globalOperationCustomizers, + List globalOpenApiMethodFilters) { + GroupedOpenApi.Builder builder = GroupedOpenApi.builder(); + globalOpenApiCustomisers.forEach(builder::addOpenApiCustomiser); + globalOperationCustomizers.forEach(builder::addOperationCustomizer); + globalOpenApiMethodFilters.forEach(builder::addOpenApiMethodFilter); + return builder; + } + /** * The type Open api resource advice. * @author bnasslashen diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java index b9ada982a..2129ff227 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java @@ -84,6 +84,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { registry.registerBeanDefinition(ACTUATOR_DEFAULT_GROUP, BeanDefinitionBuilder .genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class) .setFactoryMethod("actuatorGroupFactoryMethod") + .addConstructorArgValue(new RuntimeBeanReference(GroupedOpenApi.Builder.class)) .addConstructorArgValue(webEndpointProperties.getBasePath()) .addConstructorArgValue(new RuntimeBeanReference(ActuatorOpenApiCustomizer.class)) .addConstructorArgValue(new RuntimeBeanReference(ActuatorOperationCustomizer.class)) @@ -94,6 +95,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { registry.registerBeanDefinition(DEFAULT_GROUP_NAME, BeanDefinitionBuilder .genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class) .setFactoryMethod("defaultGroupFactoryMethod") + .addConstructorArgValue(new RuntimeBeanReference(GroupedOpenApi.Builder.class)) .addConstructorArgValue(webEndpointProperties.getBasePath()) .getBeanDefinition()); } @@ -108,15 +110,16 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) /** * Actuator {@link GroupedOpenApi} factory method. * + * @param builder the {@link GroupedOpenApi.Builder} * @param actuatorBasePath the actuator base path * @param actuatorOpenApiCustomiser the {@link ActuatorOpenApiCustomizer} * @param actuatorOperationCustomizer the {@link ActuatorOperationCustomizer} * * @return the actuator {@link GroupedOpenApi} */ - public static GroupedOpenApi actuatorGroupFactoryMethod(String actuatorBasePath, + public static GroupedOpenApi actuatorGroupFactoryMethod(GroupedOpenApi.Builder builder, String actuatorBasePath, ActuatorOpenApiCustomizer actuatorOpenApiCustomiser, ActuatorOperationCustomizer actuatorOperationCustomizer) { - return GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP) + return builder.group(ACTUATOR_DEFAULT_GROUP) .pathsToMatch(actuatorBasePath + ALL_PATTERN) .pathsToExclude(actuatorBasePath + HEALTH_PATTERN) .addOpenApiCustomiser(actuatorOpenApiCustomiser) @@ -127,12 +130,13 @@ public static GroupedOpenApi actuatorGroupFactoryMethod(String actuatorBasePath, /** * Default {@link GroupedOpenApi} factory method. * + * @param builder the {@link GroupedOpenApi.Builder} * @param actuatorBasePath the actuator base path * * @return the default {@link GroupedOpenApi} */ - public static GroupedOpenApi defaultGroupFactoryMethod(String actuatorBasePath) { - return GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME) + public static GroupedOpenApi defaultGroupFactoryMethod(GroupedOpenApi.Builder builder, String actuatorBasePath) { + return builder.group(DEFAULT_GROUP_NAME) .pathsToMatch(ALL_PATTERN) .pathsToExclude(actuatorBasePath + ALL_PATTERN) .build(); diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java index 1f2c2d062..1cd481644 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java @@ -28,6 +28,7 @@ import org.springdoc.core.SpringDocConfigProperties.GroupConfig; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; @@ -67,6 +68,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { registry.registerBeanDefinition(groupConfig.getGroup(), BeanDefinitionBuilder .genericBeanDefinition(SpringdocBeanFactoryConfigurer.class) .setFactoryMethod("groupedOpenApisFactoryMethod") + .addConstructorArgValue(new RuntimeBeanReference(GroupedOpenApi.Builder.class)) .addConstructorArgValue(groupConfig) .getBeanDefinition()) ); @@ -81,12 +83,12 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) /** * {@link GroupedOpenApi} factory method from {@link GroupConfig}. * + * @param builder the {@link GroupedOpenApi.Builder} * @param groupConfig the {@link GroupConfig} * * @return the {@link GroupedOpenApi} */ - public static GroupedOpenApi groupedOpenApisFactoryMethod(GroupConfig groupConfig) { - GroupedOpenApi.Builder builder = GroupedOpenApi.builder(); + public static GroupedOpenApi groupedOpenApisFactoryMethod(GroupedOpenApi.Builder builder, GroupConfig groupConfig) { if (!CollectionUtils.isEmpty(groupConfig.getPackagesToScan())) builder.packagesToScan(groupConfig.getPackagesToScan().toArray(new String[0])); if (!CollectionUtils.isEmpty(groupConfig.getPathsToMatch())) diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomiser.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomiser.java new file mode 100644 index 000000000..dc6d1cf11 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOpenApiCustomiser.java @@ -0,0 +1,32 @@ +/* + * + * * + * * * Copyright 2019-2022 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; + +/** + * Implement and register a bean of type {@link GlobalOpenApiCustomiser} to + * customize Open api on default OpenAPI description and groups. + * + * @author christophejan + * @see OpenApiCustomiser to customize default OpenAPI description but not + * groups + */ +public interface GlobalOpenApiCustomiser extends OpenApiCustomiser { +} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java new file mode 100644 index 000000000..544087ac7 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/GlobalOperationCustomizer.java @@ -0,0 +1,33 @@ +/* + * + * * + * * * Copyright 2019-2022 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; + +/** + * Implement and register a bean of type {@link GlobalOperationCustomizer} to + * customize an operation based on the handler method input on default OpenAPI + * description and groups + * + * @author christophejan + * @see OperationCustomizer to customize operations on default OpenAPI + * description but not groups + */ +public interface GlobalOperationCustomizer extends OperationCustomizer { +} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomiser.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomiser.java index c3573645b..d9146c0f8 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomiser.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OpenApiCustomiser.java @@ -1,7 +1,7 @@ /* * * * - * * * Copyright 2019-2020 the original author or authors. + * * * Copyright 2019-2022 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. @@ -23,8 +23,12 @@ import io.swagger.v3.oas.models.OpenAPI; /** - * The interface Open api customiser. + * Implement and register a bean of type {@link OpenApiCustomiser} to customize + * Open api on default OpenAPI description but not on groups + * * @author bnasslahsen + * @see GlobalOpenApiCustomiser to customize default OpenAPI description and + * groups */ @FunctionalInterface public interface OpenApiCustomiser { diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java index 4e7caf9da..b55a9813e 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/OperationCustomizer.java @@ -1,7 +1,7 @@ /* * * * - * * * Copyright 2019-2020 the original author or authors. + * * * Copyright 2019-2022 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. @@ -26,8 +26,12 @@ /** * Implement and register a bean of type {@link OperationCustomizer} to customize an operation - * based on the handler method input + * based on the handler method input on default OpenAPI descriptions but not + * groups + * * @author bnasslahsen + * @see GlobalOperationCustomizer to customize operations on default OpenAPI + * description and groups */ @FunctionalInterface public interface OperationCustomizer { diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java new file mode 100644 index 000000000..56fd74d18 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/GlobalOpenApiMethodFilter.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * Copyright 2019-2022 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.filters; + +/** + * Implement and register a bean of type {@link GlobalOpenApiMethodFilter} to + * conditionally including any detected methods in default OpenAPI description + * and groups. + * + * @author michael.clarke + * @see OpenApiMethodFilter to filter methods in default OpenAPI description but + * not groups + */ +@FunctionalInterface +public interface GlobalOpenApiMethodFilter extends OpenApiMethodFilter { +} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java index 317967d6b..ede876093 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/filters/OpenApiMethodFilter.java @@ -1,7 +1,7 @@ /* * * * - * * * Copyright 2019-2020 the original author or authors. + * * * Copyright 2019-2022 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. @@ -23,8 +23,13 @@ import java.lang.reflect.Method; /** - * A filter to allow conditionally including any detected methods in an OpenApi definition. + * Implement and register a bean of type {@link OpenApiMethodFilter} to + * conditionally include any detected methods in default OpenAPI descriptions + * but not groups + * * @author michael.clarke + * @see GlobalOpenApiMethodFilter to filter methods in default OpenAPI + * description and groups */ @FunctionalInterface public interface OpenApiMethodFilter { diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/HelloController.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/HelloController.java new file mode 100644 index 000000000..a38bd2590 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/HelloController.java @@ -0,0 +1,53 @@ +/* + * + * * Copyright 2019-2022 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 test.org.springdoc.api.app184; + + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/globalBeanFiltered") + public String globalBeanFiltered() { + return "globalBeanFiltered"; + } + + @GetMapping("/beanFiltered") + public String beanFiltered() { + return "beanFiltered"; + } + + @GetMapping("/group1Filtered") + public String group1Filtered() { + return "group1Filtered"; + } + + @GetMapping("/group2Filtered") + public String group2Filtered() { + return "group2Filtered"; + } + + @GetMapping("/group3Filtered") + public String group3Filtered() { + return "group3Filtered"; + } + +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java new file mode 100644 index 000000000..c9ece5f1b --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java @@ -0,0 +1,126 @@ +/* + * + * * + * * * Copyright 2019-2020 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 test.org.springdoc.api.app184; + +import java.util.Objects; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.customizers.GlobalOpenApiCustomiser; +import org.springdoc.core.customizers.GlobalOperationCustomizer; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.filters.GlobalOpenApiMethodFilter; +import org.springdoc.core.filters.OpenApiMethodFilter; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; + +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import io.swagger.v3.oas.models.servers.Server; +import test.org.springdoc.api.AbstractSpringDocTest; + +@TestPropertySource(properties = { + "springdoc.group-configs[0].group=group1", + "springdoc.group-configs[0].paths-to-exclude=/group1Filtered", +}) +public class SpringDocAppapp184Test extends AbstractSpringDocTest { + + @SpringBootApplication + @ComponentScan(basePackages = { "org.springdoc", "test.org.springdoc.api.app184" }) + static class SpringDocTestApp { + + @Bean + public GlobalOpenApiCustomiser addUrlGlobalBean() { + return openApi -> openApi.getServers().add(new Server().url("urlGlobalBean")); + } + + @Bean + public OpenApiCustomiser addUrlBean() { + return openApi -> openApi.getServers().add(new Server().url("urlBean")); + } + + @Bean + public GlobalOperationCustomizer addHeaderGlobaBeanl() { + return (operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerGlobalBean")); + } + + @Bean + public OperationCustomizer addHeaderBean() { + return (operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerBean")); + } + + @Bean + public GlobalOpenApiMethodFilter globalFilterBean() { + return method -> !Objects.equals(method.getName(), "globalBeanFiltered"); + } + + @Bean + public OpenApiMethodFilter filterBean() { + return method -> !Objects.equals(method.getName(), "beanFiltered"); + } + + @Bean + public GroupedOpenApi group2(GroupedOpenApi.Builder builder) { + return builder + .group("group2") + .addOpenApiCustomiser(openApi -> openApi.getServers().add(new Server().url("urlGroup2"))) + .addOperationCustomizer((operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerGroup2"))) + .addOpenApiMethodFilter(method -> !Objects.equals(method.getName(), "group2Filtered")) + .build(); + } + + @Bean + public GroupedOpenApi group3(GroupedOpenApi.Builder builder) { + return builder + .group("group3") + .addOpenApiCustomiser(openApi -> openApi.getServers().add(new Server().url("urlGroup3"))) + .addOperationCustomizer((operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerGroup3"))) + .addOpenApiMethodFilter(method -> !Objects.equals(method.getName(), "group3Filtered")) + .build(); + } + + } + + @Test + public void testGroup1() throws Exception { + webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/group1").exchange() + .expectStatus().isOk() + .expectBody().json(getContent("results/app184-1.json"), true); + } + + @Test + public void testGroup2() throws Exception { + webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/group2").exchange() + .expectStatus().isOk() + .expectBody().json(getContent("results/app184-2.json"), true); + } + + @Test + public void testGroup3() throws Exception { + webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/group3").exchange() + .expectStatus().isOk() + .expectBody().json(getContent("results/app184-3.json"), true); + } + +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/test/resources/results/app184-1.json b/springdoc-openapi-webflux-core/src/test/resources/results/app184-1.json new file mode 100644 index 000000000..45c4c8fe7 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/resources/results/app184-1.json @@ -0,0 +1,97 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + } + ], + "paths": { + "/group3Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group3Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group2Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group2Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/beanFiltered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "beanFiltered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/test/resources/results/app184-2.json b/springdoc-openapi-webflux-core/src/test/resources/results/app184-2.json new file mode 100644 index 000000000..84456de29 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/resources/results/app184-2.json @@ -0,0 +1,112 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + }, + { + "url": "urlGroup2" + } + ], + "paths": { + "/group3Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group3Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup2", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group1Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group1Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup2", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/beanFiltered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "beanFiltered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup2", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/test/resources/results/app184-3.json b/springdoc-openapi-webflux-core/src/test/resources/results/app184-3.json new file mode 100644 index 000000000..3fd154b16 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/resources/results/app184-3.json @@ -0,0 +1,112 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + }, + { + "url": "urlGroup3" + } + ], + "paths": { + "/group2Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group2Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup3", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group1Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group1Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup3", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/beanFiltered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "beanFiltered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup3", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webflux-core/src/test/resources/results/app184.json b/springdoc-openapi-webflux-core/src/test/resources/results/app184.json new file mode 100644 index 000000000..bfa5b82d0 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/resources/results/app184.json @@ -0,0 +1,112 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + }, + { + "url": "urlBean" + } + ], + "paths": { + "/group3Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group3Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group2Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group2Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group1Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group1Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/HelloController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/HelloController.java new file mode 100644 index 000000000..a38bd2590 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/HelloController.java @@ -0,0 +1,53 @@ +/* + * + * * Copyright 2019-2022 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 test.org.springdoc.api.app184; + + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/globalBeanFiltered") + public String globalBeanFiltered() { + return "globalBeanFiltered"; + } + + @GetMapping("/beanFiltered") + public String beanFiltered() { + return "beanFiltered"; + } + + @GetMapping("/group1Filtered") + public String group1Filtered() { + return "group1Filtered"; + } + + @GetMapping("/group2Filtered") + public String group2Filtered() { + return "group2Filtered"; + } + + @GetMapping("/group3Filtered") + public String group3Filtered() { + return "group3Filtered"; + } + +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java new file mode 100644 index 000000000..085145626 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app184/SpringDocAppapp184Test.java @@ -0,0 +1,126 @@ +/* + * + * * Copyright 2019-2020 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 test.org.springdoc.api.app184; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.Objects; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.customizers.GlobalOpenApiCustomiser; +import org.springdoc.core.customizers.GlobalOperationCustomizer; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.filters.GlobalOpenApiMethodFilter; +import org.springdoc.core.filters.OpenApiMethodFilter; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.TestPropertySource; + +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import io.swagger.v3.oas.models.servers.Server; +import test.org.springdoc.api.AbstractSpringDocTest; + +@TestPropertySource(properties = { + "springdoc.group-configs[0].group=group1", + "springdoc.group-configs[0].paths-to-exclude=/group1Filtered", +}) +public class SpringDocAppapp184Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringDocTestApp { + + @Bean + public GlobalOpenApiCustomiser addUrlGlobalBean() { + return openApi -> openApi.getServers().add(new Server().url("urlGlobalBean")); + } + + @Bean + public OpenApiCustomiser addUrlBean() { + return openApi -> openApi.getServers().add(new Server().url("urlBean")); + } + + @Bean + public GlobalOperationCustomizer addHeaderGlobaBeanl() { + return (operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerGlobalBean")); + } + + @Bean + public OperationCustomizer addHeaderBean() { + return (operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerBean")); + } + + @Bean + public GlobalOpenApiMethodFilter globalFilterBean() { + return method -> !Objects.equals(method.getName(), "globalBeanFiltered"); + } + + @Bean + public OpenApiMethodFilter filterBean() { + return method -> !Objects.equals(method.getName(), "beanFiltered"); + } + + @Bean + public GroupedOpenApi group2(GroupedOpenApi.Builder builder) { + return builder + .group("group2") + .addOpenApiCustomiser(openApi -> openApi.getServers().add(new Server().url("urlGroup2"))) + .addOperationCustomizer((operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerGroup2"))) + .addOpenApiMethodFilter(method -> !Objects.equals(method.getName(), "group2Filtered")) + .build(); + } + + @Bean + public GroupedOpenApi group3(GroupedOpenApi.Builder builder) { + return builder + .group("group3") + .addOpenApiCustomiser(openApi -> openApi.getServers().add(new Server().url("urlGroup3"))) + .addOperationCustomizer((operation, handlerMethod) -> operation.addParametersItem(new HeaderParameter().name("headerGroup3"))) + .addOpenApiMethodFilter(method -> !Objects.equals(method.getName(), "group3Filtered")) + .build(); + } + + } + + @Test + public void testGroup1() throws Exception { + mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL + "/group1")) + .andExpect(status().isOk()) + .andExpect(content().json(getContent("results/app184-1.json"), true)); + } + + @Test + public void testGroup2() throws Exception { + mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL + "/group2")) + .andExpect(status().isOk()) + .andExpect(content().json(getContent("results/app184-2.json"), true)); + } + + @Test + public void testGroup3() throws Exception { + mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL + "/group3")) + .andExpect(status().isOk()) + .andExpect(content().json(getContent("results/app184-3.json"), true)); + } + +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/app184-1.json b/springdoc-openapi-webmvc-core/src/test/resources/results/app184-1.json new file mode 100644 index 000000000..5254e3582 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/app184-1.json @@ -0,0 +1,97 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + } + ], + "paths": { + "/group3Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group3Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group2Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group2Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/beanFiltered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "beanFiltered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/app184-2.json b/springdoc-openapi-webmvc-core/src/test/resources/results/app184-2.json new file mode 100644 index 000000000..79d588188 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/app184-2.json @@ -0,0 +1,112 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + }, + { + "url": "urlGroup2" + } + ], + "paths": { + "/group3Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group3Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup2", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group1Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group1Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup2", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/beanFiltered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "beanFiltered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup2", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/app184-3.json b/springdoc-openapi-webmvc-core/src/test/resources/results/app184-3.json new file mode 100644 index 000000000..fd621e084 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/app184-3.json @@ -0,0 +1,112 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + }, + { + "url": "urlGroup3" + } + ], + "paths": { + "/group2Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group2Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup3", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group1Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group1Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup3", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/beanFiltered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "beanFiltered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerGroup3", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/app184.json b/springdoc-openapi-webmvc-core/src/test/resources/results/app184.json new file mode 100644 index 000000000..771d9dbac --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/app184.json @@ -0,0 +1,112 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + }, + { + "url": "urlGlobalBean" + }, + { + "url": "urlBean" + } + ], + "paths": { + "/group3Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group3Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group2Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group2Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/group1Filtered": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "group1Filtered", + "parameters": [ + { + "name": "headerGlobalBean", + "in": "header" + }, + { + "name": "headerBean", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} \ No newline at end of file