kobylynskyi / graphql-java-codegen

Make your GraphQL Java application schema-driven.
https://kobylynskyi.github.io/graphql-java-codegen/
MIT License
269 stars 114 forks source link

Make constructors private when generateBuilder is true #1012

Open dgshep opened 1 year ago

dgshep commented 1 year ago

When safely evolving schema (adding optional fields) the constructor signatures change for model classes, which breaks binary compatibility between generated client versions since the constructors are public. If constructors could be made private when builder class generation is used, this would remove a key area of binary incompatibility between generated java model classes.

Sketch:

public class Product {

  private final String brand;

  private Product(String brand) {
    this.brand = brand;
  }

  /*equals hashcode etc.*/

  public static Builder builder() { return new Product.Builder(); }
  public static class Builder { 
     private String brand;
     public Builder() {}
     public setBrand(String brand) {
       this.brand = brand;
       return this;
     }
  }
}
dgshep commented 1 year ago

The following change produces the desired result:

diff --git a/src/main/resources/templates/java-lang/type.ftl b/src/main/resources/templates/java-lang/type.ftl
index 23a501e..a447c8f 100644
--- a/src/main/resources/templates/java-lang/type.ftl
+++ b/src/main/resources/templates/java-lang/type.ftl
@@ -50,11 +50,11 @@ public class ${className} implements java.io.Serializable<#if implements?has_con
     </#list>
 </#if>

-    public ${className}() {
+    <#if builder>private<#else>public</#if> ${className}() {
     }

 <#if fields?has_content>
-    public ${className}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>) {
+    <#if builder>private<#else>public</#if> ${className}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>) {
     <#list fields as field>
         this.${field.name} = ${field.name};
     </#list>
kobylynskyi commented 1 year ago

To avoid breaking existing clients of this library we would need to introduce a new property, something like generatePrivateConstructor (default: false).