Skip to content

Commit 9a1952b

Browse files
committed
feat: add ResponseStatus annotation for non-200 success codes in Quarkus library
1 parent 878d821 commit 9a1952b

6 files changed

Lines changed: 178 additions & 13 deletions

File tree

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,18 @@ 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) && !"200".equals(r.code))
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+
}
357+
}
346358
return objs;
347359
}
348360

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +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+
{{#hasResponseStatusAnnotations}}import org.jboss.resteasy.reactive.ResponseStatus;{{/hasResponseStatusAnnotations}}
910

1011
{{#useGzipFeature}}
1112
import org.jboss.resteasy.annotations.GZIP;
@@ -116,4 +117,4 @@ public {{#interfaceOnly}}interface{{/interfaceOnly}}{{^interfaceOnly}}class{{/in
116117
{{#interfaceOnly}}{{>apiInterface}}{{/interfaceOnly}}{{^interfaceOnly}}{{>apiMethod}}{{/interfaceOnly}}
117118
{{/operation}}
118119
}
119-
{{/operations}}
120+
{{/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
@@ -44,4 +44,5 @@
4444
{{^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}}
4545
}){{^-last}},{{/-last}}{{/responses}}
4646
}){{/hasProduces}}{{/useMicroProfileOpenAPIAnnotations}}
47-
{{#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}});
47+
{{#vendorExtensions.x-java-success-response-code}}@ResponseStatus({{{vendorExtensions.x-java-success-response-code}}})
48+
{{/vendorExtensions.x-java-success-response-code}}{{#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: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
{{^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}}
4545
}){{^-last}},{{/-last}}{{/responses}}
4646
}){{/hasProduces}}{{/useMicroProfileOpenAPIAnnotations}}
47-
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}}) {
47+
{{#vendorExtensions.x-java-success-response-code}}@ResponseStatus({{{vendorExtensions.x-java-success-response-code}}})
48+
{{/vendorExtensions.x-java-success-response-code}}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}}) {
4849
return {{#supportAsync}}{{#useMutiny}}Uni.createFrom().item({{/useMutiny}}{{^useMutiny}}CompletableFuture.supplyAsync(() -> {{/useMutiny}}{{/supportAsync}}Response.ok().entity("magic!").build(){{#supportAsync}}){{/supportAsync}};
49-
}
50+
}

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

Lines changed: 141 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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,38 +1273,170 @@ 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+
1307+
output.deleteOnExit();
1308+
}
1309+
1310+
/**
1311+
* Verify that when using the quarkus library with interfaceOnly=true and a non-200 success response,
1312+
* the generated interface method is annotated with
1313+
* {@code @org.jboss.resteasy.reactive.ResponseStatus(<code>)}.
1314+
* ping.yaml has a 201 response, so the annotation must appear.
1315+
*/
1316+
@Test
1317+
public void generateQuarkusInterfaceAddsResponseStatusAnnotationForNon200SuccessCode() throws Exception {
1318+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1319+
output.deleteOnExit();
1320+
1321+
final OpenAPI openAPI = new OpenAPIParser()
1322+
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
1323+
1324+
codegen.setOutputDir(output.getAbsolutePath());
1325+
codegen.setLibrary(QUARKUS_LIBRARY); //Given the quarkus library is used
1326+
codegen.additionalProperties().put(INTERFACE_ONLY, true); //And only interfaces are generated
1327+
// returnResponse and returnJBossResponse are both false (defaults)
1328+
1329+
final ClientOptInput input = new ClientOptInput()
1330+
.openAPI(openAPI)
1331+
.config(codegen);
1332+
1333+
final DefaultGenerator generator = new DefaultGenerator();
1334+
final List<File> files = generator.opts(input).generate();
1335+
1336+
validateJavaSourceFiles(files);
1337+
1338+
//Then the generated interface contains the ResponseStatus import and annotation with code 201
1339+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
1340+
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
1341+
"import org.jboss.resteasy.reactive.ResponseStatus;",
1342+
"@ResponseStatus(201)");
1343+
}
1344+
1345+
/**
1346+
* Verify that the {@code @ResponseStatus} annotation is NOT emitted when returnResponse=true,
1347+
* because the user controls the status code via the {@code Response} builder in that mode.
1348+
*/
1349+
@Test
1350+
public void generateQuarkusInterfaceDoesNotAddResponseStatusAnnotationWhenReturnResponse() throws Exception {
1351+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
13071352
output.deleteOnExit();
1353+
1354+
final OpenAPI openAPI = new OpenAPIParser()
1355+
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
1356+
1357+
codegen.setOutputDir(output.getAbsolutePath());
1358+
codegen.setLibrary(QUARKUS_LIBRARY);
1359+
codegen.additionalProperties().put(INTERFACE_ONLY, true);
1360+
codegen.additionalProperties().put(RETURN_RESPONSE, true); //Given returnResponse is true
1361+
1362+
final ClientOptInput input = new ClientOptInput()
1363+
.openAPI(openAPI)
1364+
.config(codegen);
1365+
1366+
final DefaultGenerator generator = new DefaultGenerator();
1367+
final List<File> files = generator.opts(input).generate();
1368+
1369+
validateJavaSourceFiles(files);
1370+
1371+
//Then the annotation must NOT appear
1372+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
1373+
assertFileNotContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
1374+
"@ResponseStatus",
1375+
"import org.jboss.resteasy.reactive.ResponseStatus");
1376+
}
1377+
1378+
/**
1379+
* Verify that the {@code @ResponseStatus} annotation is NOT emitted when the sole success
1380+
* response code is 200, since that is the RESTEasy Reactive default.
1381+
*/
1382+
@Test
1383+
public void generateQuarkusInterfaceDoesNotAddResponseStatusAnnotationFor200Response() throws Exception {
1384+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1385+
output.deleteOnExit();
1386+
1387+
final OpenAPI openAPI = new OpenAPIParser()
1388+
.readLocation("src/test/resources/3_0/petstore.yaml", null, new ParseOptions()).getOpenAPI();
1389+
1390+
codegen.setOutputDir(output.getAbsolutePath());
1391+
codegen.setLibrary(QUARKUS_LIBRARY);
1392+
codegen.additionalProperties().put(INTERFACE_ONLY, true);
1393+
1394+
final ClientOptInput input = new ClientOptInput()
1395+
.openAPI(openAPI)
1396+
.config(codegen);
1397+
1398+
final DefaultGenerator generator = new DefaultGenerator();
1399+
final List<File> files = generator.opts(input).generate();
1400+
1401+
validateJavaSourceFiles(files);
1402+
1403+
//Then the annotation must NOT appear (petstore only uses 200 for success)
1404+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PetApi.java");
1405+
assertFileNotContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PetApi.java"),
1406+
"@ResponseStatus",
1407+
"import org.jboss.resteasy.reactive.ResponseStatus");
1408+
}
1409+
1410+
/**
1411+
* Verify that when using the quarkus library with interfaceOnly=true and a 3xx response,
1412+
* the generated interface method is annotated with {@code @ResponseStatus(<code>)}.
1413+
*/
1414+
@Test
1415+
public void generateQuarkusInterfaceAddsResponseStatusAnnotationFor3xxResponseCode() throws Exception {
1416+
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
1417+
output.deleteOnExit();
1418+
1419+
final OpenAPI openAPI = new OpenAPIParser()
1420+
.readLocation("src/test/resources/3_0/jaxrs-spec-quarkus-redirect.yaml", null, new ParseOptions()).getOpenAPI();
1421+
1422+
codegen.setOutputDir(output.getAbsolutePath());
1423+
codegen.setLibrary(QUARKUS_LIBRARY);
1424+
codegen.additionalProperties().put(INTERFACE_ONLY, true);
1425+
1426+
final ClientOptInput input = new ClientOptInput()
1427+
.openAPI(openAPI)
1428+
.config(codegen);
1429+
1430+
final DefaultGenerator generator = new DefaultGenerator();
1431+
final List<File> files = generator.opts(input).generate();
1432+
1433+
validateJavaSourceFiles(files);
1434+
1435+
//Then the generated interface contains the ResponseStatus import and annotation with code 302
1436+
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/RedirectApi.java");
1437+
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/RedirectApi.java"),
1438+
"import org.jboss.resteasy.reactive.ResponseStatus;",
1439+
"@ResponseStatus(302)");
13081440
}
13091441

13101442
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
openapi: 3.0.1
2+
info:
3+
title: redirect test
4+
version: '1.0'
5+
servers:
6+
- url: 'http://localhost:8000/'
7+
paths:
8+
/redirect:
9+
get:
10+
operationId: redirectGet
11+
responses:
12+
'302':
13+
description: Temporary Redirect
14+
headers:
15+
Location:
16+
schema:
17+
type: string
18+

0 commit comments

Comments
 (0)