Skip to content

Commit 3b730a5

Browse files
committed
OpenAPI: generated yaml/json files misplaced @parameter annotation fix #3654
1 parent 6526ac2 commit 3b730a5

File tree

5 files changed

+154
-63
lines changed

5 files changed

+154
-63
lines changed

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

Lines changed: 55 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
import io.swagger.v3.core.util.Json;
4141
import io.swagger.v3.core.util.RefUtils;
4242
import io.swagger.v3.oas.annotations.*;
43-
import io.swagger.v3.oas.annotations.enums.Explode;
44-
import io.swagger.v3.oas.annotations.enums.ParameterIn;
4543
import io.swagger.v3.oas.annotations.extensions.Extension;
4644
import io.swagger.v3.oas.annotations.extensions.Extensions;
4745
import io.swagger.v3.oas.annotations.media.ArraySchema;
@@ -231,14 +229,14 @@ public static void parse(ParserContext ctx, OpenAPIExt openapi, OperationExt ope
231229

232230
operation.setHidden(isHidden(annotations));
233231
operation.setDeprecated(isDeprecated(annotations));
234-
/**
232+
/*
235233
* @Operation:
236234
*/
237235
findAnnotationByType(method.visibleAnnotations, Operation.class).stream()
238236
.findFirst()
239237
.ifPresent(a -> operation(ctx, operation, toMap(a)));
240238

241-
/**
239+
/*
242240
* @Parameters:
243241
*/
244242
findAnnotationByType(method.visibleAnnotations, Parameters.class).stream()
@@ -248,14 +246,18 @@ public static void parse(ParserContext ctx, OpenAPIExt openapi, OperationExt ope
248246
.findFirst()
249247
.ifPresent(a -> parameters(ctx, operation, singletonList(toMap(a))));
250248
if (method.visibleParameterAnnotations != null) {
251-
for (List<AnnotationNode> paramAnnotations : method.visibleParameterAnnotations) {
252-
/**
249+
for (int paramIndex = 0;
250+
paramIndex < method.visibleParameterAnnotations.length;
251+
paramIndex++) {
252+
var paramAnnotations = method.visibleParameterAnnotations[paramIndex];
253+
var pindex = paramIndex;
254+
/*
253255
* @Parameter:
254256
*/
255257
findAnnotationByType(paramAnnotations, Parameter.class).stream()
256258
.findFirst()
257-
.ifPresent(a -> parameters(ctx, operation, singletonList(toMap(a))));
258-
/**
259+
.ifPresent(a -> parameter(ctx, operation, pindex, toMap(a)));
260+
/*
259261
* @RequestBody:
260262
*/
261263
findAnnotationByType(paramAnnotations, RequestBody.class).stream()
@@ -267,14 +269,14 @@ public static void parse(ParserContext ctx, OpenAPIExt openapi, OperationExt ope
267269
.findFirst()
268270
.ifPresent(a -> requestBody(ctx, operation, toMap(a)));
269271

270-
/**
272+
/*
271273
* @ApiResponse:
272274
*/
273275
findAnnotationByType(method.visibleAnnotations, ApiResponse.class).stream()
274276
.findFirst()
275277
.ifPresent(a -> operationResponse(ctx, operation, toMap(a)));
276278

277-
/** SecurityRequirements: */
279+
/* SecurityRequirements: */
278280
findAnnotationByType(method.visibleAnnotations, SecurityRequirements.class).stream()
279281
.map(it -> annotationList(toMap(it), "value"))
280282
.forEach(a -> securityRequirements(a, operation::addSecurityItem));
@@ -283,14 +285,14 @@ public static void parse(ParserContext ctx, OpenAPIExt openapi, OperationExt ope
283285
.findFirst()
284286
.ifPresent(a -> securityRequirements(singletonList(toMap(a)), operation::addSecurityItem));
285287

286-
/**
288+
/*
287289
* @ApiResponses:
288290
*/
289291
findAnnotationByType(method.visibleAnnotations, ApiResponses.class).stream()
290292
.flatMap(it -> annotationList(toMap(it), "value").stream())
291293
.forEach(a -> operationResponse(ctx, operation, a));
292294

293-
/**
295+
/*
294296
* @ApiResponse:
295297
*/
296298
findAnnotationByType(method.visibleAnnotations, ApiResponse.class).stream()
@@ -479,7 +481,7 @@ private static void securityRequirements(
479481

480482
private static void requestBody(
481483
ParserContext ctx, OperationExt operation, Map<String, Object> annotation) {
482-
if (annotation.size() > 0) {
484+
if (!annotation.isEmpty()) {
483485
RequestBodyExt requestBody = operation.getRequestBody();
484486
if (requestBody == null) {
485487
requestBody = new RequestBodyExt();
@@ -492,60 +494,51 @@ private static void requestBody(
492494
}
493495
}
494496

495-
@io.swagger.v3.oas.annotations.Operation(
496-
parameters =
497-
@Parameter(
498-
name = "p",
499-
description = "des",
500-
in = ParameterIn.COOKIE,
501-
required = false,
502-
deprecated = true,
503-
allowEmptyValue = true,
504-
allowReserved = false,
505-
hidden = false,
506-
explode = Explode.TRUE,
507-
ref = "Pet"))
508497
private static void parameters(
509498
ParserContext ctx, OperationExt operation, List<Map<String, Object>> parameters) {
510499
for (int i = 0; i < parameters.size(); i++) {
511500
Map<String, Object> parameterMap = parameters.get(i);
512-
String name = (String) parameterMap.get("name");
513-
io.swagger.v3.oas.models.parameters.Parameter parameter;
514-
if (name != null) {
515-
int index = i;
516-
parameter =
517-
operation.getParameters().stream()
518-
.filter(it -> it.getName().equals(name))
519-
.findFirst()
520-
.orElseGet(() -> operation.getParameter(index));
521-
} else {
522-
parameter = operation.getParameter(i);
523-
}
524-
if (parameter == null) {
525-
throw new IllegalArgumentException(
526-
"Parameter not found: "
527-
+ name
528-
+ " at position: "
529-
+ i
530-
+ " for annotation: "
531-
+ parameterMap);
532-
}
533-
Optional.ofNullable(name).ifPresent(parameter::setName);
534-
stringValue(parameterMap, "description", parameter::setDescription);
535-
enumValue(parameterMap, "in", in -> parameter.setIn(in.toLowerCase()));
536-
boolValue(parameterMap, "required", parameter::setRequired);
537-
boolValue(parameterMap, "deprecated", parameter::setDeprecated);
538-
boolValue(parameterMap, "allowEmptyValue", parameter::setAllowEmptyValue);
539-
boolValue(parameterMap, "allowReserved", parameter::setAllowReserved);
540-
// NOTE: Hidden is not present on parameter
541-
// boolValue(parameterMap, "hidden", parameter::setHidden);
542-
enumValue(parameterMap, "explode", value -> parameter.setExample(Boolean.valueOf(value)));
543-
stringValue(parameterMap, "ref", ref -> parameter.set$ref(RefUtils.constructRef(ref)));
544-
arrayOrSchema(ctx, parameterMap).ifPresent(parameter::setSchema);
545-
examples(parameterMap, parameter::setExample, parameter::setExamples);
546-
annotationList(
547-
parameterMap, "extensions", values -> extensions(values, parameter::addExtension));
501+
parameter(ctx, operation, i, parameterMap);
502+
}
503+
}
504+
505+
private static void parameter(
506+
ParserContext ctx, OperationExt operation, int index, Map<String, Object> parameterMap) {
507+
String name = (String) parameterMap.get("name");
508+
io.swagger.v3.oas.models.parameters.Parameter parameter;
509+
if (name != null) {
510+
parameter =
511+
operation.getParameters().stream()
512+
.filter(it -> it.getName().equals(name))
513+
.findFirst()
514+
.orElseGet(() -> operation.getParameter(index));
515+
} else {
516+
parameter = operation.getParameter(index);
548517
}
518+
if (parameter == null) {
519+
throw new IllegalArgumentException(
520+
"Parameter not found: "
521+
+ name
522+
+ " at position: "
523+
+ index
524+
+ " for annotation: "
525+
+ parameterMap);
526+
}
527+
Optional.ofNullable(name).ifPresent(parameter::setName);
528+
stringValue(parameterMap, "description", parameter::setDescription);
529+
enumValue(parameterMap, "in", in -> parameter.setIn(in.toLowerCase()));
530+
boolValue(parameterMap, "required", parameter::setRequired);
531+
boolValue(parameterMap, "deprecated", parameter::setDeprecated);
532+
boolValue(parameterMap, "allowEmptyValue", parameter::setAllowEmptyValue);
533+
boolValue(parameterMap, "allowReserved", parameter::setAllowReserved);
534+
// NOTE: Hidden is not present on parameter
535+
// boolValue(parameterMap, "hidden", parameter::setHidden);
536+
enumValue(parameterMap, "explode", value -> parameter.setExample(Boolean.valueOf(value)));
537+
stringValue(parameterMap, "ref", ref -> parameter.set$ref(RefUtils.constructRef(ref)));
538+
arrayOrSchema(ctx, parameterMap).ifPresent(parameter::setSchema);
539+
examples(parameterMap, parameter::setExample, parameter::setExamples);
540+
annotationList(
541+
parameterMap, "extensions", values -> extensions(values, parameter::addExtension));
549542
}
550543

551544
private static void examples(

modules/jooby-openapi/src/test/java/issues/i3652/Issue3652.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
public class Issue3652 {
1414

1515
@OpenAPITest(value = App3652.class)
16-
public void shouldPrintSecurityRequeriment(OpenAPIResult result) {
16+
public void shouldGenerateSecuritySchemeFromController(OpenAPIResult result) {
1717
assertEquals(
1818
"openapi: 3.0.1\n"
1919
+ "info:\n"
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.i3654;
7+
8+
import io.jooby.Jooby;
9+
10+
public class App3654 extends Jooby {
11+
{
12+
mvc(Controller3654.class);
13+
}
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.i3654;
7+
8+
import java.util.Map;
9+
10+
import io.jooby.annotation.GET;
11+
import io.jooby.annotation.Path;
12+
import io.jooby.annotation.PathParam;
13+
import io.jooby.annotation.QueryParam;
14+
import io.swagger.v3.oas.annotations.Operation;
15+
import io.swagger.v3.oas.annotations.Parameter;
16+
17+
@Path("/3652")
18+
public class Controller3654 {
19+
20+
@GET("/{id}")
21+
@Operation(summary = "Find a user by ID", description = "Finds a user by ID or throws a 404")
22+
public Map<String, Object> getUser(
23+
@Parameter(description = "The user ID", required = true) @PathParam String id,
24+
@Parameter(
25+
description =
26+
"Flag for fetching active/inactive users. (Defaults to true if not provided)")
27+
@QueryParam
28+
Boolean activeOnly) {
29+
return null;
30+
}
31+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.i3654;
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 Issue3654 {
14+
15+
@OpenAPITest(value = App3654.class)
16+
public void shouldGenerateParamDocInRightOrder(OpenAPIResult result) {
17+
assertEquals(
18+
"openapi: 3.0.1\n"
19+
+ "info:\n"
20+
+ " title: 3654 API\n"
21+
+ " description: 3654 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: getUser\n"
29+
+ " parameters:\n"
30+
+ " - name: id\n"
31+
+ " in: path\n"
32+
+ " description: The user ID\n"
33+
+ " required: true\n"
34+
+ " schema:\n"
35+
+ " type: string\n"
36+
+ " - name: activeOnly\n"
37+
+ " in: query\n"
38+
+ " description: Flag for fetching active/inactive users. (Defaults to true if\n"
39+
+ " not provided)\n"
40+
+ " schema:\n"
41+
+ " type: boolean\n"
42+
+ " responses:\n"
43+
+ " \"200\":\n"
44+
+ " description: Success\n"
45+
+ " content:\n"
46+
+ " application/json:\n"
47+
+ " schema:\n"
48+
+ " type: object\n"
49+
+ " additionalProperties:\n"
50+
+ " type: object\n",
51+
result.toYaml());
52+
}
53+
}

0 commit comments

Comments
 (0)