Skip to content

Commit df871e1

Browse files
committed
feat: add ResponseStatus annotation for non-200 success codes in Quarkus library
1 parent 2917ce8 commit df871e1

6 files changed

Lines changed: 186 additions & 14 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaJAXRSSpecServerCodegen.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,19 @@ public ModelsMap postProcessModels(ModelsMap objs) {
343343
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
344344
objs = super.postProcessOperationsWithModels(objs, allModels);
345345
removeImport(objs, "java.util.List");
346+
if (QUARKUS_LIBRARY.equals(library) && !returnResponse && !returnJbossResponse) {
347+
for (CodegenOperation op : objs.getOperations().getOperation()) {
348+
op.responses.stream()
349+
.filter(r -> r.is2xx || r.is3xx)
350+
.findFirst()
351+
.ifPresent(r -> op.vendorExtensions.put("x-java-success-response-code", r.code));
352+
}
353+
if (objs.getOperations().getOperation().stream()
354+
.anyMatch(op -> op.vendorExtensions.containsKey("x-java-success-response-code"))) {
355+
objs.put("hasResponseStatusAnnotations", true);
356+
additionalProperties.put("hasResponseStatusAnnotations", true);
357+
}
358+
}
346359
return objs;
347360
}
348361

modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/api.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package {{package}};
66
import {{javaxPackage}}.ws.rs.*;
77
import {{javaxPackage}}.ws.rs.core.Response;
88
{{#returnJBossResponse}}import org.jboss.resteasy.reactive.RestResponse;{{/returnJBossResponse}}
9-
9+
{{#hasResponseStatusAnnotations}}import org.jboss.resteasy.reactive.ResponseStatus;{{/hasResponseStatusAnnotations}}
1010
{{#useGzipFeature}}
1111
import org.jboss.resteasy.annotations.GZIP;
1212
{{/useGzipFeature}}
@@ -116,4 +116,4 @@ public {{#interfaceOnly}}interface{{/interfaceOnly}}{{^interfaceOnly}}class{{/in
116116
{{#interfaceOnly}}{{>apiInterface}}{{/interfaceOnly}}{{^interfaceOnly}}{{>apiMethod}}{{/interfaceOnly}}
117117
{{/operation}}
118118
}
119-
{{/operations}}
119+
{{/operations}}

modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/apiInterface.mustache

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@
5050
{{^vendorExtensions.x-java-is-response-void}}@org.eclipse.microprofile.openapi.annotations.media.Content(schema = @org.eclipse.microprofile.openapi.annotations.media.Schema(implementation = {{{baseType}}}.class{{#vendorExtensions.x-microprofile-open-api-return-schema-container}}, type = {{{.}}} {{/vendorExtensions.x-microprofile-open-api-return-schema-container}}{{#vendorExtensions.x-microprofile-open-api-return-unique-items}}, uniqueItems = true {{/vendorExtensions.x-microprofile-open-api-return-unique-items}})){{/vendorExtensions.x-java-is-response-void}}
5151
}){{^-last}},{{/-last}}{{/responses}}
5252
}){{/hasProduces}}{{/useMicroProfileOpenAPIAnnotations}}
53-
{{#supportAsync}}{{>returnAsyncTypeInterface}}{{/supportAsync}}{{^supportAsync}}{{#returnJBossResponse}}{{>returnResponseTypeInterface}}{{/returnJBossResponse}}{{^returnJBossResponse}}{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{>returnTypeInterface}}{{/returnResponse}}{{/returnJBossResponse}}{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}});
53+
{{#vendorExtensions.x-java-success-response-code}}@ResponseStatus({{{vendorExtensions.x-java-success-response-code}}}){{/vendorExtensions.x-java-success-response-code}}
54+
{{#supportAsync}}{{>returnAsyncTypeInterface}}{{/supportAsync}}{{^supportAsync}}{{#returnJBossResponse}}{{>returnResponseTypeInterface}}{{/returnJBossResponse}}{{^returnJBossResponse}}{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{>returnTypeInterface}}{{/returnResponse}}{{/returnJBossResponse}}{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}});

modules/openapi-generator/src/main/resources/JavaJaxRS/spec/libraries/quarkus/apiMethod.mustache

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
{{^vendorExtensions.x-java-is-response-void}}@org.eclipse.microprofile.openapi.annotations.media.Content(schema = @org.eclipse.microprofile.openapi.annotations.media.Schema(implementation = {{{baseType}}}.class{{#vendorExtensions.x-microprofile-open-api-return-schema-container}}, type = {{{.}}} {{/vendorExtensions.x-microprofile-open-api-return-schema-container}}{{#vendorExtensions.x-microprofile-open-api-return-unique-items}}, uniqueItems = true {{/vendorExtensions.x-microprofile-open-api-return-unique-items}})){{/vendorExtensions.x-java-is-response-void}}
4848
}){{^-last}},{{/-last}}{{/responses}}
4949
}){{/hasProduces}}{{/useMicroProfileOpenAPIAnnotations}}
50+
{{#vendorExtensions.x-java-success-response-code}}@ResponseStatus({{{vendorExtensions.x-java-success-response-code}}}){{/vendorExtensions.x-java-success-response-code}}
5051
public {{#supportAsync}}{{#useMutiny}}Uni{{/useMutiny}}{{^useMutiny}}CompletionStage{{/useMutiny}}<{{/supportAsync}}{{#returnJBossResponse}}{{>returnResponseTypeInterface}}{{/returnJBossResponse}}{{^returnJBossResponse}}Response{{/returnJBossResponse}}{{#supportAsync}}>{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}) {
5152
return {{#supportAsync}}{{#useMutiny}}Uni.createFrom().item({{/useMutiny}}{{^useMutiny}}CompletableFuture.supplyAsync(() -> {{/useMutiny}}{{/supportAsync}}Response.ok().entity("magic!").build(){{#supportAsync}}){{/supportAsync}};
52-
}
53+
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java

Lines changed: 142 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ public void generateApiWithAsyncSupportAndInterfaceOnlyAndJBossResponse() throws
552552
//And the generated interface contains CompletionStage<RestResponse<Pet>>
553553
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PetApi.java");
554554
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PetApi.java"),
555-
"\nimport org.jboss.resteasy.reactive.RestResponse;\n",
555+
"\nimport org.jboss.resteasy.reactive.RestResponse;\n",
556556
"\nimport java.util.concurrent.CompletionStage;\n",
557557
"CompletionStage<RestResponse<Pet>> addPet", "CompletionStage<RestResponse<Void>> deletePet");
558558
}
@@ -1236,7 +1236,7 @@ public void disableGenerateJsonCreator() throws Exception {
12361236

12371237
assertFileNotContains(files.get("RequiredProperties.java").toPath(), "@JsonCreator");
12381238
}
1239-
1239+
12401240
@Test
12411241
public void testDiscriminatorMappingUsedInJsonTypeName() throws Exception {
12421242
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
@@ -1273,37 +1273,37 @@ public void testDiscriminatorMappingUsedInJsonTypeName() throws Exception {
12731273
public void testGenerateJsonNullableListFieldsHelperMethodReferences_issue23251() throws Exception {
12741274
Map<String, Object> properties = new HashMap<>();
12751275
properties.put(OPENAPI_NULLABLE, "true");
1276-
1276+
12771277
File output = Files.createTempDirectory("test").toFile();
1278-
1278+
12791279
final CodegenConfigurator configurator = new CodegenConfigurator()
12801280
.setGeneratorName("jaxrs-spec")
12811281
.setAdditionalProperties(properties)
12821282
.setInputSpec("src/test/resources/bugs/issue_23251.yaml")
12831283
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
1284-
1284+
12851285
final ClientOptInput clientOptInput = configurator.toClientOptInput();
12861286
DefaultGenerator generator = new DefaultGenerator();
12871287
List<File> files = generator.opts(clientOptInput).generate();
1288-
1288+
12891289
validateJavaSourceFiles(files);
1290-
1290+
12911291
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/model/BugResponse.java");
1292-
1292+
12931293
// Assert that the generated model contains JsonNullable fields
12941294
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/model/BugResponse.java"),
12951295
"private JsonNullable<String> nullableField = JsonNullable.<String>undefined();",
12961296
"private JsonNullable<List<String>> nullableList = JsonNullable.<List<String>>undefined();",
12971297
"private JsonNullable<List<@Valid NestedResponse>> nullableObjectList = JsonNullable.<List<@Valid NestedResponse>>undefined();"
12981298
);
1299-
1299+
13001300
// Assert that the generated model contains correct add and remove helper methods reference for JsonNullable fields
13011301
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/model/BugResponse.java"),
13021302
"this.nullableList.get().add(nullableListItem);",
13031303
"this.nullableList.get().remove(nullableListItem);",
13041304
"this.nullableObjectList.get().add(nullableObjectListItem);",
13051305
"this.nullableObjectList.get().remove(nullableObjectListItem);");
1306-
1306+
13071307
output.deleteOnExit();
13081308
}
13091309

@@ -1342,4 +1342,136 @@ public void generatesDeprecatedAnnotationsForModelsOperationsAndParameters_issue
13421342
JavaFileAssert.assertThat(petApi).fileContains("findPetsByStatus", "@Deprecated", "@QueryParam(\"status\")");
13431343
}
13441344

1345+
/**
1346+
* Verify that when using the quarkus library with interfaceOnly=true, the generated interface
1347+
* method is always annotated with {@code @ResponseStatus(<code>)} for any 2xx or 3xx response,
1348+
* including 200, for explicit documentation purposes.
1349+
* ping.yaml has a 201 response.
1350+
*/
1351+
@Test
1352+
public void generateQuarkusInterfaceAddsResponseStatusAnnotationForSuccessCode() throws Exception {
1353+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1354+
output.deleteOnExit();
1355+
1356+
final OpenAPI openAPI = new OpenAPIParser()
1357+
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
1358+
1359+
codegen.setOutputDir(output.getAbsolutePath());
1360+
codegen.setLibrary(QUARKUS_LIBRARY); //Given the quarkus library is used
1361+
codegen.additionalProperties().put(INTERFACE_ONLY, true); //And only interfaces are generated
1362+
// returnResponse and returnJBossResponse are both false (defaults)
1363+
1364+
final ClientOptInput input = new ClientOptInput()
1365+
.openAPI(openAPI)
1366+
.config(codegen);
1367+
1368+
final DefaultGenerator generator = new DefaultGenerator();
1369+
final List<File> files = generator.opts(input).generate();
1370+
1371+
validateJavaSourceFiles(files);
1372+
1373+
//Then the generated interface contains the ResponseStatus import and annotation with code 201
1374+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
1375+
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
1376+
"import org.jboss.resteasy.reactive.ResponseStatus;",
1377+
"@ResponseStatus(201)");
1378+
}
1379+
1380+
/**
1381+
* Verify that {@code @ResponseStatus(200)} IS emitted even for the default 200 status code,
1382+
* for explicit documentation purposes.
1383+
*/
1384+
@Test
1385+
public void generateQuarkusInterfaceAddsResponseStatusAnnotationFor200Response() throws Exception {
1386+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1387+
output.deleteOnExit();
1388+
1389+
final OpenAPI openAPI = new OpenAPIParser()
1390+
.readLocation("src/test/resources/3_0/petstore.yaml", null, new ParseOptions()).getOpenAPI();
1391+
1392+
codegen.setOutputDir(output.getAbsolutePath());
1393+
codegen.setLibrary(QUARKUS_LIBRARY);
1394+
codegen.additionalProperties().put(INTERFACE_ONLY, true);
1395+
1396+
final ClientOptInput input = new ClientOptInput()
1397+
.openAPI(openAPI)
1398+
.config(codegen);
1399+
1400+
final DefaultGenerator generator = new DefaultGenerator();
1401+
final List<File> files = generator.opts(input).generate();
1402+
1403+
validateJavaSourceFiles(files);
1404+
1405+
//Then @ResponseStatus(200) IS present for explicit documentation
1406+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PetApi.java");
1407+
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PetApi.java"),
1408+
"import org.jboss.resteasy.reactive.ResponseStatus;",
1409+
"@ResponseStatus(200)");
1410+
}
1411+
1412+
1413+
/**
1414+
* Verify that the {@code @ResponseStatus} annotation is NOT emitted when returnResponse=true,
1415+
* because the user controls the status code via the {@code Response} builder in that mode.
1416+
*/
1417+
@Test
1418+
public void generateQuarkusInterfaceDoesNotAddResponseStatusAnnotationWhenReturnResponse() throws Exception {
1419+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1420+
output.deleteOnExit();
1421+
1422+
final OpenAPI openAPI = new OpenAPIParser()
1423+
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
1424+
1425+
codegen.setOutputDir(output.getAbsolutePath());
1426+
codegen.setLibrary(QUARKUS_LIBRARY);
1427+
codegen.additionalProperties().put(INTERFACE_ONLY, true);
1428+
codegen.additionalProperties().put(RETURN_RESPONSE, true); //Given returnResponse is true
1429+
1430+
final ClientOptInput input = new ClientOptInput()
1431+
.openAPI(openAPI)
1432+
.config(codegen);
1433+
1434+
final DefaultGenerator generator = new DefaultGenerator();
1435+
final List<File> files = generator.opts(input).generate();
1436+
1437+
validateJavaSourceFiles(files);
1438+
1439+
//Then the annotation must NOT appear
1440+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
1441+
assertFileNotContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
1442+
"@ResponseStatus",
1443+
"import org.jboss.resteasy.reactive.ResponseStatus");
1444+
}
1445+
1446+
/**
1447+
* Verify that when using the quarkus library with interfaceOnly=true and a 3xx response,
1448+
* the generated interface method is annotated with {@code @ResponseStatus(<code>)}.
1449+
*/
1450+
@Test
1451+
public void generateQuarkusInterfaceAddsResponseStatusAnnotationFor3xxResponseCode() throws Exception {
1452+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1453+
output.deleteOnExit();
1454+
1455+
final OpenAPI openAPI = new OpenAPIParser()
1456+
.readLocation("src/test/resources/3_0/jaxrs-spec/petstore-quarkus.yaml", null, new ParseOptions()).getOpenAPI();
1457+
1458+
codegen.setOutputDir(output.getAbsolutePath());
1459+
codegen.setLibrary(QUARKUS_LIBRARY);
1460+
codegen.additionalProperties().put(INTERFACE_ONLY, true);
1461+
1462+
final ClientOptInput input = new ClientOptInput()
1463+
.openAPI(openAPI)
1464+
.config(codegen);
1465+
1466+
final DefaultGenerator generator = new DefaultGenerator();
1467+
final List<File> files = generator.opts(input).generate();
1468+
1469+
validateJavaSourceFiles(files);
1470+
1471+
//Then the generated interface contains the ResponseStatus import and annotation with code 302
1472+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/RedirectApi.java");
1473+
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/RedirectApi.java"),
1474+
"import org.jboss.resteasy.reactive.ResponseStatus;",
1475+
"@ResponseStatus(302)");
1476+
}
13451477
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
openapi: 3.0.0
2+
info:
3+
description: >-
4+
This spec is mainly for testing Petstore server and contains fake endpoints,
5+
models. Please do not use this for any other purpose. Special characters: "
6+
\
7+
version: 1.0.0
8+
title: OpenAPI Petstore
9+
license:
10+
name: Apache-2.0
11+
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
12+
tags:
13+
- name: pet
14+
description: Everything about your Pets
15+
paths:
16+
/redirect:
17+
get:
18+
operationId: redirectGet
19+
responses:
20+
'302':
21+
description: Temporary Redirect
22+
headers:
23+
Location:
24+
schema:
25+
type: string

0 commit comments

Comments
 (0)