diff --git a/lib/src/main/java/graphql/nadel/schema/NadelDirectives.kt b/lib/src/main/java/graphql/nadel/schema/NadelDirectives.kt index 1ce4a261e..6a38a0599 100644 --- a/lib/src/main/java/graphql/nadel/schema/NadelDirectives.kt +++ b/lib/src/main/java/graphql/nadel/schema/NadelDirectives.kt @@ -10,9 +10,12 @@ import graphql.language.BooleanValue import graphql.language.DirectiveDefinition.newDirectiveDefinition import graphql.language.EnumTypeDefinition.newEnumTypeDefinition import graphql.language.EnumValueDefinition.newEnumValueDefinition +import graphql.language.InputObjectTypeDefinition import graphql.language.InputObjectTypeDefinition.newInputObjectDefinition import graphql.language.ObjectValue import graphql.language.StringValue +import graphql.language.TypeDefinition +import graphql.language.TypeName import graphql.language.Value import graphql.nadel.dsl.FieldMappingDefinition import graphql.nadel.dsl.RemoteArgumentDefinition @@ -20,6 +23,7 @@ import graphql.nadel.dsl.RemoteArgumentSource import graphql.nadel.dsl.RemoteArgumentSource.SourceType import graphql.nadel.dsl.TypeMappingDefinition import graphql.nadel.dsl.UnderlyingServiceHydration +import graphql.nadel.engine.util.singleOfType import graphql.nadel.util.IntValue import graphql.nadel.util.description import graphql.nadel.util.emptyArrayValue @@ -34,6 +38,7 @@ import graphql.nadel.util.onInterface import graphql.nadel.util.onObject import graphql.nadel.util.onScalar import graphql.nadel.util.onUnion +import graphql.parser.Parser import graphql.schema.GraphQLAppliedDirective import graphql.schema.GraphQLAppliedDirectiveArgument import graphql.schema.GraphQLDirectiveContainer @@ -79,6 +84,38 @@ object NadelDirectives { ) .build() + val nadelWhenConditionPredicateDefinition = parseType( + """ + input NadelWhenConditionPredicate @oneOf { + startsWith: String + equals: JSON + matches: String + } + """.trimIndent(), + ) + + val nadelWhenConditionResultDefinition = newInputObjectDefinition() + .name("NadelWhenConditionResult") + .description("Specify a condition for the hydration to activate based on the result") + .inputValueDefinition( + name = "sourceField", + type = nonNull(GraphQLString), + ) + .inputValueDefinition( + name = "predicate", + type = nonNull(nadelWhenConditionPredicateDefinition), + ) + .build() + + val nadelWhenConditionDefinition = newInputObjectDefinition() + .name("NadelWhenCondition") + .description("Specify a condition for the hydration to activate") + .inputValueDefinition( + name = "result", + type = nonNull(nadelWhenConditionResultDefinition), + ) + .build() + val hydratedDirectiveDefinition = newDirectiveDefinition() .name("hydrated") .onFieldDefinition() @@ -135,6 +172,13 @@ object NadelDirectives { description = "The arguments to the hydrated field", type = nonNull(list(nonNull(nadelHydrationArgumentDefinition))), ) + .inputValueDefinition( + name = "when", + description = "Specify a condition for the hydration to activate", + type = TypeName.newTypeName() + .name(nadelWhenConditionDefinition.name) + .build() + ) .build() val dynamicServiceDirectiveDefinition = newDirectiveDefinition() @@ -482,6 +526,14 @@ object NadelDirectives { private fun resolveArgumentValue(graphQLArgument: GraphQLAppliedDirectiveArgument): T { @Suppress("UNCHECKED_CAST") // Trust caller. Can't do much - return ValuesResolver.valueToInternalValue(graphQLArgument.argumentValue, graphQLArgument.type, GraphQLContext.getDefault(), Locale.getDefault()) as T + return ValuesResolver.valueToInternalValue( + graphQLArgument.argumentValue, + graphQLArgument.type, + GraphQLContext.getDefault(), + Locale.getDefault() + ) as T + } + private inline fun > parseType(sdl: String): T { + return Parser.parse(sdl).definitions.singleOfType() } } diff --git a/lib/src/main/java/graphql/nadel/schema/OverallSchemaGenerator.kt b/lib/src/main/java/graphql/nadel/schema/OverallSchemaGenerator.kt index 29bd4be9f..c3e0b0489 100644 --- a/lib/src/main/java/graphql/nadel/schema/OverallSchemaGenerator.kt +++ b/lib/src/main/java/graphql/nadel/schema/OverallSchemaGenerator.kt @@ -72,6 +72,10 @@ internal class OverallSchemaGenerator { addIfNotPresent(overallRegistry, allDefinitions, NadelDirectives.nadelHydrationTemplateEnumDefinition) addIfNotPresent(overallRegistry, allDefinitions, NadelDirectives.hydratedFromDirectiveDefinition) addIfNotPresent(overallRegistry, allDefinitions, NadelDirectives.hydratedTemplateDirectiveDefinition) + addIfNotPresent(overallRegistry, allDefinitions, NadelDirectives.nadelWhenConditionPredicateDefinition) + addIfNotPresent(overallRegistry, allDefinitions, NadelDirectives.nadelWhenConditionDefinition) + addIfNotPresent(overallRegistry, allDefinitions, NadelDirectives.nadelWhenConditionResultDefinition) + addIfNotPresent(overallRegistry, allDefinitions, newScalarTypeDefinition() .name(ExtendedScalars.Json.name) .build()) diff --git a/lib/src/test/kotlin/graphql/nadel/NadelSchemasTest.kt b/lib/src/test/kotlin/graphql/nadel/NadelSchemasTest.kt index f853fd240..79947072b 100644 --- a/lib/src/test/kotlin/graphql/nadel/NadelSchemasTest.kt +++ b/lib/src/test/kotlin/graphql/nadel/NadelSchemasTest.kt @@ -73,7 +73,7 @@ class NadelSchemasTest : DescribeSpec({ .build() // then - assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "JSON")) + assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "JSON", "NadelWhenCondition", "NadelWhenConditionPredicate", "NadelWhenConditionResult")) val testService = schemas.services.single() assert(testService.underlyingSchema.userTypeNames == setOf("World", "Echo", "Query", "Food")) } @@ -149,7 +149,7 @@ class NadelSchemasTest : DescribeSpec({ .build() // then - assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "JSON")) + assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "JSON", "NadelWhenCondition", "NadelWhenConditionPredicate", "NadelWhenConditionResult")) val testService = schemas.services.single() assert(testService.underlyingSchema.userTypeNames == setOf("World", "Echo", "Query", "Food")) } @@ -211,7 +211,7 @@ class NadelSchemasTest : DescribeSpec({ .build() // then - assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "Issue", "JSON")) + assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "Issue", "JSON", "NadelWhenCondition", "NadelWhenConditionPredicate", "NadelWhenConditionResult")) val issueService = schemas.services.single { it.name == "issue" } assert(issueService.underlyingSchema.userTypeNames == setOf("Query", "Issue")) @@ -272,7 +272,7 @@ class NadelSchemasTest : DescribeSpec({ .build() // then - assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "Issue", "JSON")) + assert(schemas.engineSchema.userTypeNames == setOf("World", "Echo", "Query", "Issue", "JSON", "NadelWhenCondition", "NadelWhenConditionPredicate", "NadelWhenConditionResult")) } it("does not validate the schemas") { @@ -321,7 +321,7 @@ class NadelSchemasTest : DescribeSpec({ .build() // then - assert(schemas.engineSchema.userTypeNames == setOf("Query", "World", "Echo", "Issue", "JSON")) + assert(schemas.engineSchema.userTypeNames == setOf("Query", "World", "Echo", "Issue", "JSON", "NadelWhenCondition", "NadelWhenConditionPredicate", "NadelWhenConditionResult")) val testService = schemas.services.first { it.name == "test" } assert(testService.definitionRegistry.typeNames == setOf("Query", "Echo", "World")) diff --git a/lib/src/test/kotlin/graphql/nadel/schema/NadelDirectivesTest.kt b/lib/src/test/kotlin/graphql/nadel/schema/NadelDirectivesTest.kt index f057b25b7..d8913d94e 100644 --- a/lib/src/test/kotlin/graphql/nadel/schema/NadelDirectivesTest.kt +++ b/lib/src/test/kotlin/graphql/nadel/schema/NadelDirectivesTest.kt @@ -9,6 +9,9 @@ import graphql.nadel.schema.NadelDirectives.nadelHydrationArgumentDefinition import graphql.nadel.schema.NadelDirectives.nadelHydrationComplexIdentifiedBy import graphql.nadel.schema.NadelDirectives.nadelHydrationFromArgumentDefinition import graphql.nadel.schema.NadelDirectives.nadelHydrationTemplateEnumDefinition +import graphql.nadel.schema.NadelDirectives.nadelWhenConditionDefinition +import graphql.nadel.schema.NadelDirectives.nadelWhenConditionPredicateDefinition +import graphql.nadel.schema.NadelDirectives.nadelWhenConditionResultDefinition import graphql.schema.GraphQLSchema import graphql.schema.idl.MockedWiringFactory import graphql.schema.idl.RuntimeWiring @@ -30,6 +33,9 @@ class NadelDirectivesTest : DescribeSpec({ ${AstPrinter.printAst(nadelHydrationTemplateEnumDefinition)} ${AstPrinter.printAst(hydratedFromDirectiveDefinition)} ${AstPrinter.printAst(hydratedTemplateDirectiveDefinition)} + ${AstPrinter.printAst(nadelWhenConditionDefinition)} + ${AstPrinter.printAst(nadelWhenConditionPredicateDefinition)} + ${AstPrinter.printAst(nadelWhenConditionResultDefinition)} scalar JSON """ @@ -55,6 +61,12 @@ class NadelDirectivesTest : DescribeSpec({ {name: "fieldVal" value: "$source.namespace.issueId"} {name: "argVal" value: "$argument.cloudId"} ] + when: { + result: { + sourceField: "type" + predicate: { equals: "issue" } + } + } ) } """.trimIndent())