Skip to content

Commit 6526ac2

Browse files
committed
OpenAPI: generated yaml/json files missing @Securityscheme annotation fix #3652
1 parent dff85ce commit 6526ac2

File tree

6 files changed

+122
-4
lines changed

6 files changed

+122
-4
lines changed

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/OpenAPIParser.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ public static void parse(ParserContext ctx, OpenAPIExt openapi) {
7373
Type type = Type.getObjectType(openapi.getSource().replace(".", "/"));
7474
ClassNode node = ctx.classNode(type);
7575

76+
rootOpenApiAnnotations(openapi, node);
77+
}
78+
79+
/**
80+
* These annotations are expected to be on main class (App), but we still check on controller for
81+
* them.
82+
*
83+
* @param openapi Open API.
84+
* @param node Main or controller class.
85+
*/
86+
private static void rootOpenApiAnnotations(OpenAPIExt openapi, ClassNode node) {
7687
findAnnotationByType(node.visibleAnnotations, OpenAPIDefinition.class).stream()
7788
.findFirst()
7889
.ifPresent(a -> definition(openapi, a));
@@ -200,7 +211,13 @@ private static Boolean isDeprecated(List<AnnotationNode> annotations) {
200211
return null;
201212
}
202213

203-
public static void parse(ParserContext ctx, OperationExt operation) {
214+
public static void parse(ParserContext ctx, OpenAPIExt openapi, OperationExt operation) {
215+
/* SecurityScheme */
216+
var controller = operation.getController();
217+
if (controller != null) {
218+
rootOpenApiAnnotations(openapi, controller);
219+
}
220+
204221
/** Tags: */
205222
MethodNode method = operation.getNode();
206223
List<AnnotationNode> annotations = operation.getAllAnnotations();

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/RouteParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public RouteParser(String metaInf) {
7575
this.metaInf = metaInf;
7676
}
7777

78-
public List<OperationExt> parse(ParserContext ctx) {
78+
public List<OperationExt> parse(ParserContext ctx, OpenAPIExt openapi) {
7979
List<OperationExt> operations = parse(ctx, null, ctx.classNode(ctx.getRouter()));
8080

8181
// Checkout controllers without explicit mapping, just META-INF
@@ -95,7 +95,7 @@ public List<OperationExt> parse(ParserContext ctx) {
9595
// swagger/openapi:
9696
for (OperationExt operation : operations) {
9797
operation.setApplication(application);
98-
OpenAPIParser.parse(ctx, operation);
98+
OpenAPIParser.parse(ctx, openapi, operation);
9999
}
100100

101101
List<OperationExt> result = new ArrayList<>();

modules/jooby-openapi/src/main/java/io/jooby/openapi/OpenAPIGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public OpenAPIGenerator() {
171171

172172
RouteParser routes = new RouteParser(metaInf);
173173
ParserContext ctx = new ParserContext(source, TypeFactory.fromJavaName(classname), debug);
174-
List<OperationExt> operations = routes.parse(ctx);
174+
List<OperationExt> operations = routes.parse(ctx, openapi);
175175

176176
String contextPath = ContextPathParser.parse(ctx);
177177

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3652;
7+
8+
import io.jooby.Jooby;
9+
10+
public class App3652 extends Jooby {
11+
{
12+
mvc(Controller3652.class);
13+
}
14+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3652;
7+
8+
import io.jooby.annotation.GET;
9+
import io.jooby.annotation.Path;
10+
import io.jooby.annotation.PathParam;
11+
import io.swagger.v3.oas.annotations.Operation;
12+
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
13+
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
14+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
15+
import io.swagger.v3.oas.annotations.security.SecurityScheme;
16+
17+
@Path("/3652")
18+
@SecurityScheme(
19+
name = "myBearerToken",
20+
type = SecuritySchemeType.HTTP,
21+
scheme = "bearer",
22+
bearerFormat = "JWT",
23+
in = SecuritySchemeIn.HEADER)
24+
public class Controller3652 {
25+
26+
@GET("/{id}")
27+
@Operation(summary = "Find a user by ID", description = "Finds a user by ID or throws a 404")
28+
@SecurityRequirement(name = "myBearerToken", scopes = "user:read")
29+
public String sayHi(@PathParam String id) {
30+
return "hi";
31+
}
32+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3652;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
import io.jooby.openapi.OpenAPIResult;
11+
import io.jooby.openapi.OpenAPITest;
12+
13+
public class Issue3652 {
14+
15+
@OpenAPITest(value = App3652.class)
16+
public void shouldPrintSecurityRequeriment(OpenAPIResult result) {
17+
assertEquals(
18+
"openapi: 3.0.1\n"
19+
+ "info:\n"
20+
+ " title: 3652 API\n"
21+
+ " description: 3652 API description\n"
22+
+ " version: \"1.0\"\n"
23+
+ "paths:\n"
24+
+ " /3652/{id}:\n"
25+
+ " get:\n"
26+
+ " summary: Find a user by ID\n"
27+
+ " description: Finds a user by ID or throws a 404\n"
28+
+ " operationId: sayHi\n"
29+
+ " parameters:\n"
30+
+ " - name: id\n"
31+
+ " in: path\n"
32+
+ " required: true\n"
33+
+ " schema:\n"
34+
+ " type: string\n"
35+
+ " responses:\n"
36+
+ " \"200\":\n"
37+
+ " description: Success\n"
38+
+ " content:\n"
39+
+ " application/json:\n"
40+
+ " schema:\n"
41+
+ " type: string\n"
42+
+ " security:\n"
43+
+ " - myBearerToken:\n"
44+
+ " - user:read\n"
45+
+ "components:\n"
46+
+ " securitySchemes:\n"
47+
+ " myBearerToken:\n"
48+
+ " type: http\n"
49+
+ " name: myBearerToken\n"
50+
+ " in: header\n"
51+
+ " scheme: bearer\n"
52+
+ " bearerFormat: JWT\n",
53+
result.toYaml());
54+
}
55+
}

0 commit comments

Comments
 (0)