Skip to content

Commit da735b0

Browse files
committed
Merge branch 'feature/global-customizer-and-filters' of https://github.com/christophejan/springdoc-openapi into christophejan-feature/global-customizer-and-filters
2 parents 722b793 + 7d4b5f5 commit da735b0

27 files changed

+1591
-89
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@
4949
import org.springdoc.core.customizers.ActuatorOperationCustomizer;
5050
import org.springdoc.core.customizers.DataRestDelegatingMethodParameterCustomizer;
5151
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
52+
import org.springdoc.core.customizers.GlobalOpenApiCustomiser;
53+
import org.springdoc.core.customizers.GlobalOperationCustomizer;
5254
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
5355
import org.springdoc.core.customizers.OpenApiCustomiser;
54-
import org.springdoc.core.customizers.OperationCustomizer;
5556
import org.springdoc.core.customizers.PropertyCustomizer;
5657
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
58+
import org.springdoc.core.filters.GlobalOpenApiMethodFilter;
5759
import org.springdoc.core.providers.ActuatorProvider;
5860
import org.springdoc.core.providers.CloudFunctionProvider;
5961
import org.springdoc.core.providers.JavadocProvider;
@@ -85,6 +87,7 @@
8587
import org.springframework.context.annotation.Conditional;
8688
import org.springframework.context.annotation.Configuration;
8789
import org.springframework.context.annotation.Lazy;
90+
import org.springframework.context.annotation.Scope;
8891
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
8992
import org.springframework.core.convert.support.GenericConversionService;
9093
import org.springframework.data.domain.Pageable;
@@ -109,6 +112,7 @@
109112
/**
110113
* The type Spring doc configuration.
111114
* @author bnasslahsen
115+
* @author christophejan
112116
*/
113117
@Lazy(false)
114118
@Configuration(proxyBeanMethods = false)
@@ -359,7 +363,7 @@ OpenApiCustomiser propertiesResolverForSchema(PropertyResolverUtils propertyReso
359363
@Conditional(CacheOrGroupedOpenApiCondition.class)
360364
@ConditionalOnClass(name = BINDRESULT_CLASS)
361365
@Lazy(false)
362-
static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor() {
366+
static SpringdocBeanFactoryConfigurer springdocBeanFactoryPostProcessor() {
363367
return new SpringdocBeanFactoryConfigurer();
364368
}
365369

@@ -397,6 +401,18 @@ SpringDocProviders springDocProviders(Optional<ActuatorProvider> actuatorProvide
397401
return new SpringDocProviders(actuatorProvider, springCloudFunctionProvider, springSecurityOAuth2Provider, repositoryRestResourceProvider, routerFunctionProvider, springWebProvider);
398402
}
399403

404+
@Bean
405+
@Scope("prototype")
406+
@ConditionalOnMissingBean
407+
public GroupedOpenApi.Builder groupedOpenApiBuilder(List<GlobalOpenApiCustomiser> globalOpenApiCustomisers, List<GlobalOperationCustomizer> globalOperationCustomizers,
408+
List<GlobalOpenApiMethodFilter> globalOpenApiMethodFilters) {
409+
GroupedOpenApi.Builder builder = GroupedOpenApi.builder();
410+
globalOpenApiCustomisers.forEach(builder::addOpenApiCustomiser);
411+
globalOperationCustomizers.forEach(builder::addOperationCustomizer);
412+
globalOpenApiMethodFilters.forEach(builder::addOpenApiMethodFilter);
413+
return builder;
414+
}
415+
400416
/**
401417
* The type Open api resource advice.
402418
* @author bnasslashen
@@ -428,15 +444,14 @@ static class SpringDocActuatorConfiguration {
428444
/**
429445
* Springdoc bean factory post processor 3 bean factory post processor.
430446
*
431-
* @param groupedOpenApis the grouped open apis
432447
* @return the bean factory post processor
433448
*/
434449
@Bean
435450
@Lazy(false)
436451
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
437452
@Conditional(MultipleOpenApiSupportCondition.class)
438-
static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor3(List<GroupedOpenApi> groupedOpenApis) {
439-
return new SpringdocActuatorBeanFactoryConfigurer(groupedOpenApis);
453+
static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3() {
454+
return new SpringdocActuatorBeanFactoryConfigurer();
440455
}
441456

442457
/**
@@ -447,7 +462,7 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor3(List<GroupedO
447462
@Bean
448463
@Lazy(false)
449464
@ConditionalOnManagementPort(ManagementPortType.SAME)
450-
OperationCustomizer actuatorCustomizer() {
465+
ActuatorOperationCustomizer actuatorCustomizer() {
451466
return new ActuatorOperationCustomizer();
452467
}
453468

@@ -460,7 +475,7 @@ OperationCustomizer actuatorCustomizer() {
460475
@Bean
461476
@Lazy(false)
462477
@ConditionalOnManagementPort(ManagementPortType.SAME)
463-
OpenApiCustomiser actuatorOpenApiCustomiser(WebEndpointProperties webEndpointProperties) {
478+
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser(WebEndpointProperties webEndpointProperties) {
464479
return new ActuatorOpenApiCustomizer(webEndpointProperties);
465480
}
466481

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocActuatorBeanFactoryConfigurer.java

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* *
44
* * *
5-
* * * * Copyright 2019-2020 the original author or authors.
5+
* * * * Copyright 2019-2022 the original author or authors.
66
* * * *
77
* * * * Licensed under the Apache License, Version 2.0 (the "License");
88
* * * * you may not use this file except in compliance with the License.
@@ -23,17 +23,20 @@
2323

2424
package org.springdoc.core;
2525

26-
import java.util.ArrayList;
27-
import java.util.List;
28-
2926
import org.springdoc.core.customizers.ActuatorOpenApiCustomizer;
3027
import org.springdoc.core.customizers.ActuatorOperationCustomizer;
31-
28+
import org.springframework.beans.BeansException;
3229
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
30+
import org.springframework.beans.factory.config.RuntimeBeanReference;
31+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
32+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
33+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
3334
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
3435
import org.springframework.boot.context.properties.bind.BindResult;
3536
import org.springframework.boot.context.properties.bind.Binder;
36-
import org.springframework.util.CollectionUtils;
37+
import org.springframework.context.ApplicationContext;
38+
import org.springframework.context.ApplicationContextAware;
39+
import org.springframework.util.ObjectUtils;
3740

3841
import static org.springdoc.core.Constants.ACTUATOR_DEFAULT_GROUP;
3942
import static org.springdoc.core.Constants.ALL_PATTERN;
@@ -44,58 +47,99 @@
4447
/**
4548
* The type Springdoc bean factory configurer.
4649
* @author bnasslahsen
50+
* @author christophejan
4751
*/
48-
public class SpringdocActuatorBeanFactoryConfigurer extends SpringdocBeanFactoryConfigurer{
52+
public class SpringdocActuatorBeanFactoryConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
4953

5054
/**
51-
* The Grouped open apis.
55+
* The ApplicationContext.
5256
*/
53-
private List<GroupedOpenApi> groupedOpenApis;
57+
protected ApplicationContext applicationContext;
5458

55-
/**
56-
* Instantiates a new Springdoc actuator bean factory configurer.
57-
*
58-
* @param groupedOpenApis the grouped open apis
59-
*/
60-
public SpringdocActuatorBeanFactoryConfigurer(List<GroupedOpenApi> groupedOpenApis) {
61-
this.groupedOpenApis = groupedOpenApis;
59+
@Override
60+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
61+
this.applicationContext = applicationContext;
6262
}
6363

6464
@Override
65-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
66-
final BindResult<WebEndpointProperties> result = Binder.get(environment)
65+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
66+
final BindResult<WebEndpointProperties> result = Binder.get(applicationContext.getEnvironment())
6767
.bind(MANAGEMENT_ENDPOINTS_WEB, WebEndpointProperties.class);
6868
if (result.isBound()) {
6969
WebEndpointProperties webEndpointProperties = result.get();
7070

71-
List<GroupedOpenApi> newGroups = new ArrayList<>();
72-
73-
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser = new ActuatorOpenApiCustomizer(webEndpointProperties);
74-
beanFactory.registerSingleton("actuatorOpenApiCustomiser", actuatorOpenApiCustomiser);
75-
ActuatorOperationCustomizer actuatorCustomizer = new ActuatorOperationCustomizer();
76-
beanFactory.registerSingleton("actuatorCustomizer", actuatorCustomizer);
77-
78-
GroupedOpenApi actuatorGroup = GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP)
79-
.pathsToMatch(webEndpointProperties.getBasePath() + ALL_PATTERN)
80-
.pathsToExclude(webEndpointProperties.getBasePath() + HEALTH_PATTERN)
81-
.addOperationCustomizer(actuatorCustomizer)
82-
.addOpenApiCustomiser(actuatorOpenApiCustomiser)
83-
.build();
84-
// Add the actuator group
85-
newGroups.add(actuatorGroup);
86-
87-
if (CollectionUtils.isEmpty(groupedOpenApis)) {
88-
GroupedOpenApi defaultGroup = GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME)
89-
.pathsToMatch(ALL_PATTERN)
90-
.pathsToExclude(webEndpointProperties.getBasePath() + ALL_PATTERN)
91-
.build();
92-
// Register the default group
93-
newGroups.add(defaultGroup);
94-
}
71+
boolean addDefaultGroup = ObjectUtils.isEmpty(applicationContext
72+
.getBeanNamesForType(GroupedOpenApi.class, true, false));
73+
74+
registry.registerBeanDefinition("actuatorOpenApiCustomiser", BeanDefinitionBuilder
75+
.genericBeanDefinition(ActuatorOpenApiCustomizer.class)
76+
.addConstructorArgValue(webEndpointProperties)
77+
.getBeanDefinition());
78+
79+
registry.registerBeanDefinition("actuatorCustomizer", BeanDefinitionBuilder
80+
.genericBeanDefinition(ActuatorOperationCustomizer.class)
81+
.getBeanDefinition());
9582

96-
newGroups.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt));
83+
// register the actuator group bean definition
84+
registry.registerBeanDefinition(ACTUATOR_DEFAULT_GROUP, BeanDefinitionBuilder
85+
.genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class)
86+
.setFactoryMethod("actuatorGroupFactoryMethod")
87+
.addConstructorArgValue(new RuntimeBeanReference(GroupedOpenApi.Builder.class))
88+
.addConstructorArgValue(webEndpointProperties.getBasePath())
89+
.addConstructorArgValue(new RuntimeBeanReference(ActuatorOpenApiCustomizer.class))
90+
.addConstructorArgValue(new RuntimeBeanReference(ActuatorOperationCustomizer.class))
91+
.getBeanDefinition());
92+
93+
if (addDefaultGroup) {
94+
// register the default group bean definition
95+
registry.registerBeanDefinition(DEFAULT_GROUP_NAME, BeanDefinitionBuilder
96+
.genericBeanDefinition(SpringdocActuatorBeanFactoryConfigurer.class)
97+
.setFactoryMethod("defaultGroupFactoryMethod")
98+
.addConstructorArgValue(new RuntimeBeanReference(GroupedOpenApi.Builder.class))
99+
.addConstructorArgValue(webEndpointProperties.getBasePath())
100+
.getBeanDefinition());
101+
}
97102
}
98-
initBeanFactoryPostProcessor(beanFactory);
103+
}
104+
105+
@Override
106+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
107+
SpringdocBeanFactoryConfigurer.initBeanFactoryPostProcessor(beanFactory);
108+
}
109+
110+
/**
111+
* Actuator {@link GroupedOpenApi} factory method.
112+
*
113+
* @param builder the {@link GroupedOpenApi.Builder}
114+
* @param actuatorBasePath the actuator base path
115+
* @param actuatorOpenApiCustomiser the {@link ActuatorOpenApiCustomizer}
116+
* @param actuatorOperationCustomizer the {@link ActuatorOperationCustomizer}
117+
*
118+
* @return the actuator {@link GroupedOpenApi}
119+
*/
120+
public static GroupedOpenApi actuatorGroupFactoryMethod(GroupedOpenApi.Builder builder, String actuatorBasePath,
121+
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser, ActuatorOperationCustomizer actuatorOperationCustomizer) {
122+
return builder.group(ACTUATOR_DEFAULT_GROUP)
123+
.pathsToMatch(actuatorBasePath + ALL_PATTERN)
124+
.pathsToExclude(actuatorBasePath + HEALTH_PATTERN)
125+
.addOpenApiCustomiser(actuatorOpenApiCustomiser)
126+
.addOperationCustomizer(actuatorOperationCustomizer)
127+
.build();
128+
}
129+
130+
/**
131+
* Default {@link GroupedOpenApi} factory method.
132+
*
133+
* @param builder the {@link GroupedOpenApi.Builder}
134+
* @param actuatorBasePath the actuator base path
135+
*
136+
* @return the default {@link GroupedOpenApi}
137+
*/
138+
public static GroupedOpenApi defaultGroupFactoryMethod(GroupedOpenApi.Builder builder, String actuatorBasePath) {
139+
return builder.group(DEFAULT_GROUP_NAME)
140+
.pathsToMatch(ALL_PATTERN)
141+
.pathsToExclude(actuatorBasePath + ALL_PATTERN)
142+
.build();
99143
}
100144

101145
}

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* *
44
* * *
5-
* * * * Copyright 2019-2020 the original author or authors.
5+
* * * * Copyright 2019-2022 the original author or authors.
66
* * * *
77
* * * * Licensed under the Apache License, Version 2.0 (the "License");
88
* * * * you may not use this file except in compliance with the License.
@@ -23,18 +23,19 @@
2323

2424
package org.springdoc.core;
2525

26-
import java.util.List;
27-
import java.util.stream.Collectors;
28-
2926
import io.swagger.v3.oas.models.OpenAPI;
3027

31-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
28+
import org.springdoc.core.SpringDocConfigProperties.GroupConfig;
29+
import org.springframework.beans.BeansException;
3230
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
31+
import org.springframework.beans.factory.config.RuntimeBeanReference;
32+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
33+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
34+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
3335
import org.springframework.boot.context.properties.bind.BindResult;
3436
import org.springframework.boot.context.properties.bind.Binder;
35-
import org.springframework.context.EnvironmentAware;
36-
import org.springframework.core.env.Environment;
37-
import org.springframework.lang.Nullable;
37+
import org.springframework.context.ApplicationContext;
38+
import org.springframework.context.ApplicationContextAware;
3839
import org.springframework.util.CollectionUtils;
3940

4041
import static org.springdoc.core.Constants.SPRINGDOC_PREFIX;
@@ -43,41 +44,58 @@
4344
/**
4445
* The type Springdoc bean factory configurer.
4546
* @author bnasslahsen
47+
* @author christophejan
4648
*/
47-
public class SpringdocBeanFactoryConfigurer implements EnvironmentAware, BeanFactoryPostProcessor {
49+
public class SpringdocBeanFactoryConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
4850

4951
/**
50-
* The Environment.
52+
* The ApplicationContext.
5153
*/
52-
@Nullable
53-
protected Environment environment;
54+
protected ApplicationContext applicationContext;
5455

5556
@Override
56-
public void setEnvironment(Environment environment) {
57-
this.environment = environment;
57+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
58+
this.applicationContext = applicationContext;
5859
}
5960

6061
@Override
61-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
62-
final BindResult<SpringDocConfigProperties> result = Binder.get(environment)
62+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
63+
final BindResult<SpringDocConfigProperties> result = Binder.get(applicationContext.getEnvironment())
6364
.bind(SPRINGDOC_PREFIX, SpringDocConfigProperties.class);
6465
if (result.isBound()) {
65-
SpringDocConfigProperties springDocGroupConfig = result.get();
66-
List<GroupedOpenApi> groupedOpenApis = springDocGroupConfig.getGroupConfigs().stream()
67-
.map(elt -> {
68-
GroupedOpenApi.Builder builder = GroupedOpenApi.builder();
69-
if (!CollectionUtils.isEmpty(elt.getPackagesToScan()))
70-
builder.packagesToScan(elt.getPackagesToScan().toArray(new String[0]));
71-
if (!CollectionUtils.isEmpty(elt.getPathsToMatch()))
72-
builder.pathsToMatch(elt.getPathsToMatch().toArray(new String[0]));
73-
return builder.group(elt.getGroup()).build();
74-
})
75-
.collect(Collectors.toList());
76-
groupedOpenApis.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt));
66+
result.get().getGroupConfigs().stream().forEach(groupConfig ->
67+
// register bean definitions
68+
registry.registerBeanDefinition(groupConfig.getGroup(), BeanDefinitionBuilder
69+
.genericBeanDefinition(SpringdocBeanFactoryConfigurer.class)
70+
.setFactoryMethod("groupedOpenApisFactoryMethod")
71+
.addConstructorArgValue(new RuntimeBeanReference(GroupedOpenApi.Builder.class))
72+
.addConstructorArgValue(groupConfig)
73+
.getBeanDefinition())
74+
);
7775
}
76+
}
77+
78+
@Override
79+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
7880
initBeanFactoryPostProcessor(beanFactory);
7981
}
8082

83+
/**
84+
* {@link GroupedOpenApi} factory method from {@link GroupConfig}.
85+
*
86+
* @param builder the {@link GroupedOpenApi.Builder}
87+
* @param groupConfig the {@link GroupConfig}
88+
*
89+
* @return the {@link GroupedOpenApi}
90+
*/
91+
public static GroupedOpenApi groupedOpenApisFactoryMethod(GroupedOpenApi.Builder builder, GroupConfig groupConfig) {
92+
if (!CollectionUtils.isEmpty(groupConfig.getPackagesToScan()))
93+
builder.packagesToScan(groupConfig.getPackagesToScan().toArray(new String[0]));
94+
if (!CollectionUtils.isEmpty(groupConfig.getPathsToMatch()))
95+
builder.pathsToMatch(groupConfig.getPathsToMatch().toArray(new String[0]));
96+
return builder.group(groupConfig.getGroup()).build();
97+
}
98+
8199
/**
82100
* Init bean factory post processor.
83101
*

0 commit comments

Comments
 (0)