Skip to content

Commit 8fad3cd

Browse files
authored
Merge pull request #910 from sar9ho/jspecify-required-fields-constructor
Generate required fields constructor when JSpecify is enabled
2 parents 3fe305a + 310dd4b commit 8fad3cd

File tree

3 files changed

+126
-6
lines changed

3 files changed

+126
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ graphql-dgs-codegen-core/compiled-sources/
1515
generated
1616
generated-examples
1717
scripts/__pycache__
18+
.kotlin/

graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -460,15 +460,24 @@ abstract class BaseDataTypeGenerator(
460460
}
461461

462462
if (config.generateJSpecifyAnnotations) {
463-
// Since JSpecify annotations are enabled, add a private no-arg constructor
464-
// only for the Builder to access
465-
addDefaultConstructor(javaType, false)
463+
val requiredFields = fields.filter { !it.nullable }
464+
addDefaultConstructor(javaType, requiredFields.isEmpty())
465+
466+
if (config.javaGenerateAllConstructor) {
467+
if (fields.isNotEmpty() && fields.size < 256) {
468+
addParameterizedConstructor(fields, javaType)
469+
}
470+
471+
if (requiredFields.isNotEmpty() && requiredFields.size < fields.size && requiredFields.size < 256) {
472+
addParameterizedConstructor(requiredFields, javaType)
473+
}
474+
}
466475
} else {
467476
addDefaultConstructor(javaType, true)
468-
}
469477

470-
if (config.javaGenerateAllConstructor && fields.isNotEmpty() && fields.size < 256) {
471-
addParameterizedConstructor(fields, javaType)
478+
if (config.javaGenerateAllConstructor && fields.isNotEmpty() && fields.size < 256) {
479+
addParameterizedConstructor(fields, javaType)
480+
}
472481
}
473482

474483
addToString(fields, javaType)

graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7061,4 +7061,114 @@ It takes a title and such.
70617061
dataTypes[0].writeTo(System.out)
70627062
assertCompilesJava(dataTypes)
70637063
}
7064+
7065+
@Test
7066+
fun `public no-arg constructor is generated when jspecify enabled and all fields are nullable`() {
7067+
val schema =
7068+
"""
7069+
type Foo {
7070+
a: String
7071+
b: Int
7072+
}
7073+
""".trimIndent()
7074+
7075+
val result =
7076+
CodeGen(
7077+
CodeGenConfig(
7078+
schemas = setOf(schema),
7079+
packageName = BASE_PACKAGE_NAME,
7080+
language = Language.JAVA,
7081+
generateJSpecifyAnnotations = true,
7082+
javaGenerateAllConstructor = true,
7083+
),
7084+
).generate()
7085+
7086+
val fooType = result.javaDataTypes.single { it.typeSpec().name() == "Foo" }
7087+
val constructors = fooType.typeSpec().methodSpecs().filter { it.isConstructor }
7088+
7089+
assertThat(constructors).anySatisfy { ctor ->
7090+
assertThat(ctor.modifiers()).contains(javax.lang.model.element.Modifier.PUBLIC)
7091+
assertThat(ctor.parameters()).isEmpty()
7092+
}
7093+
7094+
assertCompilesJava(result)
7095+
}
7096+
7097+
@Test
7098+
fun `required-fields constructor is generated when jspecify enabled and non-null fields exist`() {
7099+
val schema =
7100+
"""
7101+
type Foo {
7102+
a: String!
7103+
b: Int
7104+
}
7105+
""".trimIndent()
7106+
7107+
val result =
7108+
CodeGen(
7109+
CodeGenConfig(
7110+
schemas = setOf(schema),
7111+
packageName = BASE_PACKAGE_NAME,
7112+
language = Language.JAVA,
7113+
generateJSpecifyAnnotations = true,
7114+
javaGenerateAllConstructor = true,
7115+
),
7116+
).generate()
7117+
7118+
val fooType = result.javaDataTypes.single { it.typeSpec().name() == "Foo" }
7119+
val constructors = fooType.typeSpec().methodSpecs().filter { it.isConstructor }
7120+
7121+
assertThat(constructors).anySatisfy { ctor ->
7122+
assertThat(ctor.modifiers()).contains(javax.lang.model.element.Modifier.PUBLIC)
7123+
assertThat(ctor.parameters()).hasSize(1)
7124+
assertThat(ctor.parameters()[0].name()).isEqualTo("a")
7125+
}
7126+
7127+
assertThat(constructors).anySatisfy { ctor ->
7128+
assertThat(ctor.modifiers()).contains(javax.lang.model.element.Modifier.PRIVATE)
7129+
assertThat(ctor.parameters()).isEmpty()
7130+
}
7131+
7132+
assertCompilesJava(result)
7133+
}
7134+
7135+
@Test
7136+
fun `required-fields constructor is not generated when all fields are required`() {
7137+
val schema =
7138+
"""
7139+
type Foo {
7140+
a: String!
7141+
b: Int!
7142+
}
7143+
""".trimIndent()
7144+
7145+
val result =
7146+
CodeGen(
7147+
CodeGenConfig(
7148+
schemas = setOf(schema),
7149+
packageName = BASE_PACKAGE_NAME,
7150+
language = Language.JAVA,
7151+
generateJSpecifyAnnotations = true,
7152+
javaGenerateAllConstructor = true,
7153+
),
7154+
).generate()
7155+
7156+
val fooType = result.javaDataTypes.single { it.typeSpec().name() == "Foo" }
7157+
val constructors = fooType.typeSpec().methodSpecs().filter { it.isConstructor }
7158+
7159+
assertThat(constructors).anySatisfy { ctor ->
7160+
assertThat(ctor.modifiers()).contains(javax.lang.model.element.Modifier.PRIVATE)
7161+
assertThat(ctor.parameters()).isEmpty()
7162+
}
7163+
7164+
val publicMultiArgCtors =
7165+
constructors.filter { ctor ->
7166+
ctor.modifiers().contains(javax.lang.model.element.Modifier.PUBLIC) && ctor.parameters().isNotEmpty()
7167+
}
7168+
7169+
assertThat(publicMultiArgCtors).hasSize(1)
7170+
assertThat(publicMultiArgCtors.single().parameters()).hasSize(2)
7171+
7172+
assertCompilesJava(result)
7173+
}
70647174
}

0 commit comments

Comments
 (0)