Skip to content

Add global customizer and filters #1616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +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.OperationCustomizer;
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;
Expand Down Expand Up @@ -85,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;
Expand All @@ -109,6 +112,7 @@
/**
* The type Spring doc configuration.
* @author bnasslahsen
* @author christophejan
*/
@Lazy(false)
@Configuration(proxyBeanMethods = false)
Expand Down Expand Up @@ -359,7 +363,7 @@ OpenApiCustomiser propertiesResolverForSchema(PropertyResolverUtils propertyReso
@Conditional(CacheOrGroupedOpenApiCondition.class)
@ConditionalOnClass(name = BINDRESULT_CLASS)
@Lazy(false)
static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor() {
static SpringdocBeanFactoryConfigurer springdocBeanFactoryPostProcessor() {
return new SpringdocBeanFactoryConfigurer();
}

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

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public GroupedOpenApi.Builder groupedOpenApiBuilder(List<GlobalOpenApiCustomiser> globalOpenApiCustomisers, List<GlobalOperationCustomizer> globalOperationCustomizers,
List<GlobalOpenApiMethodFilter> 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
Expand Down Expand Up @@ -428,15 +444,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 BeanFactoryPostProcessor springdocBeanFactoryPostProcessor3(List<GroupedOpenApi> groupedOpenApis) {
return new SpringdocActuatorBeanFactoryConfigurer(groupedOpenApis);
static SpringdocActuatorBeanFactoryConfigurer springdocBeanFactoryPostProcessor3() {
return new SpringdocActuatorBeanFactoryConfigurer();
}

/**
Expand All @@ -447,7 +462,7 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor3(List<GroupedO
@Bean
@Lazy(false)
@ConditionalOnManagementPort(ManagementPortType.SAME)
OperationCustomizer actuatorCustomizer() {
ActuatorOperationCustomizer actuatorCustomizer() {
return new ActuatorOperationCustomizer();
}

Expand All @@ -460,7 +475,7 @@ OperationCustomizer actuatorCustomizer() {
@Bean
@Lazy(false)
@ConditionalOnManagementPort(ManagementPortType.SAME)
OpenApiCustomiser actuatorOpenApiCustomiser(WebEndpointProperties webEndpointProperties) {
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser(WebEndpointProperties webEndpointProperties) {
return new ActuatorOpenApiCustomizer(webEndpointProperties);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -44,58 +47,99 @@
/**
* 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<GroupedOpenApi> groupedOpenApis;
protected ApplicationContext applicationContext;

/**
* Instantiates a new Springdoc actuator bean factory configurer.
*
* @param groupedOpenApis the grouped open apis
*/
public SpringdocActuatorBeanFactoryConfigurer(List<GroupedOpenApi> groupedOpenApis) {
this.groupedOpenApis = groupedOpenApis;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
final BindResult<WebEndpointProperties> result = Binder.get(environment)
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
final BindResult<WebEndpointProperties> result = Binder.get(applicationContext.getEnvironment())
.bind(MANAGEMENT_ENDPOINTS_WEB, WebEndpointProperties.class);
if (result.isBound()) {
WebEndpointProperties webEndpointProperties = result.get();

List<GroupedOpenApi> 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(new RuntimeBeanReference(GroupedOpenApi.Builder.class))
.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(new RuntimeBeanReference(GroupedOpenApi.Builder.class))
.addConstructorArgValue(webEndpointProperties.getBasePath())
.getBeanDefinition());
}
}
initBeanFactoryPostProcessor(beanFactory);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
SpringdocBeanFactoryConfigurer.initBeanFactoryPostProcessor(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(GroupedOpenApi.Builder builder, String actuatorBasePath,
ActuatorOpenApiCustomizer actuatorOpenApiCustomiser, ActuatorOperationCustomizer actuatorOperationCustomizer) {
return builder.group(ACTUATOR_DEFAULT_GROUP)
.pathsToMatch(actuatorBasePath + ALL_PATTERN)
.pathsToExclude(actuatorBasePath + HEALTH_PATTERN)
.addOpenApiCustomiser(actuatorOpenApiCustomiser)
.addOperationCustomizer(actuatorOperationCustomizer)
.build();
}

/**
* 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(GroupedOpenApi.Builder builder, String actuatorBasePath) {
return builder.group(DEFAULT_GROUP_NAME)
.pathsToMatch(ALL_PATTERN)
.pathsToExclude(actuatorBasePath + ALL_PATTERN)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -23,18 +23,19 @@

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.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.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;
Expand All @@ -43,41 +44,58 @@
/**
* 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<SpringDocConfigProperties> result = Binder.get(environment)
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
final BindResult<SpringDocConfigProperties> result = Binder.get(applicationContext.getEnvironment())
.bind(SPRINGDOC_PREFIX, SpringDocConfigProperties.class);
if (result.isBound()) {
SpringDocConfigProperties springDocGroupConfig = result.get();
List<GroupedOpenApi> 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(new RuntimeBeanReference(GroupedOpenApi.Builder.class))
.addConstructorArgValue(groupConfig)
.getBeanDefinition())
);
}
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
initBeanFactoryPostProcessor(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(GroupedOpenApi.Builder builder, GroupConfig groupConfig) {
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.
*
Expand Down
Loading