Skip to content

Commit a382dd3

Browse files
author
bnasslahsen
committed
Add support of swagger annotations @securityrequirement and @tag on @repository interface. Fixes #837.
1 parent 9ecb59e commit a382dd3

File tree

14 files changed

+2116
-111
lines changed

14 files changed

+2116
-111
lines changed

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

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package org.springdoc.core;
2222

23+
import java.lang.reflect.Method;
2324
import java.util.ArrayList;
2425
import java.util.Collections;
2526
import java.util.HashMap;
@@ -253,33 +254,11 @@ public void setServersPresent(boolean serversPresent) {
253254
*/
254255
public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI) {
255256

256-
// class tags
257-
Set<Tags> tagsSet = AnnotatedElementUtils
258-
.findAllMergedAnnotations(handlerMethod.getBeanType(), Tags.class);
259-
Set<Tag> classTags = tagsSet.stream()
260-
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
261-
classTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(handlerMethod.getBeanType(), Tag.class));
262-
263-
// method tags
264-
tagsSet = AnnotatedElementUtils
265-
.findAllMergedAnnotations(handlerMethod.getMethod(), Tags.class);
266-
Set<Tag> methodTags = tagsSet.stream()
267-
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
268-
methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(handlerMethod.getMethod(), Tag.class));
269-
270-
271-
List<Tag> allTags = new ArrayList<>();
257+
Set<io.swagger.v3.oas.models.tags.Tag> tags = new HashSet<>();
272258
Set<String> tagsStr = new HashSet<>();
273259

274-
if (!CollectionUtils.isEmpty(methodTags)) {
275-
tagsStr.addAll(methodTags.stream().map(Tag::name).collect(Collectors.toSet()));
276-
allTags.addAll(methodTags);
277-
}
278-
279-
if (!CollectionUtils.isEmpty(classTags)) {
280-
tagsStr.addAll(classTags.stream().map(Tag::name).collect(Collectors.toSet()));
281-
allTags.addAll(classTags);
282-
}
260+
buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr);
261+
buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr);
283262

284263
if (springdocTags.containsKey(handlerMethod)) {
285264
io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod);
@@ -289,16 +268,18 @@ public Operation buildTags(HandlerMethod handlerMethod, Operation operation, Ope
289268
}
290269
}
291270

292-
Optional<Set<io.swagger.v3.oas.models.tags.Tag>> tags = AnnotationsUtils
293-
.getTags(allTags.toArray(new Tag[0]), true);
271+
if (!CollectionUtils.isEmpty(tagsStr))
272+
operation.setTags(new ArrayList<>(tagsStr));
273+
274+
if (isAutoTagClasses(operation))
275+
operation.addTagsItem(splitCamelCase(handlerMethod.getBeanType().getSimpleName()));
294276

295-
if (tags.isPresent()) {
296-
Set<io.swagger.v3.oas.models.tags.Tag> tagSet = tags.get();
277+
if (!CollectionUtils.isEmpty(tags)) {
297278
// Existing tags
298279
List<io.swagger.v3.oas.models.tags.Tag> openApiTags = openAPI.getTags();
299280
if (!CollectionUtils.isEmpty(openApiTags))
300-
tagSet.addAll(openApiTags);
301-
openAPI.setTags(new ArrayList<>(tagSet));
281+
tags.addAll(openApiTags);
282+
openAPI.setTags(new ArrayList<>(tags));
302283
}
303284

304285
// Handle SecurityRequirement at operation level
@@ -310,14 +291,39 @@ public Operation buildTags(HandlerMethod handlerMethod, Operation operation, Ope
310291
else
311292
securityParser.buildSecurityRequirement(securityRequirements, operation);
312293
}
313-
if (!CollectionUtils.isEmpty(tagsStr))
314-
operation.setTags(new ArrayList<>(tagsStr));
315294

295+
return operation;
296+
}
316297

317-
if (isAutoTagClasses(operation))
318-
operation.addTagsItem(splitCamelCase(handlerMethod.getBeanType().getSimpleName()));
298+
private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr) {
299+
// method tags
300+
Set<Tags> tagsSet = AnnotatedElementUtils
301+
.findAllMergedAnnotations(method, Tags.class);
302+
Set<Tag> methodTags = tagsSet.stream()
303+
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
304+
methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, Tag.class));
305+
if (!CollectionUtils.isEmpty(methodTags)) {
306+
tagsStr.addAll(methodTags.stream().map(Tag::name).collect(Collectors.toSet()));
307+
List<Tag> allTags = new ArrayList<>(methodTags);
308+
AnnotationsUtils
309+
.getTags(allTags.toArray(new Tag[0]), true).ifPresent(tags::addAll);
310+
}
311+
}
319312

320-
return operation;
313+
public void buildTagsFromClass(Class<?> beanType, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr) {
314+
List<Tag> allTags = new ArrayList<>();
315+
// class tags
316+
Set<Tags> tagsSet = AnnotatedElementUtils
317+
.findAllMergedAnnotations(beanType, Tags.class);
318+
Set<Tag> classTags = tagsSet.stream()
319+
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
320+
classTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(beanType, Tag.class));
321+
if (!CollectionUtils.isEmpty(classTags)) {
322+
tagsStr.addAll(classTags.stream().map(Tag::name).collect(Collectors.toSet()));
323+
allTags.addAll(classTags);
324+
AnnotationsUtils
325+
.getTags(allTags.toArray(new Tag[0]), true).ifPresent(tags::addAll);
326+
}
321327
}
322328

323329
/**
@@ -654,4 +660,8 @@ public void resetCalculatedOpenAPI() {
654660
public ApplicationContext getContext() {
655661
return context;
656662
}
663+
664+
public SecurityParser getSecurityParser() {
665+
return securityParser;
666+
}
657667
}

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

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package org.springdoc.core;
2222

23+
import java.lang.reflect.Method;
2324
import java.util.ArrayList;
2425
import java.util.Arrays;
2526
import java.util.HashSet;
@@ -47,7 +48,7 @@
4748
* The type Security parser.
4849
* @author bnasslahsen
4950
*/
50-
class SecurityParser {
51+
public class SecurityParser {
5152

5253
/**
5354
* The Property resolver utils.
@@ -111,38 +112,50 @@ private static boolean isEmpty(OAuthScope[] scopes) {
111112
/**
112113
* Get security requirements io . swagger . v 3 . oas . annotations . security . security requirement [ ].
113114
*
114-
* @param method the method
115+
* @param handlerMethod the handlerMethod
115116
* @return the io . swagger . v 3 . oas . annotations . security . security requirement [ ]
116117
*/
117118
public io.swagger.v3.oas.annotations.security.SecurityRequirement[] getSecurityRequirements(
118-
HandlerMethod method) {
119+
HandlerMethod handlerMethod) {
119120
// class SecurityRequirements
120-
io.swagger.v3.oas.annotations.security.SecurityRequirements classSecurity = AnnotatedElementUtils.findMergedAnnotation(method.getBeanType(), io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
121-
// method SecurityRequirements
122-
io.swagger.v3.oas.annotations.security.SecurityRequirements methodSecurity = AnnotatedElementUtils.findMergedAnnotation(method.getMethod(), io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
121+
Class<?> beanType = handlerMethod.getBeanType();
122+
Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> allSecurityTags = getSecurityRequirementsForClass(beanType);
123123

124-
Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> allSecurityTags = null;
124+
// handlerMethod SecurityRequirements
125+
Method method = handlerMethod.getMethod();
126+
allSecurityTags = getSecurityRequirementsForMethod(method,allSecurityTags);
125127

126-
if (classSecurity != null)
127-
allSecurityTags = new HashSet<>(Arrays.asList(classSecurity.value()));
128+
return (allSecurityTags != null) ? allSecurityTags.toArray(new io.swagger.v3.oas.annotations.security.SecurityRequirement[0]) : null;
129+
}
130+
131+
private Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> getSecurityRequirementsForMethod(Method method,Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> allSecurityTags) {
132+
io.swagger.v3.oas.annotations.security.SecurityRequirements methodSecurity = AnnotatedElementUtils.findMergedAnnotation(method, io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
128133
if (methodSecurity != null)
129134
allSecurityTags = addSecurityRequirements(allSecurityTags, new HashSet<>(Arrays.asList(methodSecurity.value())));
135+
if (CollectionUtils.isEmpty(allSecurityTags)) {
136+
// handlerMethod SecurityRequirement
137+
Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> securityRequirementsMethodList = AnnotatedElementUtils.findMergedRepeatableAnnotations(method,
138+
io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
139+
if (!CollectionUtils.isEmpty(securityRequirementsMethodList))
140+
allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsMethodList);
141+
}
142+
return allSecurityTags;
143+
}
130144

145+
public Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> getSecurityRequirementsForClass(Class<?> beanType) {
146+
Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> allSecurityTags =null;
147+
io.swagger.v3.oas.annotations.security.SecurityRequirements classSecurity = AnnotatedElementUtils.findMergedAnnotation(beanType, io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
148+
if (classSecurity != null)
149+
allSecurityTags = new HashSet<>(Arrays.asList(classSecurity.value()));
131150
if (CollectionUtils.isEmpty(allSecurityTags)) {
132151
// class SecurityRequirement
133152
Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> securityRequirementsClassList = AnnotatedElementUtils.findMergedRepeatableAnnotations(
134-
method.getBeanType(),
135-
io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
136-
// method SecurityRequirement
137-
Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> securityRequirementsMethodList = AnnotatedElementUtils.findMergedRepeatableAnnotations(method.getMethod(),
153+
beanType,
138154
io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
139155
if (!CollectionUtils.isEmpty(securityRequirementsClassList))
140156
allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsClassList);
141-
if (!CollectionUtils.isEmpty(securityRequirementsMethodList))
142-
allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsMethodList);
143157
}
144-
145-
return (allSecurityTags != null) ? allSecurityTags.toArray(new io.swagger.v3.oas.annotations.security.SecurityRequirement[0]) : null;
158+
return allSecurityTags;
146159
}
147160

148161
/**

springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/SpringRepositoryRestResourceProvider.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springdoc.api.AbstractOpenApiResource;
3434
import org.springdoc.core.RepositoryRestResourceProvider;
3535
import org.springdoc.core.fn.RouterOperation;
36+
import org.springdoc.data.rest.core.DataRestRepository;
3637
import org.springdoc.data.rest.core.DataRestRouterOperationBuilder;
3738

3839
import org.springframework.data.repository.support.Repositories;
@@ -123,6 +124,8 @@ public List<RouterOperation> getRouterOperations(OpenAPI openAPI) {
123124
List<RouterOperation> routerOperationList = new ArrayList<>();
124125
List<HandlerMapping> handlerMappingList = delegatingHandlerMapping.getDelegates();
125126
for (Class<?> domainType : repositories) {
127+
Class<?> repository = repositories.getRequiredRepositoryInformation(domainType).getRepositoryInterface();
128+
DataRestRepository dataRestRepository = new DataRestRepository(domainType, repository);
126129
ResourceMetadata resourceMetadata = mappings.getMetadataFor(domainType);
127130
if (resourceMetadata.isExported()) {
128131
for (HandlerMapping handlerMapping : handlerMappingList) {
@@ -135,9 +138,9 @@ public List<RouterOperation> getRouterOperations(OpenAPI openAPI) {
135138
.getValue().getBeanType().getName()))
136139
.filter(controller -> !AbstractOpenApiResource.isHiddenRestControllers(controller.getValue().getBeanType()))
137140
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a1, a2) -> a1));
138-
findControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, domainType, openAPI);
141+
findControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, dataRestRepository, openAPI);
139142
}
140-
else if (handlerMapping instanceof BasePathAwareHandlerMapping) {
143+
else if (handlerMapping instanceof BasePathAwareHandlerMapping) {
141144
BasePathAwareHandlerMapping beanBasePathAwareHandlerMapping = (BasePathAwareHandlerMapping) handlerMapping;
142145
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = beanBasePathAwareHandlerMapping.getHandlerMethods();
143146
Map<RequestMappingInfo, HandlerMethod> handlerMethodMapFiltered = handlerMethodMap.entrySet().stream()
@@ -146,7 +149,7 @@ else if (handlerMapping instanceof BasePathAwareHandlerMapping) {
146149
.filter(controller -> !AbstractOpenApiResource.isHiddenRestControllers(controller.getValue().getBeanType()))
147150
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a1, a2) -> a1));
148151

149-
findControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, domainType, openAPI);
152+
findControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, dataRestRepository, openAPI);
150153
handlerMethodMapFiltered = handlerMethodMap.entrySet().stream()
151154
.filter(requestMappingInfoHandlerMethodEntry -> ProfileController.class.equals(requestMappingInfoHandlerMethodEntry
152155
.getValue().getBeanType()) || AlpsController.class.equals(requestMappingInfoHandlerMethodEntry
@@ -157,7 +160,7 @@ else if (handlerMapping instanceof BasePathAwareHandlerMapping) {
157160
}
158161
}
159162
// search
160-
findSearchResourceMappings(openAPI, routerOperationList, handlerMappingList, domainType, resourceMetadata);
163+
findSearchResourceMappings(openAPI, routerOperationList, handlerMappingList, dataRestRepository, resourceMetadata);
161164
}
162165
return routerOperationList;
163166
}
@@ -168,10 +171,10 @@ else if (handlerMapping instanceof BasePathAwareHandlerMapping) {
168171
* @param openAPI the open api
169172
* @param routerOperationList the router operation list
170173
* @param handlerMappingList the handler mapping list
171-
* @param domainType the domain type
174+
* @param dataRestRepository the repository data rest
172175
* @param resourceMetadata the resource metadata
173176
*/
174-
private void findSearchResourceMappings(OpenAPI openAPI, List<RouterOperation> routerOperationList, List<HandlerMapping> handlerMappingList, Class<?> domainType, ResourceMetadata resourceMetadata) {
177+
private void findSearchResourceMappings(OpenAPI openAPI, List<RouterOperation> routerOperationList, List<HandlerMapping> handlerMappingList, DataRestRepository dataRestRepository, ResourceMetadata resourceMetadata) {
175178
for (HandlerMapping handlerMapping : handlerMappingList) {
176179
if (handlerMapping instanceof RepositoryRestHandlerMapping) {
177180
RepositoryRestHandlerMapping repositoryRestHandlerMapping = (RepositoryRestHandlerMapping) handlerMapping;
@@ -181,10 +184,10 @@ private void findSearchResourceMappings(OpenAPI openAPI, List<RouterOperation> r
181184
.getValue().getBeanType().getName()))
182185
.filter(controller -> !AbstractOpenApiResource.isHiddenRestControllers(controller.getValue().getBeanType()))
183186
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a1, a2) -> a1));
184-
ResourceMetadata metadata = associations.getMetadataFor(domainType);
187+
ResourceMetadata metadata = associations.getMetadataFor(dataRestRepository.getDomainType());
185188
SearchResourceMappings searchResourceMappings = metadata.getSearchResourceMappings();
186189
if (searchResourceMappings.isExported()) {
187-
findSearchControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, domainType, openAPI, searchResourceMappings);
190+
findSearchControllers(routerOperationList, handlerMethodMapFiltered, resourceMetadata, dataRestRepository, openAPI, searchResourceMappings);
188191
}
189192
}
190193
}
@@ -196,16 +199,16 @@ private void findSearchResourceMappings(OpenAPI openAPI, List<RouterOperation> r
196199
* @param routerOperationList the router operation list
197200
* @param handlerMethodMap the handler method map
198201
* @param resourceMetadata the resource metadata
199-
* @param domainType the domain type
202+
* @param dataRestRepository the repository data rest
200203
* @param openAPI the open api
201204
* @param searchResourceMappings the search resource mappings
202205
* @return the list
203206
*/
204207
private List<RouterOperation> findSearchControllers(List<RouterOperation> routerOperationList,
205-
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap, ResourceMetadata resourceMetadata, Class<?> domainType, OpenAPI openAPI, SearchResourceMappings searchResourceMappings) {
208+
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap, ResourceMetadata resourceMetadata, DataRestRepository dataRestRepository, OpenAPI openAPI, SearchResourceMappings searchResourceMappings) {
206209
Stream<MethodResourceMapping> methodResourceMappingStream = searchResourceMappings.getExportedMappings();
207210
methodResourceMappingStream.forEach(methodResourceMapping -> dataRestRouterOperationBuilder.buildSearchRouterOperationList(
208-
routerOperationList, handlerMethodMap, resourceMetadata, domainType, openAPI, methodResourceMapping));
211+
routerOperationList, handlerMethodMap, resourceMetadata, dataRestRepository, openAPI, methodResourceMapping));
209212
return routerOperationList;
210213
}
211214

@@ -216,16 +219,16 @@ private List<RouterOperation> findSearchControllers(List<RouterOperation> router
216219
* @param routerOperationList the router operation list
217220
* @param handlerMethodMap the handler method map
218221
* @param resourceMetadata the resource metadata
219-
* @param domainType the domain type
222+
* @param dataRestRepository the repository data rest
220223
* @param openAPI the open api
221224
* @return the list
222225
*/
223226
private List<RouterOperation> findControllers
224-
(List<RouterOperation> routerOperationList,
225-
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap, ResourceMetadata resourceMetadata,
226-
Class<?> domainType, OpenAPI openAPI) {
227+
(List<RouterOperation> routerOperationList,
228+
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap, ResourceMetadata resourceMetadata,
229+
DataRestRepository dataRestRepository, OpenAPI openAPI) {
227230
dataRestRouterOperationBuilder.buildEntityRouterOperationList(routerOperationList, handlerMethodMap, resourceMetadata,
228-
domainType, openAPI);
231+
dataRestRepository, openAPI);
229232
return routerOperationList;
230233
}
231234

0 commit comments

Comments
 (0)