diff --git a/customer-service-client/pom.xml b/customer-service-client/pom.xml
index b3a57ae..451ded1 100644
--- a/customer-service-client/pom.xml
+++ b/customer-service-client/pom.xml
@@ -6,7 +6,7 @@
io.github.bsayli
customer-service-client
- 0.5.0
+ 0.6.0
customer-service-client
Generated client (RestClient) using generics-aware OpenAPI templates
jar
@@ -20,16 +20,20 @@
7.15.0
3.13.0
3.6.0
+ 3.3.1
+ 3.8.1
3.1.1
3.0.0
5.1.0
5.5
5.13.4
+
+ ${project.build.directory}/upstream-templates
+ ${project.build.directory}/effective-templates
-
org.springframework.boot
spring-boot-starter-web
@@ -80,18 +84,90 @@
-
src/main/resources
- **/*.yaml
openapi-templates/**
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ ${maven.dependency.plugin.version}
+
+
+ unpack-openapi-upstream-templates
+ generate-sources
+
+ unpack
+
+
+
+
+ org.openapitools
+ openapi-generator
+ ${openapi.generator.version}
+ jar
+ true
+ templates/Java/**
+ ${openapi.templates.upstream}
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven.resources.plugin.version}
+
+
+ copy-upstream-to-effective
+ generate-sources
+
+ copy-resources
+
+
+ ${openapi.templates.effective}
+
+
+ ${openapi.templates.upstream}/templates
+
+ Java/**
+
+
+
+
+
+
+
+ overlay-local-templates
+ generate-sources
+
+ copy-resources
+
+
+ ${openapi.templates.effective}/Java
+ true
+
+
+ src/main/resources/openapi-templates
+ false
+
+ **/*.mustache
+
+
+
+
+
+
+
org.openapitools
@@ -100,6 +176,7 @@
generate-client
+ generate-sources
generate
@@ -113,8 +190,7 @@
io.github.bsayli.openapi.client.generated.dto
io.github.bsayli.openapi.client.generated.invoker
- ${project.basedir}/src/main/resources/openapi-templates
-
+ ${openapi.templates.effective}/Java
true
false
@@ -131,7 +207,7 @@
java8
true
false
- src/gen/java/main
+ src/gen/java
@@ -151,7 +227,7 @@
- ${project.build.directory}/generated-sources/openapi/src/gen/java/main
+ ${project.build.directory}/generated-sources/openapi/src/gen/java
diff --git a/customer-service-client/src/main/resources/customer-api-docs.yaml b/customer-service-client/src/main/resources/customer-api-docs.yaml
index a773c19..a45aa19 100644
--- a/customer-service-client/src/main/resources/customer-api-docs.yaml
+++ b/customer-service-client/src/main/resources/customer-api-docs.yaml
@@ -2,7 +2,7 @@ openapi: 3.1.0
info:
title: Customer Service API
description: "Demo: type-safe generic API responses with OpenAPI"
- version: 0.3.0
+ version: 0.6.0
servers:
- url: http://localhost:8084/customer-service
description: Local service URL
diff --git a/customer-service-client/src/main/resources/openapi-templates/modelEnum.mustache b/customer-service-client/src/main/resources/openapi-templates/modelEnum.mustache
deleted file mode 100644
index 90cd7f8..0000000
--- a/customer-service-client/src/main/resources/openapi-templates/modelEnum.mustache
+++ /dev/null
@@ -1,120 +0,0 @@
-{{#jackson}}
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-{{/jackson}}
-{{#gson}}
-import java.io.IOException;
-import com.google.gson.TypeAdapter;
-import com.google.gson.annotations.JsonAdapter;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
-{{/gson}}
-{{#isUri}}
-import java.net.URI;
-{{/isUri}}
-
-/**
- * {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}}
- */
-{{#gson}}
-@JsonAdapter({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Adapter.class)
-{{/gson}}
-{{#jsonb}}
-@JsonbTypeSerializer({{datatypeWithEnum}}.Serializer.class)
-@JsonbTypeDeserializer({{datatypeWithEnum}}.Deserializer.class)
-{{/jsonb}}
-{{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {
- {{#allowableValues}}{{#enumVars}}
- {{#enumDescription}}
- /**
- * {{.}}
- */
- {{/enumDescription}}
- {{#withXml}}
- @XmlEnumValue({{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}})
- {{/withXml}}
- {{{name}}}({{{value}}}){{^-last}},
- {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}}
-
- private {{{dataType}}} value;
-
- {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) {
- this.value = value;
- }
-
-{{#jackson}}
- @JsonValue
-{{/jackson}}
- public {{{dataType}}} getValue() {
- return value;
- }
-
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-{{#jackson}}
- @JsonCreator
-{{/jackson}}
- public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) {
- for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
- if (b.value.{{^isString}}equals{{/isString}}{{#isString}}{{#useEnumCaseInsensitive}}equalsIgnoreCase{{/useEnumCaseInsensitive}}{{^useEnumCaseInsensitive}}equals{{/useEnumCaseInsensitive}}{{/isString}}(value)) {
- return b;
- }
- }
- {{#isNullable}}return null;{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}{{#allowableValues}}{{#enumVars}}{{#-last}}return {{{name}}};{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}throw new IllegalArgumentException("Unexpected value '" + value + "'");{{/enumUnknownDefaultCase}}{{/isNullable}}
- }
-{{#gson}}
-
- public static class Adapter extends TypeAdapter<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> {
- @Override
- public void write(final JsonWriter jsonWriter, final {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} enumeration) throws IOException {
- jsonWriter.value(enumeration.getValue(){{#isUri}}.toASCIIString(){{/isUri}});
- }
-
- @Override
- public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException {
- {{^isNumber}}{{{dataType}}}{{/isNumber}}{{#isNumber}}String{{/isNumber}} value = {{#isFloat}}(float){{/isFloat}}{{#isUri}}URI.create({{/isUri}}jsonReader.{{#isNumber}}nextString(){{/isNumber}}{{#isInteger}}nextInt(){{/isInteger}}{{#isUri}}nextString()){{/isUri}}{{^isNumber}}{{^isInteger}}{{^isUri}}{{#isFloat}}nextDouble{{/isFloat}}{{^isFloat}}next{{{dataType}}}{{/isFloat}}(){{/isUri}}{{/isInteger}}{{/isNumber}};
- return {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}.fromValue({{#isNumber}}new BigDecimal({{/isNumber}}value{{#isNumber}}){{/isNumber}});
- }
- }
-{{/gson}}
-{{#jsonb}}
-
- public static final class Deserializer implements JsonbDeserializer<{{datatypeWithEnum}}> {
- @Override
- public {{datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
- for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
- if (String.valueOf(b.value).equals(parser.getString())) {
- return b;
- }
- }
- {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}}
- }
- }
-
- public static final class Serializer implements JsonbSerializer<{{datatypeWithEnum}}> {
- @Override
- public void serialize({{datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) {
- generator.write(obj.value);
- }
- }
-{{/jsonb}}
-{{#supportUrlQuery}}
-
- /**
- * Convert the instance into URL query string.
- *
- * @param prefix prefix of the query string
- * @return URL query string
- */
- public String toUrlQueryString(String prefix) {
- if (prefix == null) {
- prefix = "";
- }
-
- return String.format("%s=%s", prefix, this.toString());
- }
-{{/supportUrlQuery}}
-}
diff --git a/customer-service-client/src/main/resources/openapi-templates/oneof_interface.mustache b/customer-service-client/src/main/resources/openapi-templates/oneof_interface.mustache
deleted file mode 100644
index eadcb26..0000000
--- a/customer-service-client/src/main/resources/openapi-templates/oneof_interface.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-{{>additionalOneOfTypeAnnotations}}{{>generatedAnnotation}}{{>typeInfoAnnotation}}{{>xmlAnnotation}}
-public {{>sealed}}interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}}{{>permits}}{
- {{#discriminator}}
- public {{propertyType}} {{propertyGetter}}();
- {{/discriminator}}
-}
diff --git a/customer-service-client/src/main/resources/openapi-templates/pojo.mustache b/customer-service-client/src/main/resources/openapi-templates/pojo.mustache
deleted file mode 100644
index 25974f2..0000000
--- a/customer-service-client/src/main/resources/openapi-templates/pojo.mustache
+++ /dev/null
@@ -1,624 +0,0 @@
-/**
- * {{description}}{{^description}}{{classname}}{{/description}}{{#isDeprecated}}
- * @deprecated{{/isDeprecated}}
- */{{#isDeprecated}}
-@Deprecated{{/isDeprecated}}
-{{#swagger1AnnotationLibrary}}
-{{#description}}
-@ApiModel(description = "{{{.}}}")
-{{/description}}
-{{/swagger1AnnotationLibrary}}
-{{#swagger2AnnotationLibrary}}
-{{#description}}
-@Schema(description = "{{{.}}}")
-{{/description}}
-{{/swagger2AnnotationLibrary}}
-{{#jackson}}
-@JsonPropertyOrder({
-{{#vars}}
- {{classname}}.JSON_PROPERTY_{{nameInSnakeCase}}{{^-last}},{{/-last}}
-{{/vars}}
-})
-{{#isClassnameSanitized}}
-{{^hasDiscriminatorWithNonEmptyMapping}}
-@JsonTypeName("{{name}}")
-{{/hasDiscriminatorWithNonEmptyMapping}}
-{{/isClassnameSanitized}}
-{{/jackson}}
-{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}}
-{{#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}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{
-{{#serializableModel}}
- private static final long serialVersionUID = 1L;
-
-{{/serializableModel}}
- {{#vars}}
- {{#isEnum}}
- {{^isContainer}}
-{{>modelInnerEnum}}
-
- {{/isContainer}}
- {{#isContainer}}
- {{#mostInnerItems}}
-{{>modelInnerEnum}}
-
- {{/mostInnerItems}}
- {{/isContainer}}
- {{/isEnum}}
- {{#gson}}
- public static final String SERIALIZED_NAME_{{nameInSnakeCase}} = "{{baseName}}";
- {{/gson}}
- {{#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}}
- {{^isXmlAttribute}}
- {{#isDateTime}}
- @XmlJavaTypeAdapter(OffsetDateTimeXmlAdapter.class)
- {{/isDateTime}}
- {{/isXmlAttribute}}
- {{/withXml}}
- {{#gson}}
- @SerializedName(SERIALIZED_NAME_{{nameInSnakeCase}})
- {{/gson}}
- {{^isDiscriminator}}
- {{>nullable_var_annotations}}{{! prevent indent}}
- {{/isDiscriminator}}
- {{#isDiscriminator}}
- // The discriminator does not have Nullability-annotation since it is added during serialization by the @JsonTypeName annotation
- {{/isDiscriminator}}
- {{#vendorExtensions.x-field-extra-annotation}}
- {{{vendorExtensions.x-field-extra-annotation}}}
- {{/vendorExtensions.x-field-extra-annotation}}
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
- {{#isContainer}}
- {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();
- {{/isContainer}}
- {{^isContainer}}
- {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}};
- {{/isContainer}}
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}
- {{#isContainer}}
- {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
- {{/isContainer}}
- {{^isContainer}}
- {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
- {{/isContainer}}
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
-
- {{/vars}}
- public {{classname}}() {
- {{#parent}}
- {{#parcelableModel}}
- super();{{/parcelableModel}}
- {{/parent}}
- {{#gson}}
- {{#discriminator}}
- {{#discriminator.isEnum}}
- this.{{{discriminatorName}}} = this.getClass().getSimpleName();
- {{/discriminator.isEnum}}
- {{/discriminator}}
- {{/gson}}
- }
- {{#vendorExtensions.x-has-readonly-properties}}
- {{^withXml}}
- /**
- * Constructor with only readonly parameters{{#generateConstructorWithAllArgs}}{{^vendorExtensions.x-java-all-args-constructor}} and all parameters{{/vendorExtensions.x-java-all-args-constructor}}{{/generateConstructorWithAllArgs}}
- */
- {{#jsonb}}@JsonbCreator{{/jsonb}}{{#jackson}}@JsonCreator{{/jackson}}
- public {{classname}}(
- {{#readOnlyVars}}
- {{#jsonb}}@JsonbProperty(value = "{{baseName}}"{{^required}}, nullable = true{{/required}}){{/jsonb}}{{#jackson}}@JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}){{/jackson}} {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}
- {{/readOnlyVars}}
- ) {
- this();
- {{#readOnlyVars}}
- this.{{name}} = {{#vendorExtensions.x-is-jackson-optional-nullable}}{{name}} == null ? JsonNullable.<{{{datatypeWithEnum}}}>undefined() : JsonNullable.of({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{name}}{{/vendorExtensions.x-is-jackson-optional-nullable}};
- {{/readOnlyVars}}
- }
- {{/withXml}}
- {{/vendorExtensions.x-has-readonly-properties}}
-{{#vendorExtensions.x-java-all-args-constructor}}
-
- /**
- * Constructor with all args parameters
- */
- public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{#jsonb}}@JsonbProperty(value = "{{baseName}}"{{^required}}, nullable = true{{/required}}){{/jsonb}}{{#jackson}}@JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}}){{/jackson}} {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
-{{#parent}}
- super({{#parentVars}}{{name}}{{^-last}}, {{/-last}}{{/parentVars}});
-{{/parent}}
- {{#vars}}
- this.{{name}} = {{#vendorExtensions.x-is-jackson-optional-nullable}}{{name}} == null ? JsonNullable.<{{{datatypeWithEnum}}}>undefined() : JsonNullable.of({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{name}}{{/vendorExtensions.x-is-jackson-optional-nullable}};
-{{/vars}}
- }
-{{/vendorExtensions.x-java-all-args-constructor}}
-
-{{#vars}}
- {{^isReadOnly}}
- public {{classname}} {{name}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) {
- {{#vendorExtensions.x-is-jackson-optional-nullable}}this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}});{{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}this.{{name}} = {{name}};{{/vendorExtensions.x-is-jackson-optional-nullable}}
- return this;
- }
- {{#isArray}}
-
- public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) {
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
- if (this.{{name}} == null || !this.{{name}}.isPresent()) {
- this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}});
- }
- try {
- this.{{name}}.get().add({{name}}Item);
- } catch (java.util.NoSuchElementException e) {
- // this can never happen, as we make sure above that the value is present
- }
- return this;
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}
- if (this.{{name}} == null) {
- this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}};
- }
- this.{{name}}.add({{name}}Item);
- return this;
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- }
- {{/isArray}}
- {{#isMap}}
-
- public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) {
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
- if (this.{{name}} == null || !this.{{name}}.isPresent()) {
- this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}});
- }
- try {
- this.{{name}}.get().put(key, {{name}}Item);
- } catch (java.util.NoSuchElementException e) {
- // this can never happen, as we make sure above that the value is present
- }
- return this;
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}
- {{^required}}
- if (this.{{name}} == null) {
- this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}};
- }
- {{/required}}
- this.{{name}}.put(key, {{name}}Item);
- return this;
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- }
- {{/isMap}}
-
- {{/isReadOnly}}
- /**
- {{#description}}
- * {{.}}
- {{/description}}
- {{^description}}
- * Get {{name}}
- {{/description}}
- {{#minimum}}
- * minimum: {{.}}
- {{/minimum}}
- {{#maximum}}
- * maximum: {{.}}
- {{/maximum}}
- * @return {{name}}
- {{#deprecated}}
- * @deprecated
- {{/deprecated}}
- */
-{{#deprecated}}
- @Deprecated
-{{/deprecated}}
- {{>nullable_var_annotations}}{{! prevent indent}}
-{{#jsonb}}
- @JsonbProperty("{{baseName}}")
-{{/jsonb}}
-{{#useBeanValidation}}
-{{>beanValidation}}
-
-{{/useBeanValidation}}
-{{#swagger1AnnotationLibrary}}
- @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}")
-{{/swagger1AnnotationLibrary}}
-{{#swagger2AnnotationLibrary}}
- @Schema({{#example}}example = "{{{.}}}", {{/example}}requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}, description = "{{{description}}}")
-{{/swagger2AnnotationLibrary}}
-{{#vendorExtensions.x-extra-annotation}}
- {{{vendorExtensions.x-extra-annotation}}}
-{{/vendorExtensions.x-extra-annotation}}
-{{#vendorExtensions.x-is-jackson-optional-nullable}}
- {{!unannotated, Jackson would pick this up automatically and add it *in addition* to the _JsonNullable getter field}}
- @JsonIgnore
-{{/vendorExtensions.x-is-jackson-optional-nullable}}
-{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#jackson}}{{> jackson_annotations}}{{/jackson}}{{/vendorExtensions.x-is-jackson-optional-nullable}}
- public {{{datatypeWithEnum}}} {{getter}}() {
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
- {{#isReadOnly}}{{! A readonly attribute doesn't have setter => jackson will set null directly if explicitly returned by API, so make sure we have an empty JsonNullable}}
- if ({{name}} == null) {
- {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}};
- }
- {{/isReadOnly}}
- return {{name}}.orElse(null);
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}
- return {{name}};
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- }
-
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
-{{> jackson_annotations}}
-
- public JsonNullable<{{{datatypeWithEnum}}}> {{getter}}_JsonNullable() {
- return {{name}};
- }
- {{/vendorExtensions.x-is-jackson-optional-nullable}}{{#vendorExtensions.x-is-jackson-optional-nullable}}
- @JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}})
- {{#isReadOnly}}private{{/isReadOnly}}{{^isReadOnly}}public{{/isReadOnly}} void {{setter}}_JsonNullable(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) {
- {{! For getters/setters that have name differing from attribute name, we must include setter (albeit private) for jackson to be able to set the attribute}}
- this.{{name}} = {{name}};
- }
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
-
- {{^isReadOnly}}
-{{#vendorExtensions.x-setter-extra-annotation}} {{{vendorExtensions.x-setter-extra-annotation}}}
-{{/vendorExtensions.x-setter-extra-annotation}}{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{> jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) {
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
- this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}});
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}
- this.{{name}} = {{name}};
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- }
- {{/isReadOnly}}
-
- {{/vars}}
- {{#parent}}
- {{#readWriteVars}}
- {{#isOverridden}}
- @Override
- public {{classname}} {{name}}({{>nullable_var_annotations}} {{{datatypeWithEnum}}} {{name}}) {
- {{#vendorExtensions.x-is-jackson-optional-nullable}}
- this.{{setter}}(JsonNullable.<{{{datatypeWithEnum}}}>of({{name}}));
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- {{^vendorExtensions.x-is-jackson-optional-nullable}}
- this.{{setter}}({{name}});
- {{/vendorExtensions.x-is-jackson-optional-nullable}}
- return this;
- }
-
- {{/isOverridden}}
- {{/readWriteVars}}
- {{/parent}}
- @Override
- public boolean equals(Object o) {
- {{#useReflectionEqualsHashCode}}
- return EqualsBuilder.reflectionEquals(this, o, false, null, true);
- {{/useReflectionEqualsHashCode}}
- {{^useReflectionEqualsHashCode}}
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }{{#hasVars}}
- {{classname}} {{classVarName}} = ({{classname}}) o;
- return {{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}equalsNullable(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}} &&
- {{/-last}}{{/vars}}{{#parent}} &&
- super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}}
- return {{#parent}}super.equals(o){{/parent}}{{^parent}}true{{/parent}};{{/hasVars}}
- {{/useReflectionEqualsHashCode}}
- }{{#vendorExtensions.x-jackson-optional-nullable-helpers}}
-
- private static boolean equalsNullable(JsonNullable a, JsonNullable b) {
- return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get()));
- }{{/vendorExtensions.x-jackson-optional-nullable-helpers}}
-
- @Override
- public int hashCode() {
- {{#useReflectionEqualsHashCode}}
- return HashCodeBuilder.reflectionHashCode(this);
- {{/useReflectionEqualsHashCode}}
- {{^useReflectionEqualsHashCode}}
- return Objects.hash({{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}hashCodeNullable({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}});
- {{/useReflectionEqualsHashCode}}
- }{{#vendorExtensions.x-jackson-optional-nullable-helpers}}
-
- private static int hashCodeNullable(JsonNullable a) {
- if (a == null) {
- return 1;
- }
- return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31;
- }{{/vendorExtensions.x-jackson-optional-nullable-helpers}}
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("class {{classname}} {\n");
- {{#parent}}
- sb.append(" ").append(toIndentedString(super.toString())).append("\n");
- {{/parent}}
- {{#vars}}
- sb.append(" {{name}}: ").append({{#isPassword}}"*"{{/isPassword}}{{^isPassword}}toIndentedString({{name}}){{/isPassword}}).append("\n");
- {{/vars}}
- sb.append("}");
- return sb.toString();
- }
-
- /**
- * Convert the given object to string with each line indented by 4 spaces
- * (except the first line).
- */
- private{{#jsonb}} static{{/jsonb}} String toIndentedString(Object o) {
- if (o == null) {
- return "null";
- }
- return o.toString().replace("\n", "\n ");
- }
-{{#supportUrlQuery}}
-
- /**
- * Convert the instance into URL query string.
- *
- * @return URL query string
- */
- public String toUrlQueryString() {
- return toUrlQueryString(null);
- }
-
- /**
- * Convert the instance into URL query string.
- *
- * @param prefix prefix of the query string
- * @return URL query string
- */
- public String toUrlQueryString(String prefix) {
- String suffix = "";
- String containerSuffix = "";
- String containerPrefix = "";
- if (prefix == null) {
- // style=form, explode=true, e.g. /pet?name=cat&type=manx
- prefix = "";
- } else {
- // deepObject style e.g. /pet?id[name]=cat&id[type]=manx
- prefix = prefix + "[";
- suffix = "]";
- containerSuffix = "]";
- containerPrefix = "[";
- }
-
- StringJoiner joiner = new StringJoiner("&");
-
- {{#allVars}}
- // add `{{baseName}}` to the URL query string
- {{#isArray}}
- {{#items.isPrimitiveType}}
- {{#uniqueItems}}
- if ({{getter}}() != null) {
- int i = 0;
- for ({{{items.datatypeWithEnum}}} _item : {{getter}}()) {
- try {
- joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix),
- URLEncoder.encode(String.valueOf(_item), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- i++;
- }
- {{/uniqueItems}}
- {{^uniqueItems}}
- if ({{getter}}() != null) {
- for (int i = 0; i < {{getter}}().size(); i++) {
- try {
- joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix),
- URLEncoder.encode(String.valueOf({{getter}}().get(i)), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- }
- {{/uniqueItems}}
- {{/items.isPrimitiveType}}
- {{^items.isPrimitiveType}}
- {{#items.isModel}}
- {{#uniqueItems}}
- if ({{getter}}() != null) {
- int i = 0;
- for ({{{items.dataType}}} _item : {{getter}}()) {
- if (_item != null) {
- joiner.add(_item.toUrlQueryString(String.format("%s{{baseName}}%s%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix))));
- }
- }
- i++;
- }
- {{/uniqueItems}}
- {{^uniqueItems}}
- if ({{getter}}() != null) {
- for (int i = 0; i < {{getter}}().size(); i++) {
- if ({{getter}}().get(i) != null) {
- joiner.add({{getter}}().get(i).toUrlQueryString(String.format("%s{{baseName}}%s%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix))));
- }
- }
- }
- {{/uniqueItems}}
- {{/items.isModel}}
- {{^items.isModel}}
- {{#uniqueItems}}
- if ({{getter}}() != null) {
- int i = 0;
- for ({{{items.dataType}}} _item : {{getter}}()) {
- if (_item != null) {
- try {
- joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix),
- URLEncoder.encode(String.valueOf(_item), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- i++;
- }
- }
- {{/uniqueItems}}
- {{^uniqueItems}}
- if ({{getter}}() != null) {
- for (int i = 0; i < {{getter}}().size(); i++) {
- if ({{getter}}().get(i) != null) {
- try {
- joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, i, containerSuffix),
- URLEncoder.encode(String.valueOf({{getter}}().get(i)), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- }
- }
- {{/uniqueItems}}
- {{/items.isModel}}
- {{/items.isPrimitiveType}}
- {{/isArray}}
- {{^isArray}}
- {{#isMap}}
- {{^items.isModel}}
- if ({{getter}}() != null) {
- for (String _key : {{getter}}().keySet()) {
- try {
- joiner.add(String.format("%s{{baseName}}%s%s=%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, _key, containerSuffix),
- {{getter}}().get(_key), URLEncoder.encode(String.valueOf({{getter}}().get(_key)), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- }
- {{/items.isModel}}
- {{#items.isModel}}
- if ({{getter}}() != null) {
- for (String _key : {{getter}}().keySet()) {
- if ({{getter}}().get(_key) != null) {
- joiner.add({{getter}}().get(_key).toUrlQueryString(String.format("%s{{baseName}}%s%s", prefix, suffix,
- "".equals(suffix) ? "" : String.format("%s%d%s", containerPrefix, _key, containerSuffix))));
- }
- }
- }
- {{/items.isModel}}
- {{/isMap}}
- {{^isMap}}
- {{#isPrimitiveType}}
- if ({{getter}}() != null) {
- try {
- joiner.add(String.format("%s{{{baseName}}}%s=%s", prefix, suffix, URLEncoder.encode(String.valueOf({{{getter}}}()), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- {{/isPrimitiveType}}
- {{^isPrimitiveType}}
- {{#isModel}}
- if ({{getter}}() != null) {
- joiner.add({{getter}}().toUrlQueryString(prefix + "{{{baseName}}}" + suffix));
- }
- {{/isModel}}
- {{^isModel}}
- if ({{getter}}() != null) {
- try {
- joiner.add(String.format("%s{{{baseName}}}%s=%s", prefix, suffix, URLEncoder.encode(String.valueOf({{{getter}}}()), "UTF-8").replaceAll("\\+", "%20")));
- } catch (UnsupportedEncodingException e) {
- // Should never happen, UTF-8 is always supported
- throw new RuntimeException(e);
- }
- }
- {{/isModel}}
- {{/isPrimitiveType}}
- {{/isMap}}
- {{/isArray}}
-
- {{/allVars}}
- return joiner.toString();
- }
-{{/supportUrlQuery}}
-{{#parcelableModel}}
-
- public void writeToParcel(Parcel out, int flags) {
-{{#model}}
-{{#isArray}}
- out.writeList(this);
-{{/isArray}}
-{{^isArray}}
-{{#parent}}
- super.writeToParcel(out, flags);
-{{/parent}}
-{{#vars}}
- out.writeValue({{name}});
-{{/vars}}
-{{/isArray}}
-{{/model}}
- }
-
- {{classname}}(Parcel in) {
-{{#isArray}}
- in.readTypedList(this, {{arrayModelType}}.CREATOR);
-{{/isArray}}
-{{^isArray}}
-{{#parent}}
- super(in);
-{{/parent}}
-{{#vars}}
-{{#isPrimitiveType}}
- {{name}} = ({{{datatypeWithEnum}}})in.readValue(null);
-{{/isPrimitiveType}}
-{{^isPrimitiveType}}
- {{name}} = ({{{datatypeWithEnum}}})in.readValue({{complexType}}.class.getClassLoader());
-{{/isPrimitiveType}}
-{{/vars}}
-{{/isArray}}
- }
-
- public int describeContents() {
- return 0;
- }
-
- public static final Parcelable.Creator<{{classname}}> CREATOR = new Parcelable.Creator<{{classname}}>() {
- public {{classname}} createFromParcel(Parcel in) {
-{{#model}}
-{{#isArray}}
- {{classname}} result = new {{classname}}();
- result.addAll(in.readArrayList({{arrayModelType}}.class.getClassLoader()));
- return result;
-{{/isArray}}
-{{^isArray}}
- return new {{classname}}(in);
-{{/isArray}}
-{{/model}}
- }
- public {{classname}}[] newArray(int size) {
- return new {{classname}}[size];
- }
- };
-{{/parcelableModel}}
-{{#generateBuilders}}
-
- {{>javaBuilder}}{{! prevent indent}}
-{{/generateBuilders}}
-
-}
diff --git a/customer-service-client/src/main/resources/openapi-templates/sealed.mustache b/customer-service-client/src/main/resources/openapi-templates/sealed.mustache
deleted file mode 100644
index 8e5076b..0000000
--- a/customer-service-client/src/main/resources/openapi-templates/sealed.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{#useSealedOneOfInterfaces}}{{#vendorExtensions.x-is-one-of-interface}}{{#permits.0}}sealed {{/permits.0}}{{/vendorExtensions.x-is-one-of-interface}}{{^permits.0}}{{#vendorExtensions.x-implements}}final {{/vendorExtensions.x-implements}}{{/permits.0}}{{/useSealedOneOfInterfaces}}
\ No newline at end of file
diff --git a/customer-service/pom.xml b/customer-service/pom.xml
index 6320edc..12b343b 100644
--- a/customer-service/pom.xml
+++ b/customer-service/pom.xml
@@ -13,7 +13,7 @@
com.example.demo
customer-service
- 0.5.0
+ 0.6.0
customer-service
Demo service: Spring Boot 3.4 + Springdoc (OpenAPI) for generics-aware client generation
diff --git a/customer-service/src/main/java/com/example/demo/common/openapi/autoreg/AutoWrapperSchemaCustomizer.java b/customer-service/src/main/java/com/example/demo/common/openapi/autoreg/AutoWrapperSchemaCustomizer.java
new file mode 100644
index 0000000..75e64ee
--- /dev/null
+++ b/customer-service/src/main/java/com/example/demo/common/openapi/autoreg/AutoWrapperSchemaCustomizer.java
@@ -0,0 +1,48 @@
+package com.example.demo.common.openapi.autoreg;
+
+import com.example.demo.common.openapi.ApiResponseSchemaFactory;
+import com.example.demo.common.openapi.OpenApiSchemas;
+import com.example.demo.common.openapi.introspector.ResponseTypeIntrospector;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import org.springdoc.core.customizers.OpenApiCustomizer;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+@Configuration
+public class AutoWrapperSchemaCustomizer {
+
+ private final Set dataRefs;
+
+ public AutoWrapperSchemaCustomizer(
+ ListableBeanFactory beanFactory, ResponseTypeIntrospector introspector) {
+ Set refs = new LinkedHashSet<>();
+ Map mappings =
+ beanFactory.getBeansOfType(RequestMappingHandlerMapping.class);
+ mappings
+ .values()
+ .forEach(
+ rmh ->
+ rmh.getHandlerMethods().values().stream()
+ .map(HandlerMethod::getMethod)
+ .forEach(m -> introspector.extractDataRefName(m).ifPresent(refs::add)));
+ this.dataRefs = Collections.unmodifiableSet(refs);
+ }
+
+ @Bean
+ public OpenApiCustomizer autoResponseWrappers() {
+ return openApi ->
+ dataRefs.forEach(
+ ref -> {
+ String name = OpenApiSchemas.SCHEMA_SERVICE_RESPONSE + ref;
+ openApi
+ .getComponents()
+ .addSchemas(name, ApiResponseSchemaFactory.createComposedWrapper(ref));
+ });
+ }
+}
diff --git a/customer-service/src/main/java/com/example/demo/common/openapi/introspector/ResponseTypeIntrospector.java b/customer-service/src/main/java/com/example/demo/common/openapi/introspector/ResponseTypeIntrospector.java
new file mode 100644
index 0000000..d1a9cec
--- /dev/null
+++ b/customer-service/src/main/java/com/example/demo/common/openapi/introspector/ResponseTypeIntrospector.java
@@ -0,0 +1,63 @@
+package com.example.demo.common.openapi.introspector;
+
+import com.example.demo.common.api.response.ServiceResponse;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.springframework.core.ResolvableType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ResponseTypeIntrospector {
+
+ private static final List ASYNC_WRAPPERS =
+ List.of(
+ "reactor.core.publisher.Mono",
+ "reactor.core.publisher.Flux",
+ "java.util.concurrent.CompletionStage",
+ "java.util.concurrent.Future",
+ "org.springframework.web.context.request.async.DeferredResult",
+ "org.springframework.web.context.request.async.WebAsyncTask");
+
+ public Optional extractDataRefName(Method method) {
+ if (method == null) return Optional.empty();
+
+ ResolvableType rt = ResolvableType.forMethodReturnType(method);
+
+ rt = unwrapIf(rt);
+
+ for (String wrapper : ASYNC_WRAPPERS) {
+ rt = unwrapIf(rt, wrapper);
+ }
+
+ Class> raw = rt.resolve();
+ if (raw == null || !ServiceResponse.class.isAssignableFrom(raw)) {
+ return Optional.empty();
+ }
+
+ if (rt.getGenerics().length == 0) {
+ return Optional.empty();
+ }
+
+ Class> dataClass = rt.getGeneric(0).resolve();
+ return Optional.ofNullable(dataClass).map(Class::getSimpleName);
+ }
+
+ private ResolvableType unwrapIf(ResolvableType type) {
+ Class> raw = type.resolve();
+ if (raw != null && ResponseEntity.class.isAssignableFrom(raw)) {
+ return type.getGeneric(0);
+ }
+ return type;
+ }
+
+ private ResolvableType unwrapIf(ResolvableType type, String wrapperClassName) {
+ Class> raw = type.resolve();
+ if (raw != null && Objects.equals(raw.getName(), wrapperClassName)) {
+ return type.getGeneric(0);
+ }
+ return type;
+ }
+}
diff --git a/customer-service/src/main/java/com/example/demo/customer/openapi/SwaggerCustomerResponseCustomizer.java b/customer-service/src/main/java/com/example/demo/customer/openapi/SwaggerCustomerResponseCustomizer.java
deleted file mode 100644
index 1fe6a78..0000000
--- a/customer-service/src/main/java/com/example/demo/customer/openapi/SwaggerCustomerResponseCustomizer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.example.demo.customer.openapi;
-
-import static com.example.demo.common.openapi.OpenApiSchemas.SCHEMA_SERVICE_RESPONSE;
-
-import com.example.demo.common.openapi.ApiResponseSchemaFactory;
-import com.example.demo.customer.api.dto.CustomerCreateResponse;
-import com.example.demo.customer.api.dto.CustomerDeleteResponse;
-import com.example.demo.customer.api.dto.CustomerDto;
-import com.example.demo.customer.api.dto.CustomerListResponse;
-import com.example.demo.customer.api.dto.CustomerUpdateResponse;
-import java.util.List;
-import org.springdoc.core.customizers.OpenApiCustomizer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class SwaggerCustomerResponseCustomizer {
-
- private static final String REF_CUSTOMER_DTO = CustomerDto.class.getSimpleName();
- private static final String REF_CUSTOMER_CREATE_RESPONSE =
- CustomerCreateResponse.class.getSimpleName();
- private static final String REF_CUSTOMER_UPDATE_RESPONSE =
- CustomerUpdateResponse.class.getSimpleName();
- private static final String REF_CUSTOMER_DELETE_RESPONSE =
- CustomerDeleteResponse.class.getSimpleName();
- private static final String REF_CUSTOMER_LIST_RESPONSE =
- CustomerListResponse.class.getSimpleName();
-
- private static final List REGISTERED_RESPONSE_REFS =
- List.of(
- REF_CUSTOMER_DTO,
- REF_CUSTOMER_CREATE_RESPONSE,
- REF_CUSTOMER_UPDATE_RESPONSE,
- REF_CUSTOMER_DELETE_RESPONSE,
- REF_CUSTOMER_LIST_RESPONSE);
-
- private static String apiResponseWrapperNameFor(String ref) {
- return SCHEMA_SERVICE_RESPONSE + ref; // e.g. ServiceResponseCustomerDto
- }
-
- @Bean
- public OpenApiCustomizer customerWrappers() {
- return openApi ->
- REGISTERED_RESPONSE_REFS.forEach(
- ref ->
- openApi
- .getComponents()
- .addSchemas(
- apiResponseWrapperNameFor(ref),
- ApiResponseSchemaFactory.createComposedWrapper(ref)));
- }
-}
diff --git a/docs/images/generated-client-wrapper-after.png b/docs/images/generated-client-wrapper-after.png
index 1c2fc05..37421ea 100644
Binary files a/docs/images/generated-client-wrapper-after.png and b/docs/images/generated-client-wrapper-after.png differ
diff --git a/docs/images/generated-client-wrapper-before.png b/docs/images/generated-client-wrapper-before.png
index 939e6dc..cfcc607 100644
Binary files a/docs/images/generated-client-wrapper-before.png and b/docs/images/generated-client-wrapper-before.png differ