diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsForInputTypes/expected/types/PersonFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsForInputTypes/expected/types/PersonFilter.kt index eaee7a583..6c80ce104 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsForInputTypes/expected/types/PersonFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsForInputTypes/expected/types/PersonFilter.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class PersonFilter @JsonCreator constructor( @JsonProperty("email") - public val email: String? = default("email"), + public val email: String? = default("email", null), ) : GraphQLInput() { override fun fields(): List> = listOf("email" to email) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsWithExtendedInputTypes/expected/types/PersonFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsWithExtendedInputTypes/expected/types/PersonFilter.kt index 11123d09f..47a65ec86 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsWithExtendedInputTypes/expected/types/PersonFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/constantsWithExtendedInputTypes/expected/types/PersonFilter.kt @@ -11,9 +11,9 @@ import kotlin.collections.List public class PersonFilter @JsonCreator constructor( @JsonProperty("email") - public val email: String? = default("email"), + public val email: String? = default("email", null), @JsonProperty("birthYear") - public val birthYear: Int? = default("birthYear"), + public val birthYear: Int? = default("birthYear", null), ) : GraphQLInput() { override fun fields(): List> = listOf("email" to email, "birthYear" to birthYear) diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassDocs/expected/types/MovieFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassDocs/expected/types/MovieFilter.kt index 885af67ea..38c626c3d 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassDocs/expected/types/MovieFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassDocs/expected/types/MovieFilter.kt @@ -15,7 +15,7 @@ import kotlin.collections.List */ public class MovieFilter @JsonCreator constructor( @JsonProperty("titleFilter") - public val titleFilter: String? = default("titleFilter"), + public val titleFilter: String? = default("titleFilter", null), ) : GraphQLInput() { override fun fields(): List> = listOf("titleFilter" to titleFilter) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassFieldDocs/expected/types/MovieFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassFieldDocs/expected/types/MovieFilter.kt index e2c26487c..4e6788125 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassFieldDocs/expected/types/MovieFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/dataClassFieldDocs/expected/types/MovieFilter.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class MovieFilter @JsonCreator constructor( @JsonProperty("titleFilter") - public val titleFilter: String? = default("titleFilter"), + public val titleFilter: String? = default("titleFilter", null), ) : GraphQLInput() { override fun fields(): List> = listOf("titleFilter" to titleFilter) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/input/expected/types/MovieFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/input/expected/types/MovieFilter.kt index 2c9535832..fd885fbe4 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/input/expected/types/MovieFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/input/expected/types/MovieFilter.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class MovieFilter @JsonCreator constructor( @JsonProperty("genre") - public val genre: String? = default("genre"), + public val genre: String? = default("genre", null), ) : GraphQLInput() { override fun fields(): List> = listOf("genre" to genre) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultEnumValueForArray/expected/types/SomeType.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultEnumValueForArray/expected/types/SomeType.kt index bf15d9394..1874775ee 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultEnumValueForArray/expected/types/SomeType.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultEnumValueForArray/expected/types/SomeType.kt @@ -10,7 +10,8 @@ import kotlin.collections.List public class SomeType @JsonCreator constructor( @JsonProperty("colors") - public val colors: List? = default?>("colors"), + public val colors: List? = default?>("colors", + listOf(com.netflix.graphql.dgs.codegen.cases.inputWithDefaultEnumValueForArray.expected.types.Color.red)), ) : GraphQLInput() { override fun fields(): List> = listOf("colors" to colors) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultIntValueForArray/expected/types/SomeType.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultIntValueForArray/expected/types/SomeType.kt index 915ae4480..3e1ffacdc 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultIntValueForArray/expected/types/SomeType.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultIntValueForArray/expected/types/SomeType.kt @@ -11,7 +11,7 @@ import kotlin.collections.List public class SomeType @JsonCreator constructor( @JsonProperty("numbers") - public val numbers: List? = default?>("numbers"), + public val numbers: List? = default?>("numbers", listOf(1, 2, 3)), ) : GraphQLInput() { override fun fields(): List> = listOf("numbers" to numbers) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultStringValueForArray/expected/types/SomeType.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultStringValueForArray/expected/types/SomeType.kt index 7ec76cddb..8f50cad8c 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultStringValueForArray/expected/types/SomeType.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultStringValueForArray/expected/types/SomeType.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class SomeType @JsonCreator constructor( @JsonProperty("names") - public val names: List? = default?>("names"), + public val names: List? = default?>("names", listOf("A", "B")), ) : GraphQLInput() { override fun fields(): List> = listOf("names" to names) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForArray/expected/types/SomeType.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForArray/expected/types/SomeType.kt index f6f970b08..304959cf6 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForArray/expected/types/SomeType.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForArray/expected/types/SomeType.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class SomeType @JsonCreator constructor( @JsonProperty("names") - public val names: List? = default?>("names"), + public val names: List? = default?>("names", emptyList()), ) : GraphQLInput() { override fun fields(): List> = listOf("names" to names) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForEnum/expected/types/ColorFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForEnum/expected/types/ColorFilter.kt index 0bd181b68..83f320285 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForEnum/expected/types/ColorFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForEnum/expected/types/ColorFilter.kt @@ -10,7 +10,8 @@ import kotlin.collections.List public class ColorFilter @JsonCreator constructor( @JsonProperty("color") - public val color: Color? = default("color"), + public val color: Color? = default("color", + com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForEnum.expected.types.Color.red), ) : GraphQLInput() { override fun fields(): List> = listOf("color" to color) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/DgsClient.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/DgsClient.kt new file mode 100644 index 000000000..c1a62ed72 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/DgsClient.kt @@ -0,0 +1,3 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected + +public object DgsClient diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/DgsConstants.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/DgsConstants.kt new file mode 100644 index 000000000..8e9b0224b --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/DgsConstants.kt @@ -0,0 +1,25 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected + +import kotlin.String + +public object DgsConstants { + public object PERSON { + public const val TYPE_NAME: String = "Person" + + public const val Name: String = "name" + + public const val Age: String = "age" + + public const val Car: String = "car" + + public const val Hobbies: String = "hobbies" + + public const val IsHappy: String = "isHappy" + } + + public object CAR { + public const val TYPE_NAME: String = "Car" + + public const val Brand: String = "brand" + } +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Car.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Car.kt new file mode 100644 index 000000000..24dbdeab3 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Car.kt @@ -0,0 +1,16 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected.types + +import com.fasterxml.jackson.`annotation`.JsonCreator +import com.fasterxml.jackson.`annotation`.JsonProperty +import com.netflix.graphql.dgs.codegen.GraphQLInput +import kotlin.Any +import kotlin.Pair +import kotlin.String +import kotlin.collections.List + +public class Car @JsonCreator constructor( + @JsonProperty("brand") + public val brand: String = default("brand", "BMW"), +) : GraphQLInput() { + override fun fields(): List> = listOf("brand" to brand) +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Hobby.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Hobby.kt new file mode 100644 index 000000000..7ff266b61 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Hobby.kt @@ -0,0 +1,9 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected.types + +public enum class Hobby { + Football, + Hokey, + ; + + public companion object +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Person.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Person.kt new file mode 100644 index 000000000..dd328611e --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/expected/types/Person.kt @@ -0,0 +1,30 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected.types + +import com.fasterxml.jackson.`annotation`.JsonCreator +import com.fasterxml.jackson.`annotation`.JsonProperty +import com.netflix.graphql.dgs.codegen.GraphQLInput +import kotlin.Any +import kotlin.Boolean +import kotlin.Int +import kotlin.Pair +import kotlin.String +import kotlin.collections.List + +public class Person @JsonCreator constructor( + @JsonProperty("name") + public val name: String = default("name", "Damian"), + @JsonProperty("age") + public val age: Int = default("age", 18), + @JsonProperty("car") + public val car: Car = default("car", + com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected.types.Car(brand + = "Ford")), + @JsonProperty("hobbies") + public val hobbies: List = default>("hobbies", + listOf(com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForNonNullableFields.expected.types.Hobby.Hokey)), + @JsonProperty("isHappy") + public val isHappy: Boolean = default("isHappy", true), +) : GraphQLInput() { + override fun fields(): List> = listOf("name" to name, "age" to age, "car" to + car, "hobbies" to hobbies, "isHappy" to isHappy) +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/schema.graphql b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/schema.graphql new file mode 100644 index 000000000..e326420f6 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForNonNullableFields/schema.graphql @@ -0,0 +1,16 @@ +input Person { + name: String! = "Damian" + age: Int! = 18 + car: Car! = { brand: "Ford" } + hobbies: [Hobby!]! = [Hokey] + isHappy: Boolean! = true +} + +enum Hobby { + Football + Hokey +} + +input Car { + brand: String! = "BMW" +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/DgsClient.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/DgsClient.kt new file mode 100644 index 000000000..af3c37684 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/DgsClient.kt @@ -0,0 +1,3 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected + +public object DgsClient diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/DgsConstants.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/DgsConstants.kt new file mode 100644 index 000000000..8ecb9f125 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/DgsConstants.kt @@ -0,0 +1,27 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected + +import kotlin.String + +public object DgsConstants { + public object PERSON { + public const val TYPE_NAME: String = "Person" + + public const val Name: String = "name" + + public const val Age: String = "age" + + public const val Car: String = "car" + } + + public object CAR { + public const val TYPE_NAME: String = "Car" + + public const val Brand: String = "brand" + } + + public object MOVIEFILTER { + public const val TYPE_NAME: String = "MovieFilter" + + public const val Director: String = "director" + } +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/Car.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/Car.kt new file mode 100644 index 000000000..abe98203c --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/Car.kt @@ -0,0 +1,16 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected.types + +import com.fasterxml.jackson.`annotation`.JsonCreator +import com.fasterxml.jackson.`annotation`.JsonProperty +import com.netflix.graphql.dgs.codegen.GraphQLInput +import kotlin.Any +import kotlin.Pair +import kotlin.String +import kotlin.collections.List + +public class Car @JsonCreator constructor( + @JsonProperty("brand") + public val brand: String = default("brand", "BMW"), +) : GraphQLInput() { + override fun fields(): List> = listOf("brand" to brand) +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/MovieFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/MovieFilter.kt new file mode 100644 index 000000000..1d33d8b31 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/MovieFilter.kt @@ -0,0 +1,20 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected.types + +import com.fasterxml.jackson.`annotation`.JsonCreator +import com.fasterxml.jackson.`annotation`.JsonProperty +import com.netflix.graphql.dgs.codegen.GraphQLInput +import kotlin.Any +import kotlin.Pair +import kotlin.String +import kotlin.collections.List + +public class MovieFilter @JsonCreator constructor( + @JsonProperty("director") + public val director: Person? = default("director", + com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected.types.Person(name + = "Damian", car = + com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected.types.Car(brand + = "Tesla"))), +) : GraphQLInput() { + override fun fields(): List> = listOf("director" to director) +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/Person.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/Person.kt new file mode 100644 index 000000000..4b2d0e076 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/expected/types/Person.kt @@ -0,0 +1,24 @@ +package com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected.types + +import com.fasterxml.jackson.`annotation`.JsonCreator +import com.fasterxml.jackson.`annotation`.JsonProperty +import com.netflix.graphql.dgs.codegen.GraphQLInput +import kotlin.Any +import kotlin.Int +import kotlin.Pair +import kotlin.String +import kotlin.collections.List + +public class Person @JsonCreator constructor( + @JsonProperty("name") + public val name: String? = default("name", "John"), + @JsonProperty("age") + public val age: Int? = default("age", 23), + @JsonProperty("car") + public val car: Car? = default("car", + com.netflix.graphql.dgs.codegen.cases.inputWithDefaultValueForObject.expected.types.Car(brand + = "Ford")), +) : GraphQLInput() { + override fun fields(): List> = listOf("name" to name, "age" to age, "car" to + car) +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/schema.graphql b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/schema.graphql new file mode 100644 index 000000000..2e1f197f9 --- /dev/null +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithDefaultValueForObject/schema.graphql @@ -0,0 +1,13 @@ +input Person { + name: String = "John" + age: Int = 23 + car: Car = { brand: "Ford" } +} + +input Car { + brand: String! = "BMW" +} + +input MovieFilter { + director: Person = { name: "Damian", car: { brand: "Tesla" } } +} diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithExtendedType/expected/types/MovieFilter.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithExtendedType/expected/types/MovieFilter.kt index 43c8258f5..8c56f279b 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithExtendedType/expected/types/MovieFilter.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/inputWithExtendedType/expected/types/MovieFilter.kt @@ -11,9 +11,9 @@ import kotlin.collections.List public class MovieFilter @JsonCreator constructor( @JsonProperty("genre") - public val genre: String? = default("genre"), + public val genre: String? = default("genre", null), @JsonProperty("releaseYear") - public val releaseYear: Int? = default("releaseYear"), + public val releaseYear: Int? = default("releaseYear", null), ) : GraphQLInput() { override fun fields(): List> = listOf("genre" to genre, "releaseYear" to releaseYear) diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I1.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I1.kt index dd26e7d95..fffedb9f8 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I1.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I1.kt @@ -10,9 +10,9 @@ import kotlin.collections.List public class I1 @JsonCreator constructor( @JsonProperty("arg1") - public val arg1: I1? = default("arg1"), + public val arg1: I1? = default("arg1", null), @JsonProperty("arg2") - public val arg2: I2? = default("arg2"), + public val arg2: I2? = default("arg2", null), ) : GraphQLInput() { override fun fields(): List> = listOf("arg1" to arg1, "arg2" to arg2) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I2.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I2.kt index b1c26c2bd..89618e11b 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I2.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithNestedInputs/expected/types/I2.kt @@ -10,9 +10,9 @@ import kotlin.collections.List public class I2 @JsonCreator constructor( @JsonProperty("arg1") - public val arg1: String? = default("arg1"), + public val arg1: String? = default("arg1", null), @JsonProperty("arg2") - public val arg2: String? = default("arg2"), + public val arg2: String? = default("arg2", null), ) : GraphQLInput() { override fun fields(): List> = listOf("arg1" to arg1, "arg2" to arg2) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithPrimitiveAndArgs/expected/types/I.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithPrimitiveAndArgs/expected/types/I.kt index 3b06d6a31..a0d2837ea 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithPrimitiveAndArgs/expected/types/I.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithPrimitiveAndArgs/expected/types/I.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class I @JsonCreator constructor( @JsonProperty("arg") - public val arg: String? = default("arg"), + public val arg: String? = default("arg", null), ) : GraphQLInput() { override fun fields(): List> = listOf("arg" to arg) } diff --git a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithTypeAndArgs/expected/types/I.kt b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithTypeAndArgs/expected/types/I.kt index b8acb95ca..9e1235df9 100644 --- a/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithTypeAndArgs/expected/types/I.kt +++ b/graphql-dgs-codegen-core/src/integTest/kotlin/com/netflix/graphql/dgs/codegen/cases/projectionWithTypeAndArgs/expected/types/I.kt @@ -10,7 +10,7 @@ import kotlin.collections.List public class I @JsonCreator constructor( @JsonProperty("arg") - public val arg: String? = default("arg"), + public val arg: String? = default("arg", null), ) : GraphQLInput() { override fun fields(): List> = listOf("arg" to arg) } diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt index cbd181ae8..b0404855d 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/CodeGen.kt @@ -299,14 +299,15 @@ class CodeGen(private val config: CodeGenConfig) { } private fun generateJavaInputType(definitions: Collection>): CodeGenResult { - val inputTypes = definitions.asSequence() + val inputTypeDefinitions = definitions .filterIsInstance() + val inputTypes = inputTypeDefinitions.asSequence() .excludeSchemaTypeExtension() .filter { config.generateDataTypes || it.name in requiredTypeCollector.requiredTypes } return inputTypes .map { d -> - InputTypeGenerator(config, document).generate(d, findInputExtensions(d.name, definitions)) + InputTypeGenerator(config, document).generate(d, findInputExtensions(d.name, definitions), inputTypeDefinitions) }.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) } } @@ -411,12 +412,13 @@ class CodeGen(private val config: CodeGenConfig) { } private fun generateKotlinInputTypes(definitions: Collection>): CodeGenResult { - return definitions.asSequence() + val inputTypeDefinitions = definitions .filterIsInstance() + return inputTypeDefinitions.asSequence() .excludeSchemaTypeExtension() .filter { config.generateDataTypes || it.name in requiredTypeCollector.requiredTypes } .map { - KotlinInputTypeGenerator(config, document).generate(it, findInputExtensions(it.name, definitions)) + KotlinInputTypeGenerator(config, document).generate(it, findInputExtensions(it.name, definitions), inputTypeDefinitions) } .fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) } } diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt index 49dfbab58..b20025a47 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/DataTypeGenerator.kt @@ -28,6 +28,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.Serializable import javax.lang.model.element.Modifier +import com.squareup.javapoet.TypeName as JavaTypeName class DataTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTypeGenerator(config.packageNameTypes, config, document) { private val logger: Logger = LoggerFactory.getLogger(DataTypeGenerator::class.java) @@ -113,7 +114,11 @@ class DataTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTyp class InputTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTypeGenerator(config.packageNameTypes, config, document) { private val logger: Logger = LoggerFactory.getLogger(InputTypeGenerator::class.java) - fun generate(definition: InputObjectTypeDefinition, extensions: List): CodeGenResult { + fun generate( + definition: InputObjectTypeDefinition, + extensions: List, + inputTypeDefinitions: List + ): CodeGenResult { if (definition.shouldSkip(config)) { return CodeGenResult() } @@ -122,37 +127,13 @@ class InputTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTy val name = definition.name val fieldDefinitions = definition.inputValueDefinitions.map { + val type = typeUtils.findReturnType(it.type) val defaultValue = it.defaultValue?.let { defVal -> - when (defVal) { - is BooleanValue -> CodeBlock.of("\$L", defVal.isValue) - is IntValue -> CodeBlock.of("\$L", defVal.value) - is StringValue -> { - val localeValueOverride = checkAndGetLocaleValue(defVal, it.type) - if (localeValueOverride != null) CodeBlock.of("\$L", localeValueOverride) - else CodeBlock.of("\$S", defVal.value) - } - is FloatValue -> CodeBlock.of("\$L", defVal.value) - is EnumValue -> CodeBlock.of("\$T.\$N", typeUtils.findReturnType(it.type), defVal.name) - is ArrayValue -> if (defVal.values.isEmpty()) CodeBlock.of("java.util.Collections.emptyList()") else CodeBlock.of( - "java.util.Arrays.asList(\$L)", - defVal.values.map { v -> - when (v) { - is BooleanValue -> CodeBlock.of("\$L", v.isValue) - is IntValue -> CodeBlock.of("\$L", v.value) - is StringValue -> CodeBlock.of("\$S", v.value) - is FloatValue -> CodeBlock.of("\$L", v.value) - is EnumValue -> CodeBlock.of("\$L.\$N", ((it.type as ListType).type as TypeName).name, v.name) - else -> "" - } - }.joinToString() - ) - is ObjectValue -> CodeBlock.of("new \$L()", typeUtils.findReturnType(it.type)) - else -> CodeBlock.of("\$L", defVal) - } + generateCode(defVal, type, inputTypeDefinitions) } Field( name = it.name, - type = typeUtils.findReturnType(it.type), + type = type, initialValue = defaultValue, description = it.description, directives = it.directives @@ -161,13 +142,71 @@ class InputTypeGenerator(config: CodeGenConfig, document: Document) : BaseDataTy return generate(name, emptyList(), fieldDefinitions, definition.description, definition.directives) } - private fun checkAndGetLocaleValue(value: StringValue, type: Type<*>): String? { - if (typeUtils.findReturnType(type).toString() == "java.util.Locale") return "Locale.forLanguageTag(\"${value.value}\")" + private fun generateCode( + value: Value>, + type: JavaTypeName, + inputTypeDefinitions: List + ): CodeBlock? { + return when (value) { + is BooleanValue -> CodeBlock.of("\$L", value.isValue) + is IntValue -> CodeBlock.of("\$L", value.value) + is StringValue -> { + val localeValueOverride = checkAndGetLocaleValue(value, type) + if (localeValueOverride != null) CodeBlock.of("\$L", localeValueOverride) + else CodeBlock.of("\$S", value.value) + } + + is FloatValue -> CodeBlock.of("\$L", value.value) + is EnumValue -> CodeBlock.of("\$T.\$N", type, value.name) + is ArrayValue -> if (value.values.isEmpty()) CodeBlock.of("java.util.Collections.emptyList()") + else CodeBlock.of( + "java.util.Arrays.asList(\$L)", + value.values.joinToString { v -> + generateCode(v, type.className, inputTypeDefinitions).toString() + } + ) + is ObjectValue -> { + val inputObjectDefinition = inputTypeDefinitions.first { + val expectedCanonicalClassName = config.typeMapping[it.name] ?: "${config.packageNameTypes}.${it.name}" + expectedCanonicalClassName == type.className.canonicalName() + } + if (value.objectFields.isEmpty()) { + return CodeBlock.of("new $type()") + } else { + CodeBlock.of( + "new $type(){{" + value.objectFields.joinToString("") { objectProperty -> + val argumentType = + checkNotNull(inputObjectDefinition.inputValueDefinitions.find { it.name == objectProperty.name }) { + "Property \"${objectProperty.name}\" does not exist in input type \"${inputObjectDefinition.name}\"" + } + val argumentValue = generateCode( + objectProperty.value, + typeUtils.findReturnType(argumentType.type), + inputTypeDefinitions + ) + "set${objectProperty.name.replaceFirstChar { it.uppercaseChar() }}($argumentValue);" + } + "}}" + ) + } + } + else -> CodeBlock.of("\$L", value) + } + } + + private fun checkAndGetLocaleValue(value: StringValue, type: JavaTypeName): String? { + if (type.toString() == "java.util.Locale") return "Locale.forLanguageTag(\"${value.value}\")" return null } + + private val JavaTypeName.className: ClassName + get() = when (this) { + is com.squareup.javapoet.ClassName -> this + is com.squareup.javapoet.ParameterizedTypeName -> typeArguments[0].className + else -> TODO() + } } -internal data class Field(val name: String, val type: com.squareup.javapoet.TypeName, val initialValue: CodeBlock? = null, val overrideGetter: Boolean = false, val interfaceType: com.squareup.javapoet.TypeName? = null, val description: Description? = null, val directives: List = listOf()) +internal data class Field(val name: String, val type: JavaTypeName, val initialValue: CodeBlock? = null, val overrideGetter: Boolean = false, val interfaceType: com.squareup.javapoet.TypeName? = null, val description: Description? = null, val directives: List = listOf()) abstract class BaseDataTypeGenerator( internal val packageName: String, diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt index 9b580b926..21603f431 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt @@ -23,6 +23,7 @@ import com.netflix.graphql.dgs.codegen.CodeGenResult import com.netflix.graphql.dgs.codegen.filterSkipped import com.netflix.graphql.dgs.codegen.generators.java.InputTypeGenerator import com.netflix.graphql.dgs.codegen.generators.shared.applyDirectivesKotlin +import com.netflix.graphql.dgs.codegen.generators.shared.generateKotlinCode import com.netflix.graphql.dgs.codegen.shouldSkip import com.squareup.kotlinpoet.BOOLEAN import com.squareup.kotlinpoet.ClassName @@ -33,9 +34,7 @@ import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.INT import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.MemberName import com.squareup.kotlinpoet.ParameterSpec -import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.STRING import com.squareup.kotlinpoet.TypeSpec @@ -62,10 +61,28 @@ class KotlinDataTypeGenerator(config: CodeGenConfig, document: Document) : val fields = definition.fieldDefinitions .filterSkipped() .filter(ReservedKeywordFilter.filterInvalidNames) - .map { Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type), null, it.description, it.directives) } + + .map { + Field( + it.name, + typeUtils.findReturnType(it.type), + typeUtils.isNullable(it.type), + null, + it.description, + it.directives + ) + } + extensions.flatMap { it.fieldDefinitions } .filterSkipped() - .map { Field(it.name, typeUtils.findReturnType(it.type), typeUtils.isNullable(it.type), null, it.description, it.directives) } + .map { + Field( + it.name, + typeUtils.findReturnType(it.type), + typeUtils.isNullable(it.type), + null, + it.description, + it.directives + ) + } val interfaces = definition.implements return generate(definition.name, fields, interfaces, document, definition.description, definition.directives) } @@ -75,7 +92,11 @@ class KotlinInputTypeGenerator(config: CodeGenConfig, document: Document) : AbstractKotlinDataTypeGenerator(packageName = config.packageNameTypes, config = config, document = document) { private val logger: Logger = LoggerFactory.getLogger(InputTypeGenerator::class.java) - fun generate(definition: InputObjectTypeDefinition, extensions: List): CodeGenResult { + fun generate( + definition: InputObjectTypeDefinition, + extensions: List, + inputTypeDefinitions: Collection + ): CodeGenResult { if (definition.shouldSkip(config)) { return CodeGenResult() } @@ -85,46 +106,38 @@ class KotlinInputTypeGenerator(config: CodeGenConfig, document: Document) : val fields = definition.inputValueDefinitions .filter(ReservedKeywordFilter.filterInvalidNames) .map { - val type = typeUtils.findReturnType(it.type) - val defaultValue = it.defaultValue?.let { value -> generateCode(value, type) } - Field(name = it.name, type = type, nullable = it.type !is NonNullType, default = defaultValue, description = it.description, directives = it.directives) + val defaultValue = it.defaultValue?.let { value -> + generateKotlinCode( + value, + typeUtils.findReturnType(it.type), + inputTypeDefinitions, + config, + typeUtils + ) + } + Field( + name = it.name, + type = typeUtils.findReturnType(it.type), + nullable = it.type !is NonNullType, + default = defaultValue, + description = it.description, + directives = it.directives + ) }.plus( extensions.flatMap { it.inputValueDefinitions }.map { - Field(name = it.name, type = typeUtils.findReturnType(it.type), nullable = it.type !is NonNullType, default = null, description = it.description, directives = it.directives) + Field( + name = it.name, + type = typeUtils.findReturnType(it.type), + nullable = it.type !is NonNullType, + default = null, + description = it.description, + directives = it.directives + ) } ) val interfaces = emptyList>() return generate(definition.name, fields, interfaces, document, definition.description, definition.directives) } - - private fun generateCode(value: Value>, type: KtTypeName): CodeBlock = - when (value) { - is BooleanValue -> CodeBlock.of("%L", value.isValue) - is IntValue -> CodeBlock.of("%L", value.value) - is StringValue -> { - val localeValueOverride = checkAndGetLocaleValue(value, type) - if (localeValueOverride != null) CodeBlock.of("%L", localeValueOverride) - else CodeBlock.of("%S", value.value) - } - is FloatValue -> CodeBlock.of("%L", value.value) - is EnumValue -> CodeBlock.of("%M", MemberName(type.className, value.name)) - is ArrayValue -> - if (value.values.isEmpty()) CodeBlock.of("emptyList()") - else CodeBlock.of("listOf(%L)", value.values.joinToString { v -> generateCode(v, type).toString() }) - else -> CodeBlock.of("%L", value) - } - - private fun checkAndGetLocaleValue(value: StringValue, type: KtTypeName): String? { - if (type.className.canonicalName == "java.util.Locale") return "Locale.forLanguageTag(\"${value.value}\")" - return null - } - - private val KtTypeName.className: ClassName - get() = when (this) { - is ClassName -> this - is ParameterizedTypeName -> typeArguments[0].className - else -> TODO() - } } internal data class Field( diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinTypeUtils.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinTypeUtils.kt index de625ff50..c836011ea 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinTypeUtils.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinTypeUtils.kt @@ -150,7 +150,7 @@ class KotlinTypeUtils(private val packageName: String, private val config: CodeG "BooleanValue" -> BOOLEAN "ID" -> STRING "IDValue" -> STRING - else -> "$packageName.$name".toKtTypeName() + else -> "${config.packageNameTypes}.$name".toKtTypeName() } } diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin2/GenerateKotlin2InputTypes.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin2/GenerateKotlin2InputTypes.kt index d76b9247d..02a5bf379 100644 --- a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin2/GenerateKotlin2InputTypes.kt +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin2/GenerateKotlin2InputTypes.kt @@ -22,26 +22,18 @@ import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import com.netflix.graphql.dgs.codegen.CodeGenConfig import com.netflix.graphql.dgs.codegen.GraphQLInput +import com.netflix.graphql.dgs.codegen.generators.kotlin.KotlinTypeUtils import com.netflix.graphql.dgs.codegen.generators.kotlin.ReservedKeywordFilter import com.netflix.graphql.dgs.codegen.generators.kotlin.addOptionalGeneratedAnnotation import com.netflix.graphql.dgs.codegen.generators.kotlin.sanitizeKdoc import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findInputExtensions import com.netflix.graphql.dgs.codegen.generators.shared.excludeSchemaTypeExtension +import com.netflix.graphql.dgs.codegen.generators.shared.generateKotlinCode import com.netflix.graphql.dgs.codegen.shouldSkip +import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.AnnotationSpec -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.CodeBlock -import com.squareup.kotlinpoet.FileSpec -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import com.squareup.kotlinpoet.PropertySpec -import com.squareup.kotlinpoet.TypeSpec -import com.squareup.kotlinpoet.asClassName -import graphql.language.Document -import graphql.language.InputObjectTypeDefinition -import graphql.language.InputValueDefinition +import graphql.language.* fun generateKotlin2InputTypes( config: CodeGenConfig, @@ -50,6 +42,12 @@ fun generateKotlin2InputTypes( ): List { val typeLookup = Kotlin2TypeLookup(config, document) + val typeUtils = KotlinTypeUtils( + packageName = config.packageName, + config = config, + document = document + ) + return document .getDefinitionsOfType(InputObjectTypeDefinition::class.java) .excludeSchemaTypeExtension() @@ -94,8 +92,18 @@ fun generateKotlin2InputTypes( type = type ).addAnnotation(AnnotationSpec.builder(JsonProperty::class).addMember("%S", field.name).build()) .apply { - if (type.isNullable) { - defaultValue("default<%T, %T>(%S)", typeName, type, field.name) + if (field.defaultValue != null || type.isNullable) { + val value = field.defaultValue?.let { + generateKotlinCode( + it, + type, + document + .getDefinitionsOfType(InputObjectTypeDefinition::class.java), + config, + typeUtils + ) + } + defaultValue("default<%T, %T>(%S, $value)", typeName, type, field.name) } } .build() diff --git a/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt new file mode 100644 index 000000000..dd7c522bd --- /dev/null +++ b/graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/shared/GenerateKotlinCode.kt @@ -0,0 +1,91 @@ +/* + * + * Copyright 2020 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.netflix.graphql.dgs.codegen.generators.shared + +import com.netflix.graphql.dgs.codegen.CodeGenConfig +import com.netflix.graphql.dgs.codegen.generators.kotlin.KotlinTypeUtils +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.TypeName +import graphql.language.* + +fun generateKotlinCode( + value: Value>, + type: TypeName, + inputTypeDefinitions: Collection, + config: CodeGenConfig, + typeUtils: KotlinTypeUtils +): CodeBlock { + return when (value) { + is BooleanValue -> CodeBlock.of("%L", value.isValue) + is IntValue -> CodeBlock.of("%L", value.value) + is StringValue -> { + val localeValueOverride = checkAndGetLocaleValue(value, type) + if (localeValueOverride != null) CodeBlock.of("%L", localeValueOverride) + else CodeBlock.of("%S", value.value) + } + is FloatValue -> CodeBlock.of("%L", value.value) + is EnumValue -> CodeBlock.of("%M", MemberName(type.className, value.name)) + is ArrayValue -> + if (value.values.isEmpty()) CodeBlock.of("emptyList()") + else CodeBlock.of( + "listOf(%L)", + value.values.joinToString { v -> generateKotlinCode(v, type, inputTypeDefinitions, config, typeUtils).toString() } + ) + + is ObjectValue -> { + val inputObjectDefinition = inputTypeDefinitions.first { + val expectedCanonicalClassName = config.typeMapping[it.name] ?: "${config.packageNameTypes}.${it.name}" + expectedCanonicalClassName == type.className.canonicalName + } + + CodeBlock.of( + type.className.canonicalName + "(%L)", + value.objectFields.joinToString { objectProperty -> + val argumentType = + checkNotNull(inputObjectDefinition.inputValueDefinitions.find { it.name == objectProperty.name }) { + "Property \"${objectProperty.name}\" does not exist in input type \"${inputObjectDefinition.name}\"" + } + "${objectProperty.name} = ${ + generateKotlinCode( + objectProperty.value, + typeUtils.findReturnType(argumentType.type), + inputTypeDefinitions, + config, + typeUtils + ) + }" + } + ) + } + + else -> CodeBlock.of("%L", value) + } +} + +private fun checkAndGetLocaleValue(value: StringValue, type: TypeName): String? { + if (type.className.canonicalName == "java.util.Locale") return "Locale.forLanguageTag(\"${value.value}\")" + return null +} + +private val TypeName.className: ClassName + get() = when (this) { + is ClassName -> this + is ParameterizedTypeName -> typeArguments[0].className + else -> TODO() + } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt index 189b81c9d..41baf7523 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/CodeGenTest.kt @@ -1754,7 +1754,7 @@ class CodeGenTest { val colorField = fields[0] assertThat(colorField.name).isEqualTo("colors") - assertThat(colorField.initializer.toString()).isEqualTo("""java.util.Arrays.asList(Color.red)""") + assertThat(colorField.initializer.toString()).isEqualTo("""java.util.Arrays.asList($typesPackageName.Color.red)""") assertCompilesJava(dataTypes + enumTypes) } @@ -4647,4 +4647,189 @@ It takes a title and such. assertThat(dataTypes[0].typeSpec.fieldSpecs[0].initializer.toString()).isEqualTo("Locale.forLanguageTag(\"en-US\")") assertCompilesJava(dataTypes) } + + @Test + fun `The default empty object value should result in constructor call`() { + val schema = """ + input Movie { + director: Person = {} + } + + input Person { + name: String = "Damian" + age: Int = 33 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName + ) + ).generate().javaDataTypes + + assertThat(dataTypes).hasSize(2) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("Movie") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("director") + assertThat(colorField.initializer.toString()).isEqualTo("""new $typesPackageName.Person()""") + + assertCompilesJava(dataTypes) + } + + @Test + fun `The default object with properties should result in constructor call with args`() { + val schema = """ + input Movie { + director: Person = { name: "Harrison", car: { brand: "Ford" } } + } + + input Person { + name: String = "Damian" + car: Car = { brand: "Tesla" } + } + + input Car { + brand: String = "VW" + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName + ) + ).generate().javaDataTypes + + assertThat(dataTypes).hasSize(3) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("Movie") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("director") + assertThat(colorField.initializer.toString()).isEqualTo( + "new com.netflix.graphql.dgs.codegen.tests.generated.types.Person()" + + """{{setName("Harrison");setCar(new com.netflix.graphql.dgs.codegen.tests.generated.types.Car(){{setBrand("Ford");}})""" + + ";}}" + ) + assertCompilesJava(dataTypes) + } + + @Test + fun `The default list value should support objects`() { + val schema = """ + input Director { + movies: [Movie!]! = [{ name: "Braveheart" }, { name: "Matrix", year: 1999 }] + } + + input Movie { + name: String = "Toy Story" + year: Int = 1995 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName + ) + ).generate().javaDataTypes + + assertThat(dataTypes).hasSize(2) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("Director") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("movies") + assertThat(colorField.initializer.toString()).isEqualTo( + "java.util.Arrays.asList(" + + "new com.netflix.graphql.dgs.codegen.tests.generated.types.Movie(){{setName(\"Braveheart\");}}, " + + "new com.netflix.graphql.dgs.codegen.tests.generated.types.Movie(){{setName(\"Matrix\");setYear(1999);}}" + + ")" + ) + assertCompilesJava(dataTypes) + } + + @Test + fun `The default object value should call constructor from typeMapping`() { + val schema = """ + input Movie { + director: Person = { name: "Harrison" } + } + + input Person { + name: String = "Damian" + age: Int = 33 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + typeMapping = mapOf("Person" to "mypackage.Human") + ) + ).generate().javaDataTypes + + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val type = data.typeSpec + assertThat(type.name).isEqualTo("Movie") + + val fields = type.fieldSpecs + assertThat(fields).hasSize(1) + + val colorField = fields[0] + assertThat(colorField.name).isEqualTo("director") + assertThat(colorField.initializer.toString()).isEqualTo("new mypackage.Human(){{setName(\"Harrison\");}}") + } + + @Test + fun `Codegen should fail when default value specifies property does not exist in input type`() { + val schema = """ + input Movie { + director: Person = { firstname: "Harrison" } + } + + input Person { + name: String = "Damian" + } + """.trimIndent() + + val exception = assertThrows { + CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName + ) + ).generate() + } + assertThat(exception.message).isEqualTo("Property \"firstname\" does not exist in input type \"Person\"") + } } diff --git a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt index acb4f29cd..19ed70139 100644 --- a/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt +++ b/graphql-dgs-codegen-core/src/test/kotlin/com/netflix/graphql/dgs/codegen/KotlinCodeGenTest.kt @@ -3763,4 +3763,253 @@ It takes a title and such. assertThat(typeSpec.primaryConstructor!!.parameters[0].defaultValue.toString()).isEqualTo("Locale.forLanguageTag(\"en-US\")") assertCompilesKotlin(dataTypes) } + + @Test + fun `The default empty object value should result in constructor call`() { + val schema = """ + input Movie { + director: Person = {} + } + + input Person { + name: String = "Damian" + age: Int = 33 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN + ) + ).generate().kotlinDataTypes + + assertThat(dataTypes).hasSize(2) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("Movie") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("director") + assertThat(colorParam.type.toString()).isEqualTo("$typesPackageName.Person?") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("$typesPackageName.Person()") + + assertCompilesKotlin(dataTypes) + } + + @Test + fun `The default object with properties should result in constructor call with args`() { + val schema = """ + input Movie { + director: Person = { name: "Harrison", car: { brand: "Ford" } } + } + + input Person { + name: String = "Damian" + car: Car = { brand: "Tesla" } + } + + input Car { + brand: String = "VW" + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN + ) + ).generate().kotlinDataTypes + + assertThat(dataTypes).hasSize(3) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("Movie") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("director") + assertThat(colorParam.type.toString()).isEqualTo("$typesPackageName.Person?") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("$typesPackageName.Person(name = \"Harrison\", car = $typesPackageName.Car(brand = \"Ford\"))") + + assertCompilesKotlin(dataTypes) + } + + @Test + fun `The default list value should support objects`() { + val schema = """ + input Director { + movies: [Movie!]! = [{ name: "Braveheart" }, { name: "Matrix", year: 1999 }] + } + + input Movie { + name: String = "Toy Story" + year: Int = 1995 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN + ) + ).generate().kotlinDataTypes + assertThat(dataTypes).hasSize(2) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("Director") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("movies") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List<$typesPackageName.Movie>") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf($typesPackageName.Movie(name = "Braveheart"), $typesPackageName.Movie(name = "Matrix", year = 1_999))""") + + assertCompilesKotlin(dataTypes) + } + + @Test + fun `The default list value should support objects with typeMapping`() { + val schema = """ + input Director { + movies: [Movie!]! = [{ name: "Braveheart" }, { name: "Matrix", year: 1999 }] + } + + input Movie { + name: String = "Toy Story" + year: Int = 1995 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN, + typeMapping = mapOf("Movie" to "mypackage.Film") + ) + ).generate().kotlinDataTypes + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("Director") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("movies") + assertThat(colorParam.type.toString()).isEqualTo("kotlin.collections.List") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("""listOf(mypackage.Film(name = "Braveheart"), mypackage.Film(name = "Matrix", year = 1_999))""") + } + + @Test + fun `The default object value should call constructor from typeMapping`() { + val schema = """ + input Movie { + director: Person = { name: "Harrison" } + } + + input Person { + name: String = "Damian" + age: Int = 33 + } + """.trimIndent() + + val dataTypes = CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN, + typeMapping = mapOf("Person" to "mypackage.Human") + ) + ).generate().kotlinDataTypes + + assertThat(dataTypes).hasSize(1) + + val data = dataTypes[0] + assertThat(data.packageName).isEqualTo(typesPackageName) + + val members = data.members + assertThat(members).hasSize(1) + + val type = members[0] as TypeSpec + assertThat(type.name).isEqualTo("Movie") + + val ctorSpec = type.primaryConstructor + assertThat(ctorSpec).isNotNull + assertThat(ctorSpec!!.parameters).hasSize(1) + + val colorParam = ctorSpec.parameters[0] + assertThat(colorParam.name).isEqualTo("director") + assertThat(colorParam.type.toString()).isEqualTo("mypackage.Human?") + assertThat(colorParam.defaultValue).isNotNull + assertThat(colorParam.defaultValue.toString()).isEqualTo("mypackage.Human(name = \"Harrison\")") + } + + @Test + fun `Codegen should fail when default value specifies property does not exist in input type`() { + val schema = """ + input Movie { + director: Person = { firstname: "Harrison", } + } + + input Person { + name: String = "Damian" + } + """.trimIndent() + + val exception = assertThrows { + CodeGen( + CodeGenConfig( + schemas = setOf(schema), + packageName = basePackageName, + language = Language.KOTLIN + ) + ).generate() + } + assertThat(exception.message).isEqualTo("Property \"firstname\" does not exist in input type \"Person\"") + } } diff --git a/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/Kotlin2Core.kt b/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/Kotlin2Core.kt index 6c27f9bad..10031b169 100644 --- a/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/Kotlin2Core.kt +++ b/graphql-dgs-codegen-shared-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/Kotlin2Core.kt @@ -173,9 +173,9 @@ abstract class GraphQLInput : InputValue { } @JvmStatic - protected inline fun default(arg: String): TValue? { + protected inline fun default(arg: String, defaultValue: TValue): TValue { DefaultTracker.add(TClass::class.qualifiedName!!, arg) - return null + return defaultValue } }