Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 10fd6dd

Browse files
io.swagger.v3.oas.annotations.Webhook does not work when defined on the method level. Fixes #2998
1 parent 31ed191 commit 10fd6dd

File tree

3 files changed

+116
-30
lines changed

3 files changed

+116
-30
lines changed

‎springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
package org.springdoc.core.service;
2828

29+
import java.lang.annotation.Annotation;
30+
import java.lang.reflect.AnnotatedElement;
2931
import java.lang.reflect.Method;
3032
import java.util.ArrayList;
3133
import java.util.Arrays;
@@ -51,6 +53,7 @@
5153
import io.swagger.v3.core.util.AnnotationsUtils;
5254
import io.swagger.v3.oas.annotations.Hidden;
5355
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
56+
import io.swagger.v3.oas.annotations.Webhook;
5457
import io.swagger.v3.oas.annotations.Webhooks;
5558
import io.swagger.v3.oas.annotations.security.SecuritySchemes;
5659
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -534,64 +537,96 @@ private Optional<OpenAPIDefinition> getOpenAPIDefinition() {
534537
return Optional.ofNullable(apiDef);
535538
}
536539

540+
537541
/**
538542
* Get webhooks webhooks [ ].
539543
*
540544
* @return the webhooks [ ]
541545
*/
542546
public Webhooks[] getWebhooks() {
543-
// List to collect all Webhooks annotations
544547
List<Webhooks> allWebhooks = new ArrayList<>();
545548

546-
// Get beans with @Webhooks annotation managed by Spring
547-
Map<String, Object> beansWithWebhooksAnnotation = context.getBeansWithAnnotation(Webhooks.class);
549+
// First: scan Spring-managed beans
550+
Map<String, Object> beans = context.getBeansWithAnnotation(Webhooks.class);
548551

549-
// Process Spring-managed beans
550-
if (!beansWithWebhooksAnnotation.isEmpty()) {
551-
beansWithWebhooksAnnotation.values().forEach(controller -> {
552-
// Get the @Webhooks annotation(s) from each bean
553-
Webhooks[] webhooksAnnotations = controller.getClass().getAnnotationsByType(Webhooks.class);
554-
allWebhooks.addAll(Arrays.asList(webhooksAnnotations));
555-
});
552+
for (Object bean : beans.values()) {
553+
Class<?> beanClass = bean.getClass();
554+
555+
// Collect @Webhooks or @Webhook on class level
556+
collectWebhooksFromElement(beanClass, allWebhooks);
557+
558+
// Collect from methods
559+
for (Method method : beanClass.getDeclaredMethods()) {
560+
collectWebhooksFromElement(method, allWebhooks);
561+
}
556562
}
557563

558-
// If no beans with @Webhooks annotation found, perform classpath scanning
564+
// Fallback: classpath scanning if nothing found
559565
if (allWebhooks.isEmpty()) {
560-
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
566+
ClassPathScanningCandidateComponentProvider scanner =
567+
new ClassPathScanningCandidateComponentProvider(false);
561568
scanner.addIncludeFilter(new AnnotationTypeFilter(Webhooks.class));
569+
scanner.addIncludeFilter(new AnnotationTypeFilter(Webhook.class));
562570

563-
// Scan base packages if available
564571
if (AutoConfigurationPackages.has(context)) {
565-
List<String> packagesToScan = AutoConfigurationPackages.get(context);
572+
for (String basePackage : AutoConfigurationPackages.get(context)) {
573+
Set<BeanDefinition> candidates = scanner.findCandidateComponents(basePackage);
566574

567-
for (String basePackage : packagesToScan) {
568-
// Perform the scan and get candidate components
569-
Set<BeanDefinition> components = scanner.findCandidateComponents(basePackage);
570-
571-
// Loop through the components
572-
for (BeanDefinition beanDefinition : components) {
575+
for (BeanDefinition bd : candidates) {
573576
try {
574-
// Get the class name and load the class
575-
String className = beanDefinition.getBeanClassName();
576-
Class<?> clazz = Class.forName(className);
577+
Class<?> clazz = Class.forName(bd.getBeanClassName());
578+
579+
// Class-level annotations
580+
collectWebhooksFromElement(clazz, allWebhooks);
577581

578-
// Get @Webhooks annotation from the class
579-
Webhooks[] webhooksAnnotations = clazz.getAnnotationsByType(Webhooks.class);
580-
allWebhooks.addAll(Arrays.asList(webhooksAnnotations));
582+
// Method-level annotations
583+
for (Method method : clazz.getDeclaredMethods()) {
584+
collectWebhooksFromElement(method, allWebhooks);
585+
}
581586

582587
} catch (ClassNotFoundException e) {
583-
// Log the error if the class is not found
584588
LOGGER.error("Class not found in classpath: {}", e.getMessage());
585589
}
586590
}
587591
}
588592
}
589593
}
590594

591-
// Convert the list of Webhooks annotations to an array and return
592595
return allWebhooks.toArray(new Webhooks[0]);
593596
}
594597

598+
/**
599+
* Collect webhooks from element.
600+
*
601+
* @param element the element
602+
* @param collector the collector
603+
*/
604+
private void collectWebhooksFromElement(AnnotatedElement element, List<Webhooks> collector) {
605+
// If @Webhooks is present (container)
606+
Webhooks container = element.getAnnotation(Webhooks.class);
607+
if (container != null) {
608+
collector.add(container);
609+
}
610+
611+
// If individual @Webhook annotations are present
612+
Webhook[] individualWebhooks = element.getAnnotationsByType(Webhook.class);
613+
if (individualWebhooks.length > 0) {
614+
collector.add(new Webhooks() {
615+
@Override
616+
public Webhook[] value() {
617+
return individualWebhooks;
618+
}
619+
620+
@Override
621+
public Class<? extends Annotation> annotationType() {
622+
return Webhooks.class;
623+
}
624+
});
625+
}
626+
}
627+
628+
629+
595630
/**
596631
* Build open api with open api definition.
597632
*

‎springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app9/WebHookResource.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
@Webhooks({
1414
@Webhook(
15-
name = "newPet",
15+
name = "newPet1",
1616
operation = @Operation(
1717
requestBody = @RequestBody(
1818
description = "Information about a new pet in the system",
@@ -35,7 +35,38 @@
3535
)
3636
})
3737
@Component
38-
public class WebHookResource {}
38+
public class WebHookResource {
39+
40+
@Webhook(
41+
name = "newPet",
42+
operation = @Operation(
43+
requestBody = @RequestBody(
44+
description = "Information about a new pet in the system",
45+
content = {
46+
@Content(
47+
mediaType = "application/json",
48+
schema = @Schema(
49+
description = "Webhook Pet",
50+
implementation = RequestDto.class
51+
)
52+
)
53+
}
54+
),
55+
method = "post",
56+
responses = @ApiResponse(
57+
responseCode = "200",
58+
description = "Return a 200 status to indicate that the data was received successfully"
59+
)
60+
)
61+
)
62+
public void newPet(RequestDto requestDto) {
63+
// This method is intentionally left empty.
64+
// The actual processing of the webhook data would be implemented here.
65+
System.out.println("Received new pet with personal number: " + requestDto.getPersonalNumber());
66+
}
67+
68+
69+
}
3970

4071

4172
class RequestDto {

‎springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app9.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@
2424
}
2525
},
2626
"webhooks": {
27+
"newPet1": {
28+
"post": {
29+
"requestBody": {
30+
"description": "Information about a new pet in the system",
31+
"content": {
32+
"application/json": {
33+
"schema": {
34+
"$ref": "#/components/schemas/RequestDto",
35+
"description": "Webhook Pet"
36+
}
37+
}
38+
}
39+
},
40+
"responses": {
41+
"200": {
42+
"description": "Return a 200 status to indicate that the data was received successfully"
43+
}
44+
}
45+
}
46+
},
2747
"newPet": {
2848
"post": {
2949
"requestBody": {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /