From c158c8e7b93ca38cc3c00d600fd54669ae289c39 Mon Sep 17 00:00:00 2001 From: Ameer Abdallah Date: Thu, 30 Oct 2025 14:58:10 -0700 Subject: [PATCH 1/2] #1351: Add missing @JsonProperty to setter methods for POJOs (#1355) * Update templates to support @JsonProperty for setters and add tests for @JsonProperty handling in getters and setters * Replace qualified names with class import for JsonProperty --- .../libraries/microprofile/pojo.qute | 1 + .../microprofile/pojoQueryParam.qute | 1 + .../jsonproperty-getter-and-setter/pom.xml | 93 +++++++++++ .../main/openapi/jsonproperty-openapi.yaml | 129 ++++++++++++++++ .../src/main/resources/application.properties | 1 + .../it/JsonPropertyGetterSetterTest.java | 146 ++++++++++++++++++ client/integration-tests/pom.xml | 1 + 7 files changed, 372 insertions(+) create mode 100644 client/integration-tests/jsonproperty-getter-and-setter/pom.xml create mode 100644 client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.yaml create mode 100644 client/integration-tests/jsonproperty-getter-and-setter/src/main/resources/application.properties create mode 100644 client/integration-tests/jsonproperty-getter-and-setter/src/test/java/io/quarkiverse/openapi/generator/it/JsonPropertyGetterSetterTest.java 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 ef43ddd2b..3452c1e26 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute @@ -85,6 +85,7 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if serializabl /** * Set {v.name} **/ + @JsonProperty("{v.baseName}") public void {v.setter}({v.datatypeWithEnum} {v.name}) { this.{v.name} = {v.name}; } 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 91612c3c5..9db295c16 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute @@ -74,6 +74,7 @@ /** * Set {v.name} **/ + @com.fasterxml.jackson.annotation.JsonProperty("{v.baseName}") public void {v.setter}({v.datatypeWithEnum} {v.name}) { this.{v.name} = {v.name}; } diff --git a/client/integration-tests/jsonproperty-getter-and-setter/pom.xml b/client/integration-tests/jsonproperty-getter-and-setter/pom.xml new file mode 100644 index 000000000..952320339 --- /dev/null +++ b/client/integration-tests/jsonproperty-getter-and-setter/pom.xml @@ -0,0 +1,93 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-jsonproperty-getter-and-setter + Quarkus - OpenAPI Generator - Integration Tests - Client - @JsonProperty for getters and setters + Ensures that @JsonProperty is properly added to getters and setters + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + 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/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.yaml b/client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.yaml new file mode 100644 index 000000000..b4977ab3e --- /dev/null +++ b/client/integration-tests/jsonproperty-getter-and-setter/src/main/openapi/jsonproperty-openapi.yaml @@ -0,0 +1,129 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Animals - OpenAPI 3.0", + "version": "1.0.5" + }, + "servers": [ + { + "url": "/api/v3" + } + ], + "tags": [ + { + "name": "primate", + "description": "Everything about Primates" + } + ], + "paths": { + "/primate/{id}": { + "get": { + "tags": [ + "primate" + ], + "summary": "Find primate by ID", + "description": "Returns a single primate", + "operationId": "getPrimateById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of primate to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Primate" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Primate not found" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "type": "object", + "properties": { + "born": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + }, + "deceased": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + }, + "animal_name": { + "type": "string", + "description": "Animal name" + } + }, + "xml": { + "name": "animal" + } + }, + "Mammal": { + "type": "object", + "allOf": [ { + "$ref": "#/components/schemas/Animal" + } ], + "properties": { + "gender": { + "type": "string", + "enum": [ + "female", + "male" + ] + } + }, + "xml": { + "name": "mammal" + } + }, + "Primate": { + "required": [ + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Mammal" + } + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "jane doe" + } + }, + "xml": { + "name": "primate" + } + } + } + } +} 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 new file mode 100644 index 000000000..964ab5d21 --- /dev/null +++ b/client/integration-tests/jsonproperty-getter-and-setter/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.openapi-generator.codegen.spec.jsonproperty_openapi_yaml.base-package=org.acme.jsonproperty.gettersetter \ No newline at end of file diff --git a/client/integration-tests/jsonproperty-getter-and-setter/src/test/java/io/quarkiverse/openapi/generator/it/JsonPropertyGetterSetterTest.java b/client/integration-tests/jsonproperty-getter-and-setter/src/test/java/io/quarkiverse/openapi/generator/it/JsonPropertyGetterSetterTest.java new file mode 100644 index 000000000..9759e675f --- /dev/null +++ b/client/integration-tests/jsonproperty-getter-and-setter/src/test/java/io/quarkiverse/openapi/generator/it/JsonPropertyGetterSetterTest.java @@ -0,0 +1,146 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; + +import jakarta.json.Json; +import jakarta.json.JsonObjectBuilder; + +import org.acme.jsonproperty.gettersetter.model.Animal; +import org.acme.jsonproperty.gettersetter.model.Mammal; +import org.acme.jsonproperty.gettersetter.model.Primate; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class JsonPropertyGetterSetterTest { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @BeforeAll + static void setup() { + OBJECT_MAPPER.findAndRegisterModules(); + } + + @Test + void verifyGetterSetterWorksOnSnakeCasedFields() { + JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder(); + jsonObjectBuilder.add("animal_name", "Lion"); + + Animal animal; + try { + animal = OBJECT_MAPPER.readValue(jsonObjectBuilder.build().toString(), Animal.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + + assertEquals(Animal.class, animal.getClass()); + assertEquals("Lion", animal.getAnimalName()); + } + + @Test + void verifyGetterSetterWorksOnNonSnakeCasedFields() { + JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder(); + // OffsetDateTime field to verify that non-snake-cased fields also work + jsonObjectBuilder.add("born", "2020-01-01T00:00:00Z"); + + Animal animal; + try { + animal = OBJECT_MAPPER.readValue(jsonObjectBuilder.build().toString(), Animal.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + + assertEquals(Animal.class, animal.getClass()); + DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendInstant().toFormatter(); + assertEquals("2020-01-01T00:00:00Z", animal.getBorn().format(formatter)); + } + + @Test + void verifyJavaReflectionIndicatesJsonPropertyOnSettersAndGetters() { + try { + var getter = Animal.class.getMethod("getAnimalName"); + var setter = Animal.class.getMethod("setAnimalName", String.class); + + var getterAnnotation = getter.getAnnotation(JsonProperty.class); + var setterAnnotation = setter.getAnnotation(JsonProperty.class); + + assertEquals("animal_name", getterAnnotation.value()); + assertEquals("animal_name", setterAnnotation.value()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + try { + var getter = Primate.class.getMethod("getAnimalName"); + var setter = Primate.class.getMethod("setAnimalName", String.class); + + var getterAnnotation = getter.getAnnotation(JsonProperty.class); + var setterAnnotation = setter.getAnnotation(JsonProperty.class); + + assertEquals("animal_name", getterAnnotation.value()); + assertEquals("animal_name", setterAnnotation.value()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + try { + var getter = Mammal.class.getMethod("getAnimalName"); + var setter = Mammal.class.getMethod("setAnimalName", String.class); + + var getterAnnotation = getter.getAnnotation(JsonProperty.class); + var setterAnnotation = setter.getAnnotation(JsonProperty.class); + + assertEquals("animal_name", getterAnnotation.value()); + assertEquals("animal_name", setterAnnotation.value()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + @Test + void verifyJavaReflectionIndicatesJsonPropertyOnSettersAndGettersQueryParam() { + try { + var getter = Animal.AnimalQueryParam.class.getMethod("getAnimalName"); + var setter = Animal.AnimalQueryParam.class.getMethod("setAnimalName", String.class); + + var getterAnnotation = getter.getAnnotation(JsonProperty.class); + var setterAnnotation = setter.getAnnotation(JsonProperty.class); + + assertEquals("animal_name", getterAnnotation.value()); + assertEquals("animal_name", setterAnnotation.value()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + try { + var getter = Mammal.MammalQueryParam.class.getMethod("getAnimalName"); + var setter = Mammal.MammalQueryParam.class.getMethod("setAnimalName", String.class); + + var getterAnnotation = getter.getAnnotation(JsonProperty.class); + var setterAnnotation = setter.getAnnotation(JsonProperty.class); + + assertEquals("animal_name", getterAnnotation.value()); + assertEquals("animal_name", setterAnnotation.value()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + try { + var getter = Primate.PrimateQueryParam.class.getMethod("getAnimalName"); + var setter = Primate.PrimateQueryParam.class.getMethod("setAnimalName", String.class); + + var getterAnnotation = getter.getAnnotation(JsonProperty.class); + var setterAnnotation = setter.getAnnotation(JsonProperty.class); + + assertEquals("animal_name", getterAnnotation.value()); + assertEquals("animal_name", setterAnnotation.value()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } +} diff --git a/client/integration-tests/pom.xml b/client/integration-tests/pom.xml index 661f2ec90..bd9b5cfb0 100644 --- a/client/integration-tests/pom.xml +++ b/client/integration-tests/pom.xml @@ -49,6 +49,7 @@ serializable-model equals-hashcode override-credential-provider + jsonproperty-getter-and-setter From 86b35f3f94368bdc720ecde7a2c846d6de39db92 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:49:17 -0500 Subject: [PATCH 2/2] Fix IT test to LTS version --- client/integration-tests/jsonproperty-getter-and-setter/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/integration-tests/jsonproperty-getter-and-setter/pom.xml b/client/integration-tests/jsonproperty-getter-and-setter/pom.xml index 952320339..d6b77dca5 100644 --- a/client/integration-tests/jsonproperty-getter-and-setter/pom.xml +++ b/client/integration-tests/jsonproperty-getter-and-setter/pom.xml @@ -3,7 +3,7 @@ quarkus-openapi-generator-integration-tests io.quarkiverse.openapi.generator - 3.0.0-SNAPSHOT + 3.0.0-lts-SNAPSHOT 4.0.0