Skip to content

Commit

Permalink
chore: upgrade to openapi generator 7.11 (#2517)
Browse files Browse the repository at this point in the history
Upgraded to openapi generator version 7.11. One of our issues was fixed
in this version but we got two new issues back for which I added
workarounds.. It looks like both are caused by the new support for
JSON-B polymorphism type annotations introduced by
OpenAPITools/openapi-generator#20164:

Solves PZ-5160
  • Loading branch information
edgarvonk authored Jan 27, 2025
1 parent efe56a4 commit c277176
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 82 deletions.
10 changes: 6 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -517,12 +517,15 @@ tasks {
"sourceFolder" to "",
"dateLibrary" to "java8",
"disallowAdditionalPropertiesIfNotPresent" to "false",
"openApiNullable" to "false",
"useJakartaEe" to "true"
)
)
// Specify custom Mustache template dir as temporary workaround for the issue where OpenAPI Generator
// fails to generate import statements for @JsonbCreator annotations.
// Specify custom Mustache template dir as temporary workaround for issues we have with the OpenAPI Generator.
// Both issues have to do with the support for JSON-B polymorphism type annotations introduced by
// https://github.com/OpenAPITools/openapi-generator/pull/20164 in OpenAPI Generator version 7.11.
// Instead of overriding these Mustache templates the obvious workaround seems to set the additional property
// 'jsonbPolymorphism' to false in this Gradle build file. However, that does not seem to work.
// Probably because this property is set by the OpenAPI Generator library itself regardless of our configuration.
templateDir.set("$rootDir/src/main/resources/openapi-generator-templates")
}

Expand Down Expand Up @@ -568,7 +571,6 @@ tasks {
"sourceFolder" to "",
"dateLibrary" to "java8-localdatetime",
"disallowAdditionalPropertiesIfNotPresent" to "false",
"openApiNullable" to "false",
"useJakartaEe" to "true"
)
)
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ nodejs = "20.17.0"
jsonschema2pojo = "1.2.2"
kotlin-csv = "1.10.0"
openapi = "4.0.7"
openapi-generator = "7.10.0"
openapi-generator = "7.11.0"
opentelemetry = "1.46.0"
opentelemetry-instrumentation = "2.12.0"
node-gradle = "7.1.0"
Expand Down
2 changes: 1 addition & 1 deletion src/itest/kotlin/nl/info/zac/itest/KlantRestServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ class KlantRestServiceTest : BehaviorSpec({
the response should be a 200 HTTP response with personal data from both the BRP and Klanten databases
"""
) {
response.code shouldBe HTTP_STATUS_OK
val responseBody = response.body!!.string()
logger.info { "Response: $responseBody" }
response.code shouldBe HTTP_STATUS_OK
responseBody shouldEqualJson """
{
"bsn": "$TEST_PERSON_HENDRIKA_JANSE_BSN",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
import org.apache.commons.lang3.StringUtils;

import net.atos.client.bag.model.generated.PointGeoJSON;
import net.atos.client.bag.model.generated.PolygonGeoJSON;
import net.atos.client.bag.model.generated.PuntOfVlak;
import net.atos.client.bag.model.generated.Surface;
import net.atos.client.zgw.zrc.model.Zaak;
import net.atos.client.zgw.zrc.model.zaakobjecten.Zaakobject;
import net.atos.client.zgw.zrc.model.zaakobjecten.ZaakobjectAdres;
Expand Down Expand Up @@ -95,11 +95,11 @@ public static String getHuisnummerWeergave(final Integer huisnummer, final Strin
return volledigHuisnummer.toString().trim();
}

public static RestGeometry convertVlak(final PolygonGeoJSON polygonGeoJSON) {
public static RestGeometry convertVlak(final Surface surface) {
return new RestGeometry(
polygonGeoJSON.getType().value(),
surface.getType().value(),
null,
polygonGeoJSON.getCoordinates()
surface.getCoordinates()
.stream()
.map(coords -> coords.stream()
.map(RESTBAGConverter::convertCoordinates)
Expand Down
64 changes: 0 additions & 64 deletions src/main/resources/openapi-generator-templates/model.mustache

This file was deleted.

166 changes: 166 additions & 0 deletions src/main/resources/openapi-generator-templates/pojo.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
{{! Lifely/INFO temporary workaround for the issue where the OpenAPI Generator Gradle plugin }}
{{! incorrectly generates '@JsonbTransient' annotations for certain 'type' fields in polymorphic model classes }}
{{! which has the result that these fields are not serialized and deserialized leading to errors in the various APIs we use. }}
{{! For example in the generated BRP 'PersonenQuery.java' class the 'type' String field was given the '@JsonbTransient' annotation }}
{{! which breaks the ZAC BRP API integration because this is mandatory field in the BRP API. }}
{{! This was most likely introduced by: https://github.com/OpenAPITools/openapi-generator/pull/20164 }}
{{! It may also be that the BRP OpenAPI spec does not use the discriminator keyword correctly since these 'type' fields are simple strings. }}
{{! and not polymorphic structures. Also see: https://apidog.com/blog/openapi-discriminator-guide/ }}

{{! This template is based on a copy of the Java Mustache model template of the main branch of https://github.com/OpenAPITools/openapi-generator }}
{{! This workaround can be removed once we have migrated to a version of the OpenAPI Generator Gradle plugin that has solved this issue. }}

{{#withXml}}
{{#hasVars}}@XmlType(name = "{{classname}}", propOrder =
{ {{#vars}}"{{name}}"{{^-last}}, {{/-last}}{{/vars}} }
){{/hasVars}}
{{^hasVars}}@XmlType(name = "{{classname}}"){{/hasVars}}
{{> xmlAnnotation }}
{{/withXml}}
{{#jackson}}
@JsonPropertyOrder({
{{#vars}}
{{classname}}.JSON_PROPERTY_{{nameInSnakeCase}}{{^-last}},{{/-last}}
{{/vars}}
})
{{#isClassnameSanitized}}
{{^hasDiscriminatorWithNonEmptyMapping}}
@JsonTypeName("{{name}}")
{{/hasDiscriminatorWithNonEmptyMapping}}
{{/isClassnameSanitized}}
{{/jackson}}
{{#description}}
/**
* {{{.}}}
*/
{{/description}}
{{>additionalModelTypeAnnotations}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}
{{#vendorExtensions.x-class-extra-annotation}}
{{{vendorExtensions.x-class-extra-annotation}}}
{{/vendorExtensions.x-class-extra-annotation}}
public class {{classname}} {{#parent}}extends {{{.}}}{{/parent}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {
{{#vars}}{{#isEnum}}{{^isContainer}}
{{>enumClass}}{{/isContainer}}{{#isContainer}}{{#mostInnerItems}}
{{>enumClass}}{{/mostInnerItems}}{{/isContainer}}{{/isEnum}}
{{#jackson}}
public static final String JSON_PROPERTY_{{nameInSnakeCase}} = "{{baseName}}";
{{/jackson}}
{{#withXml}}
@Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
{{#isXmlWrapped}}
@XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
{{/isXmlWrapped}}
{{/withXml}}
{{#description}}
/**
* {{{.}}}
*/
{{/description}}
{{^withXml}}
{{! Lifely/INFO. Changed this line to prevent the generation of the @JsonbTransient annotation for polymorphism. See reasons above. }}
{{#jsonb}}@JsonbProperty("{{baseName}}"){{/jsonb}}
{{! Lifely/INFO. End of change. }}
{{/withXml}}
{{#vendorExtensions.x-field-extra-annotation}}
{{{vendorExtensions.x-field-extra-annotation}}}
{{/vendorExtensions.x-field-extra-annotation}}
{{#isContainer}}
private {{{datatypeWithEnum}}} {{name}}{{#required}} = {{{defaultValue}}}{{/required}}{{^required}} = null{{/required}};
{{/isContainer}}
{{^isContainer}}
private {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{/isContainer}}
{{/vars}}
{{#vendorExtensions.x-has-readonly-properties}}{{^withXml}}
public {{classname}}() {
}

{{#jsonb}}@JsonbCreator{{/jsonb}}{{#jackson}}@JsonCreator{{/jackson}}
public {{classname}}(
{{#readOnlyVars}}
{{#jsonb}}@JsonbProperty(value = "{{baseName}}"{{^required}}, nillable = true{{/required}}){{/jsonb}}{{#jackson}}@JsonProperty(value = JSON_PROPERTY_{{nameInSnakeCase}}{{#required}}, required = true{{/required}}){{/jackson}} {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}
{{/readOnlyVars}}
) {
{{#readOnlyVars}}
this.{{name}} = {{name}};
{{/readOnlyVars}}
}
{{/withXml}}{{/vendorExtensions.x-has-readonly-properties}}
{{#vars}}
/**
{{#description}}
* {{.}}
{{/description}}
{{^description}}
* Get {{name}}
{{/description}}
{{#minimum}}
* minimum: {{.}}
{{/minimum}}
{{#maximum}}
* maximum: {{.}}
{{/maximum}}
* @return {{name}}
{{#deprecated}}
* @deprecated
{{/deprecated}}
**/
{{#deprecated}}
@Deprecated
{{/deprecated}}
{{#vendorExtensions.x-extra-annotation}}
{{{vendorExtensions.x-extra-annotation}}}
{{/vendorExtensions.x-extra-annotation}}
{{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}}{{#jackson}}{{> jackson_annotations}}{{/jackson}} {{#withXml}}{{#isEnum}}{{^isArray}}{{^isMap}} public {{dataType}} {{getter}}() {
if ({{name}} == null) {
return null;
}
return {{name}}.value();
}{{/isMap}}{{/isArray}}{{/isEnum}}{{/withXml}}{{^withXml}}{{#isEnum}}{{^isArray}}{{^isMap}}public {{datatypeWithEnum}} {{getter}}() {
return {{name}};
}{{/isMap}}{{/isArray}}{{/isEnum}}{{/withXml}}{{#isEnum}}{{#isArray}}public {{{datatypeWithEnum}}} {{getter}}() {
return {{name}};
}{{/isArray}}{{/isEnum}}{{#isEnum}}{{#isMap}}public {{{datatypeWithEnum}}} {{getter}}() {
return {{name}};
}{{/isMap}}{{/isEnum}}{{^isEnum}}public {{{datatypeWithEnum}}} {{getter}}() {
return {{name}};
}{{/isEnum}}

{{^isReadOnly}}
/**
* Set {{name}}
*/
{{#vendorExtensions.x-setter-extra-annotation}} {{{vendorExtensions.x-setter-extra-annotation}}}
{{/vendorExtensions.x-setter-extra-annotation}}{{#jackson}}{{> jackson_annotations}}{{/jackson}} public void {{setter}}({{{datatypeWithEnum}}} {{name}}) {
this.{{name}} = {{name}};
}

public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) {
this.{{name}} = {{name}};
return this;
}
{{#isArray}}

public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) {
if (this.{{name}} == null) {
this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}};
}
this.{{name}}.add({{name}}Item);
return this;
}
{{/isArray}}
{{#isMap}}

public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) {
if (this.{{name}} == null) {
this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}};
}
this.{{name}}.put(key, {{name}}Item);
return this;
}
{{/isMap}}
{{/isReadOnly}}

{{/vars}}
{{>pojoOverrides}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{! Lifely/INFO temporary workaround for the issue where the OpenAPI Generator Gradle plugin }}
{{! generates '@JsonbTypeInfo(key = "type")' annotations in the generated model classes both for }}
{{! parent and child polymorphic classes resulting in errors such as: }}
{{! "Caused by: jakarta.json.bind.JsonbException: One polymorphic chain cannot have two conflicting property names. }}
{{! Polymorphic type defined on the type net.atos.client.bag.model.generated.Standplaats }}
{{! and net.atos.client.bag.model.generated.AdresseerbaarObject have conflicting property name" }}
{{! This was most likely introduced by: https://github.com/OpenAPITools/openapi-generator/pull/20164 }}

{{! This template is based on a copy of the Java Mustache model template of the main branch of https://github.com/OpenAPITools/openapi-generator }}
{{! This workaround can be removed once we have migrated to a version of the OpenAPI Generator Gradle plugin that has solved this issue. }}

{{#jackson}}

@JsonIgnoreProperties(
value = "{{{discriminator.propertyBaseName}}}", // ignore manually set {{{discriminator.propertyBaseName}}}, it will be automatically generated by Jackson during serialization
allowSetters = true // allows the {{{discriminator.propertyBaseName}}} to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminator.propertyBaseName}}}", visible = true)
{{#discriminator.mappedModels}}
{{#-first}}
@JsonSubTypes({
{{/-first}}
@JsonSubTypes.Type(value = {{modelName}}.class, name = "{{^vendorExtensions.x-discriminator-value}}{{mappingName}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}"),
{{#-last}}
})
{{/-last}}
{{/discriminator.mappedModels}}
{{/jackson}}
{{#jsonbPolymorphism}}
{{! Workaround Lifely/INFO: the original code has been removed for reasons see above }}
{{/jsonbPolymorphism}}
10 changes: 5 additions & 5 deletions src/test/kotlin/net/atos/zac/app/bag/BagFixtures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import net.atos.client.bag.model.generated.AdresseerbaarObjectIOHal
import net.atos.client.bag.model.generated.Ligplaats
import net.atos.client.bag.model.generated.LigplaatsIOHal
import net.atos.client.bag.model.generated.PointGeoJSON
import net.atos.client.bag.model.generated.PolygonGeoJSON
import net.atos.client.bag.model.generated.PuntOfVlak
import net.atos.client.bag.model.generated.Standplaats
import net.atos.client.bag.model.generated.StandplaatsIOHal
import net.atos.client.bag.model.generated.StatusPlaats
import net.atos.client.bag.model.generated.StatusVerblijfsobject
import net.atos.client.bag.model.generated.Surface
import net.atos.client.bag.model.generated.Verblijfsobject
import net.atos.client.bag.model.generated.VerblijfsobjectIOHal
import java.math.BigDecimal
Expand All @@ -24,7 +24,7 @@ fun createLigplaatsAdresseerbaarObject(status: StatusPlaats) =
ligplaats = LigplaatsIOHal().apply {
ligplaats = Ligplaats().apply {
this.status = status
this.geometrie = createPolygonGeoJSON()
this.geometrie = createSurface()
}
}
}
Expand All @@ -34,7 +34,7 @@ fun createStandplaatsAdresseerbaarObject(status: StatusPlaats) =
standplaats = StandplaatsIOHal().apply {
standplaats = Standplaats().apply {
this.status = status
this.geometrie = createPolygonGeoJSON()
this.geometrie = createSurface()
}
}
}
Expand All @@ -56,8 +56,8 @@ fun createPuntOfVlak() = PuntOfVlak().apply {
}
}

fun createPolygonGeoJSON() = PolygonGeoJSON().apply {
type = PolygonGeoJSON.TypeEnum.POLYGON
fun createSurface() = Surface().apply {
type = Surface.TypeEnum.POLYGON
coordinates = listOf(listOf(createCoordinates())) as List<List<List<BigDecimal?>?>?>?
}

Expand Down
Loading

0 comments on commit c277176

Please sign in to comment.