Skip to content

Commit 24825b2

Browse files
authored
Merge pull request #152 from SentryMan/inherit-roles
Inherit interface roles and class openAPIResponse
2 parents 3dedf4c + 1231324 commit 24825b2

File tree

6 files changed

+78
-8
lines changed

6 files changed

+78
-8
lines changed

http-api/src/main/java/io/avaje/http/api/OpenAPIResponse.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.avaje.http.api;
22

33
import static java.lang.annotation.ElementType.METHOD;
4+
import static java.lang.annotation.ElementType.TYPE;
45
import static java.lang.annotation.RetentionPolicy.RUNTIME;
56

67
import java.lang.annotation.Repeatable;
@@ -11,7 +12,9 @@
1112
* Specify endpoint response status code/description/type.
1213
*
1314
* <p>When not specified the default 2xx openAPI generation is based on the javadoc of the method.
14-
* <p> Will not override the default 2xx generated openapi unless status code is 2xx
15+
*
16+
* <p>Will not override the default 2xx generated openapi unless status code is 2xx
17+
*
1518
* <pre>{@code
1619
* @Post("/post")
1720
* @OpenAPIReturns(responseCode = "200", description = "from annotaion")
@@ -20,9 +23,21 @@
2023
* ResponseModel endpoint() {}
2124
*
2225
* }</pre>
26+
*
27+
* <p>Can also be placed on a class to add to every method in the controller.
28+
*
29+
* <pre>{@code
30+
* @OpenAPIResponse(
31+
* responseCode = "403",
32+
* description = "Insufficient rights to this resource."
33+
* )
34+
* public class MyController {
35+
* ...
36+
* }
37+
* }</pre>
2338
*/
24-
@Target(value = METHOD)
25-
@Retention(value = RUNTIME)
39+
@Target({TYPE, METHOD})
40+
@Retention(RUNTIME)
2641
@Repeatable(OpenAPIResponses.class)
2742
public @interface OpenAPIResponse {
2843

http-api/src/main/java/io/avaje/http/api/OpenAPIResponses.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.avaje.http.api;
22

33
import static java.lang.annotation.ElementType.METHOD;
4+
import static java.lang.annotation.ElementType.TYPE;
45
import static java.lang.annotation.RetentionPolicy.RUNTIME;
56

67
import java.lang.annotation.Retention;
@@ -11,8 +12,8 @@
1112
*
1213
* @see OpenAPIResponse
1314
*/
14-
@Target(value = METHOD)
15-
@Retention(value = RUNTIME)
15+
@Target({TYPE, METHOD})
16+
@Retention(RUNTIME)
1617
public @interface OpenAPIResponses {
1718
OpenAPIResponse[] value();
1819
}

http-generator-core/src/main/java/io/avaje/http/generator/core/ControllerReader.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.lang.annotation.Annotation;
66
import java.util.ArrayList;
7+
import java.util.Arrays;
78
import java.util.List;
89
import java.util.Optional;
910
import java.util.Set;
@@ -21,6 +22,8 @@
2122
import javax.validation.Valid;
2223

2324
import io.avaje.http.api.Controller;
25+
import io.avaje.http.api.OpenAPIResponse;
26+
import io.avaje.http.api.OpenAPIResponses;
2427
import io.avaje.http.api.Path;
2528
import io.avaje.http.api.Produces;
2629
import io.swagger.v3.oas.annotations.Hidden;
@@ -38,6 +41,7 @@ public class ControllerReader {
3841
private final List<MethodReader> methods = new ArrayList<>();
3942
private final Set<String> staticImportTypes = new TreeSet<>();
4043
private final Set<String> importTypes = new TreeSet<>();
44+
private final List<OpenAPIResponse> apiResponses;
4145

4246
/**
4347
* The produces media type for the controller. Null implies JSON.
@@ -57,12 +61,46 @@ public ControllerReader(TypeElement beanType, ProcessingContext ctx) {
5761
this.ctx = ctx;
5862
this.interfaces = initInterfaces();
5963
this.interfaceMethods = initInterfaceMethods();
60-
this.roles = Util.findRoles(beanType);
64+
this.roles = buildRoles();
6165
if (ctx.isOpenApiAvailable()) {
6266
docHidden = initDocHidden();
6367
}
6468
this.hasValid = initHasValid();
6569
this.produces = initProduces();
70+
this.apiResponses = buildApiResponses();
71+
}
72+
73+
private List<OpenAPIResponse> buildApiResponses() {
74+
final var responses = new ArrayList<OpenAPIResponse>();
75+
76+
Optional.ofNullable(beanType.getAnnotation(OpenAPIResponses.class)).stream()
77+
.map(OpenAPIResponses::value)
78+
.flatMap(Arrays::stream)
79+
.forEach(responses::add);
80+
81+
Arrays.stream(beanType.getAnnotationsByType(OpenAPIResponse.class)).forEach(responses::add);
82+
83+
for (final Element anInterface : interfaces) {
84+
85+
Optional.ofNullable(anInterface.getAnnotation(OpenAPIResponses.class)).stream()
86+
.map(OpenAPIResponses::value)
87+
.flatMap(Arrays::stream)
88+
.forEach(responses::add);
89+
90+
Arrays.stream(anInterface.getAnnotationsByType(OpenAPIResponse.class))
91+
.forEach(responses::add);
92+
}
93+
94+
return responses;
95+
}
96+
97+
private ArrayList<String> buildRoles() {
98+
final var roleList = new ArrayList<>(Util.findRoles(beanType));
99+
100+
for (final Element anInterface : interfaces) {
101+
roleList.addAll(Util.findRoles(anInterface));
102+
}
103+
return roleList;
66104
}
67105

68106
protected void addImports(boolean withSingleton) {
@@ -267,6 +305,10 @@ public List<MethodReader> methods() {
267305
return methods;
268306
}
269307

308+
public List<OpenAPIResponse> openApiResponses() {
309+
return apiResponses;
310+
}
311+
270312
public String path() {
271313

272314
return Optional.ofNullable(findAnnotation(Controller.class))

http-generator-core/src/main/java/io/avaje/http/generator/core/MethodReader.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,12 @@ private List<OpenAPIResponse> buildApiResponses() {
170170
.map(OpenAPIResponses::value)
171171
.flatMap(Arrays::stream),
172172
Arrays.stream(method.getAnnotationsByType(OpenAPIResponse.class))));
173-
174-
return Stream.concat(methodResponses, superMethodResponses).collect(Collectors.toList());
173+
174+
var responses =
175+
Stream.concat(methodResponses, superMethodResponses).collect(Collectors.toList());
176+
177+
responses.addAll(bean.openApiResponses());
178+
return responses;
175179
}
176180

177181
public <A extends Annotation> A findAnnotation(Class<A> type) {

tests/test-javalin-jsonb/src/main/java/org/example/myapp/web/test/HealthController.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.example.myapp.web.test;
22

3+
import org.example.myapp.web.AppRoles;
4+
import org.example.myapp.web.Roles;
5+
36
import io.avaje.http.api.Get;
47
import io.avaje.http.api.MediaType;
58
import io.avaje.http.api.OpenAPIResponse;
@@ -10,11 +13,13 @@
1013
import io.swagger.v3.oas.annotations.tags.Tag;
1114

1215
@Path("javalin")
16+
@Roles(AppRoles.ANYONE)
1317
@OpenAPIDefinition(
1418
info =
1519
@Info(
1620
title = "Example service showing off the Path extension method of controller",
1721
description = ""))
22+
@OpenAPIResponse(responseCode = "403", description = "Not Authorized")
1823
public interface HealthController {
1924
/**
2025
* Standard Get

tests/test-javalin-jsonb/src/test/resources/expectedInheritedOpenApi.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
}
3030
}
3131
},
32+
"403" : {
33+
"description" : "Not Authorized"
34+
},
3235
"200" : {
3336
"description" : "a health check",
3437
"content" : {

0 commit comments

Comments
 (0)