Skip to content

feat: pass DataFetcherEnvironment to arguments #173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package com.expedia.graphql.sample.model
import com.expedia.graphql.annotations.GraphQLDescription

@GraphQLDescription("simple response that contains value read from context")
data class ContextualResponse(val passedInValue: Int, val contextValue: String)
data class ContextualResponse(val passedInValue: Int, val contextValue: String)
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.springframework.stereotype.Component
* schema and will be automatically autowired at runtime using value from the environment.
*
* @see com.expedia.graphql.sample.context.MyGraphQLContextBuilder
* @see com.expedia.graphql.FunctionDataFetcher
* @see com.expedia.graphql.execution.FunctionDataFetcher
*/
@Component
class ContextualQuery: Query {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.expedia.graphql.sample.query

import graphql.schema.DataFetchingEnvironment
import org.springframework.stereotype.Component

@Component
class EnvironmentQuery : Query {
fun nestedEnvironment(value: Int) = NestedNode(value)
}

data class NestedNode(
val value: Int,
val parentValue: Int? = null
) {
fun nested(environment: DataFetchingEnvironment, value: Int): NestedNode {
val parentValue: Int? = if (environment.executionStepInfo.hasParent()) {
environment.executionStepInfo.parent.getArgument("value")
} else {
null
}

return NestedNode(value, parentValue)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.expedia.graphql.execution

import com.expedia.graphql.generator.extensions.getName
import com.expedia.graphql.generator.extensions.isDataFetchingEnvironment
import com.expedia.graphql.generator.extensions.isGraphQLContext
import com.expedia.graphql.generator.extensions.javaTypeClass
import com.fasterxml.jackson.databind.ObjectMapper
Expand Down Expand Up @@ -51,14 +52,18 @@ class FunctionDataFetcher(
}

private fun mapParameterToValue(param: KParameter, environment: DataFetchingEnvironment): Any? =
if (param.isGraphQLContext()) {
environment.getContext()
} else {
val name = param.getName()
val klazz = param.javaTypeClass()
val value = objectMapper.convertValue(environment.arguments[name], klazz)
val predicateResult = executionPredicate?.evaluate(value = value, parameter = param, environment = environment)

predicateResult ?: value
when {
param.isGraphQLContext() -> environment.getContext()
param.isDataFetchingEnvironment() -> environment
else -> convertParameterValue(param, environment)
}

private fun convertParameterValue(param: KParameter, environment: DataFetchingEnvironment): Any? {
val name = param.getName()
val klazz = param.javaTypeClass()
val value = objectMapper.convertValue(environment.arguments[name], klazz)
val predicateResult = executionPredicate?.evaluate(value = value, parameter = param, environment = environment)

return predicateResult ?: value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package com.expedia.graphql.generator.extensions

import com.expedia.graphql.annotations.GraphQLContext
import com.expedia.graphql.exceptions.CouldNotGetNameOfKParameterException
import graphql.schema.DataFetchingEnvironment
import kotlin.reflect.KParameter
import kotlin.reflect.full.findAnnotation

internal fun KParameter.isInterface() = this.type.getKClass().isInterface()

internal fun KParameter.isGraphQLContext() = this.findAnnotation<GraphQLContext>() != null

internal fun KParameter.isDataFetchingEnvironment() = this.type.classifier == DataFetchingEnvironment::class

@Throws(CouldNotGetNameOfKParameterException::class)
internal fun KParameter.getName(): String =
this.name ?: throw CouldNotGetNameOfKParameterException(this)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.expedia.graphql.generator.extensions.getDeprecationReason
import com.expedia.graphql.generator.extensions.getGraphQLDescription
import com.expedia.graphql.generator.extensions.getName
import com.expedia.graphql.generator.extensions.getTypeOfFirstArgument
import com.expedia.graphql.generator.extensions.isDataFetchingEnvironment
import com.expedia.graphql.generator.extensions.isGraphQLContext
import com.expedia.graphql.generator.extensions.isInterface
import com.expedia.graphql.generator.extensions.safeCast
Expand Down Expand Up @@ -37,6 +38,7 @@ internal class FunctionTypeBuilder(generator: SchemaGenerator) : TypeBuilder(gen

fn.valueParameters
.filterNot { it.isGraphQLContext() }
.filterNot { it.isDataFetchingEnvironment() }
.forEach {
// deprecation of arguments is currently unsupported: https://github.com/facebook/graphql/issues/197
builder.argument(argument(it))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ internal class FunctionDataFetcherTest {
fun printList(items: List<String>) = items.joinToString(separator = ":")

fun context(@GraphQLContext string: String) = string

fun dataFetchingEnvironment(environment: DataFetchingEnvironment) = environment.field.name
}

@Test
Expand Down Expand Up @@ -93,4 +95,14 @@ internal class FunctionDataFetcherTest {
dataFetcher.get(mockEnvironmet)
}
}

@Test
fun `dataFetchingEnvironement is passed as an argument`() {
val dataFetcher = FunctionDataFetcher(target = MyClass(), fn = MyClass::dataFetchingEnvironment)
val mockEnvironmet: DataFetchingEnvironment = mockk()
every { mockEnvironmet.field } returns mockk {
every { name } returns "fooBarBaz"
}
assertEquals(expected = "fooBarBaz", actual = dataFetcher.get(mockEnvironmet))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.expedia.graphql.generator.extensions

import com.expedia.graphql.annotations.GraphQLDescription
import com.expedia.graphql.exceptions.CouldNotGetNameOfKParameterException
import graphql.schema.DataFetchingEnvironment
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -29,6 +30,8 @@ internal class KParameterExtensionsKtTest {
internal fun noDescription(myClass: MyClass) = myClass

internal fun paramDescription(@GraphQLDescription("param description") myClass: MyClass) = myClass

internal fun dataFetchingEnvironment(environment: DataFetchingEnvironment) = environment.field.name
}

@Test
Expand Down Expand Up @@ -69,4 +72,16 @@ internal class KParameterExtensionsKtTest {
val param = Container::interfaceInput.findParameterByName("myInterface")
assertTrue(param?.isInterface().isTrue())
}

@Test
fun `valid DataFetchingEnvironment passes`() {
val param = Container::dataFetchingEnvironment.findParameterByName("environment")
assertTrue(param?.isDataFetchingEnvironment().isTrue())
}

@Test
fun `invalid DataFetchingEnvironment fails`() {
val param = Container::interfaceInput.findParameterByName("myInterface")
assertFalse(param?.isDataFetchingEnvironment().isTrue())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.expedia.graphql.annotations.GraphQLDirective
import com.expedia.graphql.execution.FunctionDataFetcher
import graphql.Scalars
import graphql.introspection.Introspection
import graphql.schema.DataFetchingEnvironment
import graphql.schema.GraphQLNonNull
import org.junit.jupiter.api.Test
import java.util.UUID
Expand Down Expand Up @@ -52,6 +53,8 @@ internal class FunctionTypeBuilderTest : TypeTestHelper() {
fun context(@GraphQLContext context: String, string: String) = "$context and $string"

fun completableFuture(num: Int): CompletableFuture<Int> = CompletableFuture.completedFuture(num)

fun dataFetchingEnvironment(environment: DataFetchingEnvironment): String = environment.field.name
}

@Test
Expand Down Expand Up @@ -160,4 +163,13 @@ internal class FunctionTypeBuilderTest : TypeTestHelper() {
assertEquals(expected = 1, actual = result.arguments.size)
assertEquals("Int", (result.type as? GraphQLNonNull)?.wrappedType?.name)
}

@Test
fun `DataFetchingEnvironment argument type is ignored`() {
val kFunction = Happy::dataFetchingEnvironment
val result = builder.function(fn = kFunction)

assertEquals(expected = 0, actual = result.arguments.size)
assertEquals("String", (result.type as? GraphQLNonNull)?.wrappedType?.name)
}
}