1
18
19 package org.springdoc.core;
20
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.stream.Collectors;
26
27 import com.fasterxml.jackson.databind.node.ObjectNode;
28 import io.swagger.v3.core.converter.ModelConverter;
29 import io.swagger.v3.oas.models.Components;
30 import io.swagger.v3.oas.models.OpenAPI;
31 import io.swagger.v3.oas.models.media.ObjectSchema;
32 import io.swagger.v3.oas.models.media.Schema;
33 import org.springdoc.core.converters.AdditionalModelsConverter;
34 import org.springdoc.core.converters.ModelConverterRegistrar;
35 import org.springdoc.core.converters.PropertyCustomizingConverter;
36 import org.springdoc.core.converters.ResponseSupportConverter;
37 import org.springdoc.core.customizers.OpenApiBuilderCustomiser;
38 import org.springdoc.core.customizers.OpenApiCustomiser;
39 import org.springdoc.core.customizers.PropertyCustomizer;
40
41 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
42 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
43 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
44 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
45 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
46 import org.springframework.boot.context.properties.bind.BindResult;
47 import org.springframework.boot.context.properties.bind.Binder;
48 import org.springframework.context.ApplicationContext;
49 import org.springframework.context.annotation.Bean;
50 import org.springframework.context.annotation.Conditional;
51 import org.springframework.context.annotation.Configuration;
52 import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
53 import org.springframework.core.env.Environment;
54 import org.springframework.util.CollectionUtils;
55
56 import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
57 import static org.springdoc.core.Constants.SPRINGDOC_PREFIX;
58 import static org.springdoc.core.Constants.SPRINGDOC_SCHEMA_RESOLVE_PROPERTIES;
59 import static org.springdoc.core.SpringDocUtils.getConfig;
60 import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
61
62 @Configuration
63 @ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true)
64 public class SpringDocConfiguration {
65
66 static {
67 getConfig().replaceWithSchema(ObjectNode.class, new ObjectSchema());
68 }
69
70 @Bean
71 LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer() {
72 return new LocalVariableTableParameterNameDiscoverer();
73 }
74
75 @Bean
76 AdditionalModelsConverter pageableSupportConverter() {
77 return new AdditionalModelsConverter();
78 }
79
80 @Bean
81 PropertyCustomizingConverter propertyCustomizingConverter(Optional<List<PropertyCustomizer>> customizers) {
82 return new PropertyCustomizingConverter(customizers);
83 }
84
85 @Bean
86 @ConditionalOnMissingBean
87 ResponseSupportConverter responseSupportConverter() {
88 return new ResponseSupportConverter();
89 }
90
91 @Bean
92 @ConditionalOnMissingBean
93 OpenAPIBuilder openAPIBuilder(Optional<OpenAPI> openAPI, ApplicationContext context,
94 SecurityParser securityParser,
95 SpringDocConfigProperties springDocConfigProperties,
96 Optional<List<OpenApiBuilderCustomiser>> openApiBuilderCustomisers) {
97 return new OpenAPIBuilder(openAPI, context, securityParser, springDocConfigProperties, openApiBuilderCustomisers);
98 }
99
100 @Bean
101 ModelConverterRegistrar modelConverterRegistrar(Optional<List<ModelConverter>> modelConverters) {
102 return new ModelConverterRegistrar(modelConverters.orElse(Collections.emptyList()));
103 }
104
105 @Bean
106 @ConditionalOnWebApplication
107 @ConditionalOnMissingBean
108 OperationBuilder operationBuilder(GenericParameterBuilder parameterBuilder, RequestBodyBuilder requestBodyBuilder,
109 SecurityParser securityParser, PropertyResolverUtils propertyResolverUtils) {
110 return new OperationBuilder(parameterBuilder, requestBodyBuilder,
111 securityParser, propertyResolverUtils);
112 }
113
114 @Bean
115 PropertyResolverUtils propertyResolverUtils(ConfigurableBeanFactory factory) {
116 return new PropertyResolverUtils(factory);
117 }
118
119 @Bean
120 @ConditionalOnWebApplication
121 @ConditionalOnMissingBean
122 RequestBodyBuilder requestBodyBuilder(GenericParameterBuilder parameterBuilder) {
123 return new RequestBodyBuilder(parameterBuilder);
124 }
125
126 @Bean
127 @ConditionalOnMissingBean
128 SecurityParser securityParser(PropertyResolverUtils propertyResolverUtils) {
129 return new SecurityParser(propertyResolverUtils);
130 }
131
132 @Bean
133 ReturnTypeParser genericReturnTypeParser() {
134 return new ReturnTypeParser() {};
135 }
136
137 @Bean
138 @ConditionalOnMissingBean
139 GenericParameterBuilder parameterBuilder(PropertyResolverUtils propertyResolverUtils) {
140 return new GenericParameterBuilder(propertyResolverUtils);
141 }
142
143 @Bean
144 @ConditionalOnProperty(SPRINGDOC_SCHEMA_RESOLVE_PROPERTIES)
145 OpenApiCustomiser propertiesResolverForSchema(PropertyResolverUtils propertyResolverUtils, OpenAPIBuilder openAPIBuilder) {
146 return openApi -> {
147 Components components = openApi.getComponents();
148 Map<String, Schema> schemas = components.getSchemas();
149 schemas.values().forEach(schema -> openAPIBuilder.resolveProperties(schema, propertyResolverUtils));
150 };
151 }
152
153 @Bean
154 @Conditional(CacheOrGroupedOpenApiCondition.class)
155 BeanFactoryPostProcessor springdocBeanFactoryPostProcessor(Environment environment) {
156 return beanFactory -> {
157 final BindResult<SpringDocConfigProperties> result = Binder.get(environment)
158 .bind(SPRINGDOC_PREFIX, SpringDocConfigProperties.class);
159 if (result.isBound()) {
160 SpringDocConfigProperties springDocGroupConfig = result.get();
161 List<GroupedOpenApi> groupedOpenApis = springDocGroupConfig.getGroupConfigs().stream()
162 .map(elt -> {
163 GroupedOpenApi.Builder builder = GroupedOpenApi.builder();
164 if (!CollectionUtils.isEmpty(elt.getPackagesToScan()))
165 builder.packagesToScan(elt.getPackagesToScan().toArray(new String[0]));
166 if (!CollectionUtils.isEmpty(elt.getPathsToMatch()))
167 builder.pathsToMatch(elt.getPathsToMatch().toArray(new String[0]));
168 return builder.setGroup(elt.getGroup()).build();
169 })
170 .collect(Collectors.toList());
171 groupedOpenApis.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt));
172 }
173 for (String beanName : beanFactory.getBeanNamesForType(OpenAPIBuilder.class))
174 beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
175 for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class))
176 beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
177 };
178 }
179 }
180