Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add builder pattern to java client and server #18033

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions bin/configs/java-native.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ templateDir: modules/openapi-generator/src/main/resources/Java
additionalProperties:
artifactId: petstore-native
hideGenerationTimestamp: "true"
generateBuilders: true
1 change: 1 addition & 0 deletions bin/configs/java-resttemplate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ additionalProperties:
hideGenerationTimestamp: "true"
java8: true
containerDefaultToNull: true
generateBuilders: true
1 change: 1 addition & 0 deletions bin/configs/spring-boot-3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ additionalProperties:
useBeanValidation: true
withXml: true
hideGenerationTimestamp: "true"
generateBuilders: true
1 change: 1 addition & 0 deletions bin/configs/spring-boot-useoptional.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ additionalProperties:
useOptional: true
artifactId: spring-boot-useoptional
hideGenerationTimestamp: "true"
generateBuilders: true
1 change: 1 addition & 0 deletions bin/configs/spring-boot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ additionalProperties:
hideGenerationTimestamp: "true"
camelCaseDollarSign: "true"
modelNameSuffix: 'Dto'
generateBuilders: true
Original file line number Diff line number Diff line change
Expand Up @@ -1125,5 +1125,10 @@ public boolean getIsEnum() {
public void setIsEnum(boolean isEnum) {
this.isEnum = isEnum;
}


public boolean isExplode() {
return isExplode;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
public static final String JAVAX_PACKAGE = "javaxPackage";
public static final String USE_JAKARTA_EE = "useJakartaEe";
public static final String CONTAINER_DEFAULT_TO_NULL = "containerDefaultToNull";
public static final String GENERATE_BUILDERS = "generateBuilders";

public static final String CAMEL_CASE_DOLLAR_SIGN = "camelCaseDollarSign";
public static final String USE_ONE_OF_INTERFACES = "useOneOfInterfaces";
Expand Down Expand Up @@ -141,6 +142,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
protected boolean camelCaseDollarSign = false;
protected boolean useJakartaEe = false;
protected boolean containerDefaultToNull = false;
protected boolean generateBuilders = false;

private Map<String, String> schemaKeyToModelNameCache = new HashMap<>();

Expand Down Expand Up @@ -281,6 +283,7 @@ public AbstractJavaCodegen() {
cliOptions.add(CliOption.newBoolean(CAMEL_CASE_DOLLAR_SIGN, "Fix camelCase when starting with $ sign. when true : $Value when false : $value"));
cliOptions.add(CliOption.newBoolean(USE_JAKARTA_EE, "whether to use Jakarta EE namespace instead of javax"));
cliOptions.add(CliOption.newBoolean(CONTAINER_DEFAULT_TO_NULL, "Set containers (array, set, map) default to null"));
cliOptions.add(CliOption.newBoolean(GENERATE_BUILDERS, "whether to create a builder in the models"));

cliOptions.add(CliOption.newString(CodegenConstants.PARENT_GROUP_ID, CodegenConstants.PARENT_GROUP_ID_DESC));
cliOptions.add(CliOption.newString(CodegenConstants.PARENT_ARTIFACT_ID, CodegenConstants.PARENT_ARTIFACT_ID_DESC));
Expand Down Expand Up @@ -567,6 +570,11 @@ public void processOpts() {
this.setUseOneOfInterfaces(Boolean.parseBoolean(additionalProperties.get(USE_ONE_OF_INTERFACES).toString()));
}

if (additionalProperties.containsKey(GENERATE_BUILDERS)) {
this.setGenerateBuilders(Boolean.parseBoolean(additionalProperties.get(GENERATE_BUILDERS).toString()));
}
writePropertyBack(GENERATE_BUILDERS, generateBuilders);

if (!StringUtils.isEmpty(parentGroupId) && !StringUtils.isEmpty(parentArtifactId) && !StringUtils.isEmpty(parentVersion)) {
additionalProperties.put("parentOverridden", true);
}
Expand Down Expand Up @@ -668,11 +676,69 @@ public void processOpts() {
additionalProperties.put(CONTAINER_DEFAULT_TO_NULL, containerDefaultToNull);
}

public void setGenerateBuilders(boolean generateBuilders) {
this.generateBuilders = generateBuilders;
}

public boolean isGenerateBuilders() {
return generateBuilders;
}

/**
* Analyse and post process all Models.
* Add parentVars to every Model which has a parent. This allows to generate
* fluent setter methods for inherited properties.
* @param objs the models map.
* @return the processed models map.
*/
@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
objs = super.postProcessAllModels(objs);
objs = super.updateAllModels(objs);

for (ModelsMap modelsAttrs : objs.values()) {
for (ModelMap mo : modelsAttrs.getModels()) {
CodegenModel codegenModel = mo.getModel();
Set<String> inheritedImports = new HashSet<>();
Map<String, CodegenProperty> propertyHash = new HashMap<>(codegenModel.vars.size());
for (final CodegenProperty property : codegenModel.vars) {
propertyHash.put(property.name, property);
}
CodegenModel parentCodegenModel = codegenModel.parentModel;
while (parentCodegenModel != null) {
for (final CodegenProperty property : parentCodegenModel.vars) {
// helper list of parentVars simplifies templating
if (!propertyHash.containsKey(property.name)) {
propertyHash.put(property.name, property);
final CodegenProperty parentVar = property.clone();
parentVar.isInherited = true;
LOGGER.info("adding parent variable {}", property.name);
codegenModel.parentVars.add(parentVar);
Set<String> imports = parentVar.getImports(true, this.importBaseType, generatorMetadata.getFeatureSet()).stream().filter(Objects::nonNull).collect(Collectors.toSet());
for (String imp: imports) {
// Avoid dupes
if (!codegenModel.getImports().contains(imp)) {
inheritedImports.add(imp);
codegenModel.getImports().add(imp);
}
}
}
}
parentCodegenModel = parentCodegenModel.getParentModel();
}
if (codegenModel.getParentModel() != null) {
codegenModel.parentRequiredVars = new ArrayList<>(codegenModel.getParentModel().requiredVars);
}
// There must be a better way ...
for (String imp: inheritedImports) {
String qimp = importMapping().get(imp);
if (qimp != null) {
Map<String,String> toAdd = new HashMap<>();
toAdd.put("import", qimp);
modelsAttrs.getImports().add(toAdd);
}
}
}
}
if (!additionalModelTypeAnnotations.isEmpty()) {
for (String modelName : objs.keySet()) {
Map<String, Object> models = objs.get(modelName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.tags.Tag;
import java.io.File;
Expand Down Expand Up @@ -1166,66 +1165,6 @@ public CodegenModel fromModel(String name, Schema model) {
return codegenModel;
}

/**
* Analyse and post process all Models.
* Add parentVars to every Model which has a parent. This allows to generate
* fluent setter methods for inherited properties.
* @param objs the models map.
* @return the processed models map.
*/
@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
objs = super.postProcessAllModels(objs);
objs = super.updateAllModels(objs);

for (ModelsMap modelsAttrs : objs.values()) {
for (ModelMap mo : modelsAttrs.getModels()) {
CodegenModel codegenModel = mo.getModel();
Set<String> inheritedImports = new HashSet<>();
Map<String, CodegenProperty> propertyHash = new HashMap<>(codegenModel.vars.size());
for (final CodegenProperty property : codegenModel.vars) {
propertyHash.put(property.name, property);
}
CodegenModel parentCodegenModel = codegenModel.parentModel;
while (parentCodegenModel != null) {
for (final CodegenProperty property : parentCodegenModel.vars) {
// helper list of parentVars simplifies templating
if (!propertyHash.containsKey(property.name)) {
propertyHash.put(property.name, property);
final CodegenProperty parentVar = property.clone();
parentVar.isInherited = true;
LOGGER.info("adding parent variable {}", property.name);
codegenModel.parentVars.add(parentVar);
Set<String> imports = parentVar.getImports(true, this.importBaseType, generatorMetadata.getFeatureSet()).stream().filter(Objects::nonNull).collect(Collectors.toSet());
for (String imp: imports) {
// Avoid dupes
if (!codegenModel.getImports().contains(imp)) {
inheritedImports.add(imp);
codegenModel.getImports().add(imp);
}
}
}
}
parentCodegenModel = parentCodegenModel.getParentModel();
}
if (codegenModel.getParentModel() != null) {
codegenModel.parentRequiredVars = new ArrayList<>(codegenModel.getParentModel().requiredVars);
}
// There must be a better way ...
for (String imp: inheritedImports) {
String qimp = importMapping().get(imp);
if (qimp != null) {
Map<String,String> toAdd = new HashMap<>();
toAdd.put("import", qimp);
modelsAttrs.getImports().add(toAdd);
}
}
}
}
return objs;
}


/*
* Add dynamic imports based on the parameters and vendor extensions of an operation.
* The imports are expanded by the mustache {{import}} tag available to model and api
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
public static class Builder {{#parentModel}}extends {{classname}}.Builder {{/parentModel}}{

private {{classname}} instance;

public Builder() {
this(new {{classname}}());
}

protected Builder({{classname}} instance) {
{{#parentModel}}
super(instance);
{{/parentModel}}
this.instance = instance;
}

{{#vars}}
public {{classname}}.Builder {{name}}({{{datatypeWithEnum}}} {{name}}) {
{{#vendorExtensions.x-is-jackson-optional-nullable}}
this.instance.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{name}});
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
this.instance.{{name}} = {{name}};
{{/vendorExtensions.x-is-jackson-optional-nullable}}
return this;
}
{{#vendorExtensions.x-is-jackson-optional-nullable}}
public {{classname}}.Builder {{name}}(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) {
this.instance.{{name}} = {{name}};
return this;
}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{/vars}}

{{#parentVars}}
public {{classname}}.Builder {{name}}({{{datatypeWithEnum}}} {{name}}) { // inherited: {{isInherited}}
super.{{name}}({{name}});
return this;
}
{{#vendorExtensions.x-is-jackson-optional-nullable}}
public {{classname}}.Builder {{name}}(JsonNullable<{{{datatypeWithEnum}}}> {{name}}) {
this.instance.{{name}} = {{name}};
return this;
}
{{/vendorExtensions.x-is-jackson-optional-nullable}}

{{/parentVars}}

/**
* returns a built {{classname}} instance.
*
* The builder is not reusable.
*/
public {{classname}} build() {
try {
return this.instance;
} finally {
// ensure that this.instance is not reused{{#parentModel}}
super.build();{{/parentModel}}
this.instance = null;
}
}

@Override
public String toString() {
return getClass() + "=(" + instance + ")";
}
}

/**
* Create a builder with no initialized field.
*/
public static {{classname}}.Builder builder() {
return new {{classname}}.Builder();
}

/**
* Create a builder with a shallow copy of this instance.
*/
public {{classname}}.Builder toBuilder() {
return new {{classname}}.Builder(){{#allVars}}
.{{name}}({{getter}}()){{/allVars}};
}
Original file line number Diff line number Diff line change
Expand Up @@ -593,4 +593,8 @@ static {
JSON.registerDiscriminator({{classname}}.class, "{{propertyBaseName}}", mappings);
}
{{/discriminator}}
{{#generateBuilders}}

{{>javaBuilder}}
{{/generateBuilders}}
}
16 changes: 10 additions & 6 deletions modules/openapi-generator/src/main/resources/Java/pojo.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,18 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
{{/vendorExtensions.x-field-extra-annotation}}
{{#vendorExtensions.x-is-jackson-optional-nullable}}
{{#isContainer}}
private JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();
{{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();
{{/isContainer}}
{{^isContainer}}
private JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}};
{{#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}}
private {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{/isContainer}}
{{^isContainer}}
{{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{/isContainer}}
{{/vendorExtensions.x-is-jackson-optional-nullable}}

Expand Down Expand Up @@ -279,7 +279,7 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens

{{/vars}}
{{#parent}}
{{#allVars}}
{{#readWriteVars}}
{{#isOverridden}}
@Override
public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) {
Expand All @@ -293,7 +293,7 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
}

{{/isOverridden}}
{{/allVars}}
{{/readWriteVars}}
{{/parent}}
@Override
public boolean equals(Object o) {
Expand Down Expand Up @@ -613,5 +613,9 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
}
};
{{/parcelableModel}}
{{#generateBuilders}}

{{>javaBuilder}}
{{/generateBuilders}}

}
Loading