Skip to content

Commit

Permalink
feat: [k2] add support for data types inherited from extended interfa…
Browse files Browse the repository at this point in the history
…ce (#741)

* feat: add support for data types inherited from extended interface

* refactor: change expected integration test by graphql specification & rollback extensionTypes definition (because of graphql specification)

graphql specification (interface extensions): https://spec.graphql.org/October2021/#sel-GAHbpBBBBKBUytV

* style: lint check
  • Loading branch information
Lavmee authored Oct 4, 2024
1 parent 57b2e1e commit 78f6c92
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected

import com.netflix.graphql.dgs.client.codegen.InputValueSerializerInterface
import com.netflix.graphql.dgs.codegen.GraphQLProjection
import com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.client.QueryProjection
import graphql.language.OperationDefinition
import kotlin.String

public object DgsClient {
public fun buildQuery(inputValueSerializer: InputValueSerializerInterface? = null,
_projection: QueryProjection.() -> QueryProjection): String =
GraphQLProjection.asQuery(OperationDefinition.Operation.QUERY,
QueryProjection(inputValueSerializer), _projection)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected

import kotlin.String

public object DgsConstants {
public const val QUERY_TYPE: String = "Query"

public object QUERY {
public const val TYPE_NAME: String = "Query"

public const val People: String = "people"
}

public object EMPLOYEE {
public const val TYPE_NAME: String = "Employee"

public const val Firstname: String = "firstname"

public const val Lastname: String = "lastname"

public const val Company: String = "company"

public const val Age: String = "age"
}

public object PERSON {
public const val TYPE_NAME: String = "Person"

public const val Firstname: String = "firstname"

public const val Lastname: String = "lastname"

public const val Age: String = "age"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.client

import com.netflix.graphql.dgs.client.codegen.InputValueSerializerInterface
import com.netflix.graphql.dgs.codegen.GraphQLProjection

public class EmployeeProjection(
inputValueSerializer: InputValueSerializerInterface? = null,
) : GraphQLProjection(inputValueSerializer) {
public val firstname: EmployeeProjection
get() {
field("firstname")
return this
}

public val lastname: EmployeeProjection
get() {
field("lastname")
return this
}

public val company: EmployeeProjection
get() {
field("company")
return this
}

public val age: EmployeeProjection
get() {
field("age")
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.client

import com.netflix.graphql.dgs.client.codegen.InputValueSerializerInterface
import com.netflix.graphql.dgs.codegen.GraphQLProjection

public class PersonProjection(
inputValueSerializer: InputValueSerializerInterface? = null,
) : GraphQLProjection(inputValueSerializer) {
public val firstname: PersonProjection
get() {
field("firstname")
return this
}

public val lastname: PersonProjection
get() {
field("lastname")
return this
}

public fun onEmployee(_projection: EmployeeProjection.() -> EmployeeProjection):
PersonProjection {
fragment("Employee", EmployeeProjection(), _projection)
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.client

import com.netflix.graphql.dgs.client.codegen.InputValueSerializerInterface
import com.netflix.graphql.dgs.codegen.GraphQLProjection
import kotlin.String

public class QueryProjection(
inputValueSerializer: InputValueSerializerInterface? = null,
) : GraphQLProjection(inputValueSerializer) {
public fun people(_alias: String? = null, _projection: PersonProjection.() -> PersonProjection):
QueryProjection {
field(_alias, "people", PersonProjection(inputValueSerializer), _projection)
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.types

import com.fasterxml.jackson.`annotation`.JsonIgnoreProperties
import com.fasterxml.jackson.`annotation`.JsonProperty
import com.fasterxml.jackson.`annotation`.JsonTypeInfo
import com.fasterxml.jackson.databind.`annotation`.JsonDeserialize
import com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder
import java.lang.IllegalStateException
import kotlin.Int
import kotlin.String
import kotlin.Suppress
import kotlin.jvm.JvmName

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonDeserialize(builder = Employee.Builder::class)
public class Employee(
firstname: () -> String = firstnameDefault,
lastname: () -> String? = lastnameDefault,
company: () -> String? = companyDefault,
age: () -> Int = ageDefault,
) : Person {
private val __firstname: () -> String = firstname

private val __lastname: () -> String? = lastname

private val __company: () -> String? = company

private val __age: () -> Int = age

@Suppress("INAPPLICABLE_JVM_NAME")
@get:JvmName("getFirstname")
override val firstname: String
get() = __firstname.invoke()

@Suppress("INAPPLICABLE_JVM_NAME")
@get:JvmName("getLastname")
override val lastname: String?
get() = __lastname.invoke()

@get:JvmName("getCompany")
public val company: String?
get() = __company.invoke()

@Suppress("INAPPLICABLE_JVM_NAME")
@get:JvmName("getAge")
override val age: Int
get() = __age.invoke()

public companion object {
private val firstnameDefault: () -> String =
{ throw IllegalStateException("Field `firstname` was not requested") }

private val lastnameDefault: () -> String? =
{ throw IllegalStateException("Field `lastname` was not requested") }

private val companyDefault: () -> String? =
{ throw IllegalStateException("Field `company` was not requested") }

private val ageDefault: () -> Int =
{ throw IllegalStateException("Field `age` was not requested") }
}

@JsonPOJOBuilder
@JsonIgnoreProperties("__typename")
public class Builder {
private var firstname: () -> String = firstnameDefault

private var lastname: () -> String? = lastnameDefault

private var company: () -> String? = companyDefault

private var age: () -> Int = ageDefault

@JsonProperty("firstname")
public fun withFirstname(firstname: String): Builder = this.apply {
this.firstname = { firstname }
}

@JsonProperty("lastname")
public fun withLastname(lastname: String?): Builder = this.apply {
this.lastname = { lastname }
}

@JsonProperty("company")
public fun withCompany(company: String?): Builder = this.apply {
this.company = { company }
}

@JsonProperty("age")
public fun withAge(age: Int): Builder = this.apply {
this.age = { age }
}

public fun build(): Employee = Employee(
firstname = firstname,
lastname = lastname,
company = company,
age = age,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.types

import com.fasterxml.jackson.`annotation`.JsonSubTypes
import com.fasterxml.jackson.`annotation`.JsonTypeInfo
import kotlin.Int
import kotlin.String
import kotlin.Suppress
import kotlin.jvm.JvmName

@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "__typename",
)
@JsonSubTypes(value = [
JsonSubTypes.Type(value = Employee::class, name = "Employee")
])
public sealed interface Person {
@Suppress("INAPPLICABLE_JVM_NAME")
@get:JvmName("getFirstname")
public val firstname: String

@Suppress("INAPPLICABLE_JVM_NAME")
@get:JvmName("getLastname")
public val lastname: String?

@Suppress("INAPPLICABLE_JVM_NAME")
@get:JvmName("getAge")
public val age: Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.netflix.graphql.dgs.codegen.cases.dataClassWithExtendedInterfaceInheritance.expected.types

import com.fasterxml.jackson.`annotation`.JsonIgnoreProperties
import com.fasterxml.jackson.`annotation`.JsonProperty
import com.fasterxml.jackson.`annotation`.JsonTypeInfo
import com.fasterxml.jackson.databind.`annotation`.JsonDeserialize
import com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder
import java.lang.IllegalStateException
import kotlin.collections.List
import kotlin.jvm.JvmName

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonDeserialize(builder = Query.Builder::class)
public class Query(
people: () -> List<Person?>? = peopleDefault,
) {
private val __people: () -> List<Person?>? = people

@get:JvmName("getPeople")
public val people: List<Person?>?
get() = __people.invoke()

public companion object {
private val peopleDefault: () -> List<Person?>? =
{ throw IllegalStateException("Field `people` was not requested") }
}

@JsonPOJOBuilder
@JsonIgnoreProperties("__typename")
public class Builder {
private var people: () -> List<Person?>? = peopleDefault

@JsonProperty("people")
public fun withPeople(people: List<Person?>?): Builder = this.apply {
this.people = { people }
}

public fun build(): Query = Query(
people = people,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
type Query {
people: [Person]
}

interface Person {
firstname: String!
lastname: String
}

type Employee implements Person {
firstname: String!
lastname: String
company: String
}

extend interface Person {
age: Int!
}

extend type Employee {
age: Int!
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ class Kotlin2TypeLookup(
*/
private val interfaceFields: Map<String, List<String>> = document
.getDefinitionsOfType(InterfaceTypeDefinition::class.java)
.associate { i -> i.name to i.fieldDefinitions.map { it.name } }
.groupBy(keySelector = { it.name }, valueTransform = { it.fieldDefinitions })
.mapValues { e -> e.value.flatten().map { i -> i.name } }

/**
* A map of interfaces to the types that implement them
Expand Down

0 comments on commit 78f6c92

Please sign in to comment.