From 82577862702f1739f718a7ad82f32bc85d86a322 Mon Sep 17 00:00:00 2001 From: Ameer Abdallah Date: Thu, 6 Nov 2025 20:12:07 -0800 Subject: [PATCH 1/2] [#1369] Add @SuppressWarnings to generated client classes --- .../template/QuteTemplatingEngineAdapter.java | 1 + .../templates/libraries/microprofile/api.qute | 1 + .../auth/compositeAuthenticationProvider.qute | 1 + .../microprofile/auth/headersFactory.qute | 1 + .../microprofile/multipartFormdataPojo.qute | 1 + .../libraries/microprofile/pojo.qute | 1 + .../microprofile/pojoQueryParam.qute | 1 + .../microprofile/suppressWarnings.qute | 1 + client/integration-tests/pom.xml | 1 + .../suppress-warnings/pom.xml | 98 ++++++++ .../openapi/suppresswarnings-openapi.json | 236 ++++++++++++++++++ .../src/main/resources/application.properties | 1 + .../generator/it/SuppressWarningsTest.java | 83 ++++++ .../ClassWithoutSuppressWarningsExample.java | 5 + 14 files changed, 432 insertions(+) create mode 100644 client/deployment/src/main/resources/templates/libraries/microprofile/suppressWarnings.qute create mode 100644 client/integration-tests/suppress-warnings/pom.xml create mode 100644 client/integration-tests/suppress-warnings/src/main/openapi/suppresswarnings-openapi.json create mode 100644 client/integration-tests/suppress-warnings/src/main/resources/application.properties create mode 100644 client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/SuppressWarningsTest.java create mode 100644 client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/classwithoutsuppresswarningsexample/ClassWithoutSuppressWarningsExample.java diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java index 651ee91b3..33978f25d 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java @@ -13,6 +13,7 @@ public class QuteTemplatingEngineAdapter extends AbstractTemplatingEngineAdapter private static final String IDENTIFIER = "qute"; private static final String[] DEFAULT_TEMPLATES = { + "suppressWarnings.qute", "additionalEnumTypeAnnotations.qute", "additionalEnumTypeUnexpectedMember.qute", "additionalModelTypeAnnotations.qute", diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute index 78dacb610..214524aa8 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute @@ -28,6 +28,7 @@ import {imp.import}; {#for apiAnnotation in additionalApiTypeAnnotations.orEmpty} {apiAnnotation} {/for} +{#include suppressWarnings.qute/} public interface {classname} { {#for op in operations.operation} diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute index 29b73b8cc..7e02949cd 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute @@ -22,6 +22,7 @@ package {apiPackage}.auth; @io.quarkiverse.openapi.generator.markers.ApiKeyAuthenticationMarker(name="{auth.name}", openApiSpecId="{quarkus-generator.openApiSpecId}", apiKeyIn=io.quarkiverse.openapi.generator.providers.ApiKeyIn.cookie, apiKeyName="{auth.keyParamName}") {/if} {/for} +{#include suppressWarnings.qute/} public class CompositeAuthenticationProvider implements jakarta.ws.rs.client.ClientRequestFilter { @jakarta.inject.Inject diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute index ddebaffec..c7ad15d7c 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute @@ -1,5 +1,6 @@ package {apiPackage}.auth; +{#include suppressWarnings.qute/} public class AuthenticationPropagationHeadersFactory extends io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory { @jakarta.inject.Inject diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/multipartFormdataPojo.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/multipartFormdataPojo.qute index bd0749a4f..2d7ac960b 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/multipartFormdataPojo.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/multipartFormdataPojo.qute @@ -1,3 +1,4 @@ + {#include suppressWarnings.qute/} public static class {op.operationIdCamelCase}MultipartForm { {#for p in op.formParams} @jakarta.ws.rs.FormParam("{p.baseName}") diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute index 3452c1e26..91c09bccc 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; {#else} @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) {/if} +{#include suppressWarnings.qute/} public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if serializableModel} implements java.io.Serializable{/if} { {#for v in m.vars} diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute index 9db295c16..4e7736c39 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute @@ -5,6 +5,7 @@ {/if} @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) {#include additionalModelTypeAnnotations.qute m=m/} + {#include suppressWarnings.qute/} public static class {m.classname}QueryParam {#if m.parent}extends {m.parent}{/if}{#if serializableModel} implements java.io.Serializable{/if} { {#for v in m.vars} diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/suppressWarnings.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/suppressWarnings.qute new file mode 100644 index 000000000..a415a8d6a --- /dev/null +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/suppressWarnings.qute @@ -0,0 +1 @@ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) diff --git a/client/integration-tests/pom.xml b/client/integration-tests/pom.xml index cb18f51f5..80118affc 100644 --- a/client/integration-tests/pom.xml +++ b/client/integration-tests/pom.xml @@ -51,6 +51,7 @@ equals-hashcode override-credential-provider jsonproperty-getter-and-setter + suppress-warnings diff --git a/client/integration-tests/suppress-warnings/pom.xml b/client/integration-tests/suppress-warnings/pom.xml new file mode 100644 index 000000000..98eac0635 --- /dev/null +++ b/client/integration-tests/suppress-warnings/pom.xml @@ -0,0 +1,98 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-suppress-warnings + Quarkus - OpenAPI Generator - Integration Tests - Client - Suppress Warnings Test + Ensures that generated classes have the @SuppressWarnings annotation + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + com.github.javaparser + javaparser-symbol-solver-core + 3.27.1 + test + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/suppress-warnings/src/main/openapi/suppresswarnings-openapi.json b/client/integration-tests/suppress-warnings/src/main/openapi/suppresswarnings-openapi.json new file mode 100644 index 000000000..a6f25c583 --- /dev/null +++ b/client/integration-tests/suppress-warnings/src/main/openapi/suppresswarnings-openapi.json @@ -0,0 +1,236 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team", + "email": "apiteam@swagger.io", + "url": "http://swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "https://petstore.swagger.io/v2" + } + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to\nNam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia.\n\nSed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien.\n", + "operationId": "findPets", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "required": false, + "style": "form", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "description": "Creates a new pet in the store. Duplicates are allowed", + "operationId": "addPet", + "requestBody": { + "description": "Pet to add to the store", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewPet" + } + } + } + }, + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{id}": { + "get": { + "description": "Returns a user based on a single ID, if the user does not have access to the pet", + "operationId": "find pet by id", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to fetch", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "description": "deletes a single pet based on the ID supplied", + "operationId": "deletePet", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "204": { + "description": "pet deleted" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "allOf": [ + { + "$ref": "#/components/schemas/NewPet" + }, + { + "type": "object", + "required": ["id"], + "properties": { + "id": { + "type": "integer", + "format": "int64" + } + } + } + ] + }, + "NewPet": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "type": "object", + "required": ["code", "message"], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} + diff --git a/client/integration-tests/suppress-warnings/src/main/resources/application.properties b/client/integration-tests/suppress-warnings/src/main/resources/application.properties new file mode 100644 index 000000000..ac87c8af0 --- /dev/null +++ b/client/integration-tests/suppress-warnings/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.openapi-generator.codegen.spec.suppresswarnings_openapi_json.base-package=org.acme.suppresswarnings \ No newline at end of file diff --git a/client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/SuppressWarningsTest.java b/client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/SuppressWarningsTest.java new file mode 100644 index 000000000..a6a895b9f --- /dev/null +++ b/client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/SuppressWarningsTest.java @@ -0,0 +1,83 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.CompilationUnit; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class SuppressWarningsTest { + + @Test + void testSuppressWarningsAnnotationsExist() throws IOException { + List javaFiles = getJavaFilesInDirectory("target/generated-sources/open-api/org/acme/suppresswarnings"); + + assertFalse(javaFiles.isEmpty(), "No Java files found to verify"); + + List violations = getClassesWithNoSuppressWarningsAnnotation(javaFiles); + + assertTrue(violations.isEmpty(), + "Classes missing @SuppressWarnings:\n" + String.join("\n", violations)); + } + + /** + * Negative test to ensure that the checker correctly identifies classes missing the + * \@SuppressWarnings annotation. + */ + @Test + void testSuppressWarningsCheckerFailsIfAnnotationMissing() throws IOException { + List javaFiles = getJavaFilesInDirectory( + "src/test/java/io/quarkiverse/openapi/generator/it/classwithoutsuppresswarningsexample"); + + List violations = getClassesWithNoSuppressWarningsAnnotation(javaFiles); + + assertFalse(violations.isEmpty(), "Expected violations for classes missing @SuppressWarnings"); + } + + private List getJavaFilesInDirectory(String directory) throws IOException { + Path sourceRoot = Paths.get(directory); + + try (Stream pathStream = Files.walk(sourceRoot)) { + return pathStream + .filter(Files::isRegularFile) + .filter(p -> p.toString().endsWith(".java")) + .toList(); + } + } + + private List getClassesWithNoSuppressWarningsAnnotation(List javaFiles) { + List violations = new ArrayList<>(); + + // Java reflection on classes loaded from ClassLoader does not work since it strips + // SuppressWarnings annotations during compilation. Gotta manually parse the source files. + for (Path javaFile : javaFiles) { + try { + CompilationUnit cu = StaticJavaParser.parse(javaFile); + + // Check all class/interface declarations in the file (only checks top level classes) + cu.getPrimaryType().ifPresent(typeDecl -> { + if (typeDecl.getAnnotationByName(SuppressWarnings.class.getSimpleName()).isEmpty()) { + violations.add(javaFile + " : " + typeDecl.getNameAsString() + " missing @SuppressWarnings"); + } + }); + + } catch (Exception e) { + violations.add(javaFile + " : Failed to parse - " + e.getMessage()); + } + } + return violations; + } +} diff --git a/client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/classwithoutsuppresswarningsexample/ClassWithoutSuppressWarningsExample.java b/client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/classwithoutsuppresswarningsexample/ClassWithoutSuppressWarningsExample.java new file mode 100644 index 000000000..926c21990 --- /dev/null +++ b/client/integration-tests/suppress-warnings/src/test/java/io/quarkiverse/openapi/generator/it/classwithoutsuppresswarningsexample/ClassWithoutSuppressWarningsExample.java @@ -0,0 +1,5 @@ +package io.quarkiverse.openapi.generator.it.classwithoutsuppresswarningsexample; + +// A class without @SuppressWarnings annotation for testing purposes. Do not delete! +public class ClassWithoutSuppressWarningsExample { +} From b22406b9f5c73e9bf193d99ed7bd1c7b6269f475 Mon Sep 17 00:00:00 2001 From: Ameer Abdallah Date: Thu, 6 Nov 2025 20:13:00 -0800 Subject: [PATCH 2/2] Fix openapi file extension --- .../{jsonproperty-openapi.yaml => jsonproperty-openapi.json} | 0 .../src/main/resources/application.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/{jsonproperty-openapi.yaml => jsonproperty-openapi.json} (100%) diff --git a/client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.yaml b/client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.json similarity index 100% rename from client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.yaml rename to client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.json diff --git a/client/integration-tests/jsonproperty-getter-and-setter/src/main/resources/application.properties b/client/integration-tests/jsonproperty-getter-and-setter/src/main/resources/application.properties index 964ab5d21..d2f99b87c 100644 --- a/client/integration-tests/jsonproperty-getter-and-setter/src/main/resources/application.properties +++ b/client/integration-tests/jsonproperty-getter-and-setter/src/main/resources/application.properties @@ -1 +1 @@ -quarkus.openapi-generator.codegen.spec.jsonproperty_openapi_yaml.base-package=org.acme.jsonproperty.gettersetter \ No newline at end of file +quarkus.openapi-generator.codegen.spec.jsonproperty_openapi_json.base-package=org.acme.jsonproperty.gettersetter \ No newline at end of file