Skip to content

Commit

Permalink
[client] support @include and @Skip directives (#1341)
Browse files Browse the repository at this point in the history
  • Loading branch information
dariuszkuc authored Jan 14, 2022
1 parent a747e3d commit 8fbec9d
Show file tree
Hide file tree
Showing 22 changed files with 282 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ internal fun generateGraphQLObjectTypeSpec(

val constructorParameter = ParameterSpec.builder(propertySpec.name, propertySpec.type)
val className = propertySpec.type as? ClassName
if (className != null && context.enumClassToTypeSpecs.keys.contains(className)) {
if (propertySpec.type.isNullable) {
constructorParameter.defaultValue("null")
} else if (className != null && context.enumClassToTypeSpecs.keys.contains(className)) {
constructorParameter.defaultValue("%T.%N", className, className.member(UNKNOWN_VALUE))
}
constructorBuilder.addParameter(constructorParameter.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeName
import graphql.Directives.DeprecatedDirective
import graphql.Directives.IncludeDirective
import graphql.Directives.SkipDirective
import graphql.language.Field
import graphql.language.FieldDefinition
import graphql.language.NonNullType
Expand Down Expand Up @@ -59,7 +61,10 @@ internal fun generatePropertySpecs(
throw MissingArgumentException(context.operationName, objectName, selectedField.name, missingRequiredArguments)
}

val kotlinFieldType = generateTypeName(context, fieldDefinition.type, selectedField.selectionSet)
val optional = selectedField.directives.any {
it.name == SkipDirective.name || it.name == IncludeDirective.name
}
val kotlinFieldType = generateTypeName(context, fieldDefinition.type, selectedField.selectionSet, optional = optional)
val fieldName = selectedField.alias ?: fieldDefinition.name

val propertySpecBuilder = PropertySpec.builder(fieldName, kotlinFieldType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ import kotlinx.serialization.Serializable
/**
* Generate [TypeName] reference to a Kotlin class representation of an underlying GraphQL type.
*/
internal fun generateTypeName(context: GraphQLClientGeneratorContext, graphQLType: Type<*>, selectionSet: SelectionSet? = null): TypeName {
val nullable = graphQLType !is NonNullType
internal fun generateTypeName(
context: GraphQLClientGeneratorContext,
graphQLType: Type<*>,
selectionSet: SelectionSet? = null,
optional: Boolean = false
): TypeName {
val nullable = optional || graphQLType !is NonNullType

return when (graphQLType) {
is NonNullType -> generateTypeName(context, graphQLType.type, selectionSet)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query IncludeSkipDirectivesQuery($includeCondition: Boolean!, $skipCondition: Boolean!) {
enumQuery @include(if: $includeCondition)
scalarQuery @skip(if: $skipCondition) {
count
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.expediagroup.graphql.generated

import com.expediagroup.graphql.client.Generated
import com.expediagroup.graphql.client.types.GraphQLClientRequest
import com.expediagroup.graphql.generated.enums.CustomEnum
import com.expediagroup.graphql.generated.includeskipdirectivesquery.ScalarWrapper
import kotlin.Boolean
import kotlin.String
import kotlin.reflect.KClass

public const val INCLUDE_SKIP_DIRECTIVES_QUERY: String =
"query IncludeSkipDirectivesQuery(${'$'}includeCondition: Boolean!, ${'$'}skipCondition: Boolean!) {\n enumQuery @include(if: ${'$'}includeCondition)\n scalarQuery @skip(if: ${'$'}skipCondition) {\n count\n }\n}"

@Generated
public class IncludeSkipDirectivesQuery(
public override val variables: IncludeSkipDirectivesQuery.Variables
) : GraphQLClientRequest<IncludeSkipDirectivesQuery.Result> {
public override val query: String = INCLUDE_SKIP_DIRECTIVES_QUERY

public override val operationName: String = "IncludeSkipDirectivesQuery"

public override fun responseType(): KClass<IncludeSkipDirectivesQuery.Result> =
IncludeSkipDirectivesQuery.Result::class

@Generated
public data class Variables(
public val includeCondition: Boolean,
public val skipCondition: Boolean
)

@Generated
public data class Result(
/**
* Query that returns enum value
*/
public val enumQuery: CustomEnum? = null,
/**
* Query that returns wrapper object with all supported scalar types
*/
public val scalarQuery: ScalarWrapper? = null
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.expediagroup.graphql.generated.enums

import com.expediagroup.graphql.client.Generated
import com.fasterxml.jackson.`annotation`.JsonEnumDefaultValue
import com.fasterxml.jackson.`annotation`.JsonProperty
import kotlin.Deprecated

/**
* Custom enum description
*/
@Generated
public enum class CustomEnum {
/**
* First enum value
*/
ONE,
/**
* Third enum value
*/
@Deprecated(message = "only goes up to two")
THREE,
/**
* Second enum value
*/
TWO,
/**
* Lowercase enum value
*/
@JsonProperty("four")
FOUR,
/**
* This is a default enum value that will be used when attempting to deserialize unknown value.
*/
@JsonEnumDefaultValue
__UNKNOWN_VALUE,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.expediagroup.graphql.generated.includeskipdirectivesquery

import com.expediagroup.graphql.client.Generated
import kotlin.Int

/**
* Wrapper that holds all supported scalar types
*/
@Generated
public data class ScalarWrapper(
/**
* A signed 32-bit nullable integer value
*/
public val count: Int? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public class InputListQuery(
/**
* Query accepting list input
*/
public val listInputQuery: String?
public val listInputQuery: String? = null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ public data class ScalarWrapper(
/**
* Custom scalar of UUID
*/
public val custom: UUID?
public val custom: UUID? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ public data class ScalarWrapper(
*/
@JsonSerialize(converter = UUIDToAnyConverter::class)
@JsonDeserialize(converter = AnyToUUIDConverter::class)
public val custom: UUID?,
public val custom: UUID? = null,
/**
* List of custom scalar UUIDs
*/
@JsonSerialize(contentConverter = UUIDToAnyConverter::class)
@JsonDeserialize(contentConverter = AnyToUUIDConverter::class)
public val customList: List<UUID>?,
public val customList: List<UUID>? = null,
/**
* Custom scalar of Locale
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public data class ComplexObject(
* Optional value
* Second line of the description
*/
public val optional: String?,
public val optional: String? = null,
/**
* Some additional details
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public data class ComplexObject(
* Optional value
* Second line of the description
*/
public val optional: String?,
public val optional: String? = null,
/**
* Some additional details
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ public data class ScalarWrapper(
/**
* A signed 32-bit nullable integer value
*/
public val count: Int?,
public val count: Int? = null,
/**
* Custom scalar of UUID
*/
@Serializable(with = UUIDSerializer::class)
public val custom: UUID?,
public val custom: UUID? = null,
/**
* ID represents unique identifier that is not intended to be human readable
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ public data class ScalarWrapper(
/**
* A signed 32-bit nullable integer value
*/
public val count: Int?,
public val count: Int? = null,
/**
* Custom scalar of UUID
*/
@Serializable(with = UUIDSerializer::class)
public val custom: UUID?,
public val custom: UUID? = null,
/**
* ID represents unique identifier that is not intended to be human readable
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public data class ComplexObject(
* Optional value
* Second line of the description
*/
public val optional: String?,
public val optional: String? = null,
/**
* Some additional details
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.expediagroup.graphql.plugin.gradle.tasks.GraphQLGenerateSDLTask
import com.expediagroup.graphql.plugin.gradle.tasks.GraphQLGenerateTestClientTask

buildscript {
repositories {
mavenLocal {
content {
includeGroup("com.expediagroup")
}
}
}

val graphQLKotlinVersion = System.getenv("GRAPHQL_KOTLIN_VERSION") ?: "5.0.0-SNAPSHOT"
dependencies {
classpath("com.expediagroup:graphql-kotlin-gradle-plugin:$graphQLKotlinVersion")
}
}

plugins {
id("org.springframework.boot") version "2.5.5"
kotlin("jvm") version "1.5.31"
kotlin("plugin.spring") version "1.5.31"
}

apply(plugin = "com.expediagroup.graphql")

group = "com.expediagroup"
version = "0.0.1-SNAPSHOT"

repositories {
mavenCentral()
mavenLocal {
content {
includeGroup("com.expediagroup")
}
}
}

val graphQLKotlinVersion = System.getenv("GRAPHQL_KOTLIN_VERSION") ?: "5.0.0-SNAPSHOT"
val icuVersion = System.getenv("ICU_VERSION") ?: "69.1"
val junitVersion = System.getenv("JUNIT_VERSION") ?: "5.7.2"
val kotlinVersion = System.getenv("KOTLIN_VERSION") ?: "1.5.31"
val springBootVersion = System.getenv("SPRINGBOOT_VERSION") ?: "2.5.5"
dependencies {
implementation("com.expediagroup:graphql-kotlin-hooks-provider:$graphQLKotlinVersion")
implementation("com.expediagroup:graphql-kotlin-spring-client:$graphQLKotlinVersion")
implementation("com.expediagroup:graphql-kotlin-spring-server:$graphQLKotlinVersion")
implementation("com.ibm.icu:icu4j:$icuVersion")
testImplementation(kotlin("test-junit5", kotlinVersion))
testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
testImplementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion")
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}

tasks.withType<Test> {
useJUnitPlatform()
}

val graphqlGenerateSDL by tasks.getting(GraphQLGenerateSDLTask::class) {
packages.set(listOf("com.expediagroup.directives"))
}
val graphqlGenerateTestClient by tasks.getting(GraphQLGenerateTestClientTask::class) {
packageName.set("com.expediagroup.directives.generated")
schemaFile.set(graphqlGenerateSDL.schemaFile)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "include-skip-test"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.expediagroup.directives

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class Application

fun main(args: Array<String>) {
runApplication<Application>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.expediagroup.directives

import com.expediagroup.graphql.server.operations.Query
import org.springframework.stereotype.Component
import java.util.UUID

@Component
class SimpleQuery : Query {

fun simpleQuery(): String = UUID.randomUUID().toString()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
graphql:
packages:
- "com.expediagroup.directives"
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.expediagroup.directives

import com.expediagroup.directives.generated.IncludeSkipQuery
import com.expediagroup.graphql.client.spring.GraphQLWebClient
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.web.server.LocalServerPort
import java.util.UUID
import kotlin.test.assertNotNull
import kotlin.test.assertNull

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApplicationTest(@LocalServerPort private val port: Int) {

@Test
fun `verify include and skip directives are honored by client`(): Unit = runBlocking {
val client = GraphQLWebClient(url = "http://localhost:$port/graphql")

val skippedQuery = IncludeSkipQuery(variables = IncludeSkipQuery.Variables(
includeCondition = false,
skipCondition = true
))

val response = client.execute(skippedQuery)
val simpleResponse = response.data?.simpleQuery
assertNotNull(simpleResponse)
assertNotNull(UUID.fromString(simpleResponse))

val included = response.data?.included
assertNull(included)
val skipped = response.data?.skipped
assertNull(skipped)

val includeQuery = IncludeSkipQuery(variables = IncludeSkipQuery.Variables(
includeCondition = true,
skipCondition = false
))

val nonNullResponse = client.execute(includeQuery)
val simpleResponseNonNull = nonNullResponse.data?.simpleQuery
assertNotNull(simpleResponseNonNull)
assertNotNull(UUID.fromString(simpleResponseNonNull))

val includedNonNull = nonNullResponse.data?.included
assertNotNull(includedNonNull)
assertNotNull(UUID.fromString(includedNonNull))
val skippedNonNull = nonNullResponse.data?.skipped
assertNotNull(skippedNonNull)
assertNotNull(UUID.fromString(skippedNonNull))
}
}
Loading

0 comments on commit 8fbec9d

Please sign in to comment.