11package graphql.kickstart.tools.directive
22
3- import graphql.Scalars
43import graphql.introspection.Introspection
54import graphql.introspection.Introspection.DirectiveLocation.*
6- import graphql.kickstart.tools.SchemaError
75import graphql.kickstart.tools.SchemaParserOptions
86import graphql.kickstart.tools.directive.SchemaDirectiveWiringEnvironmentImpl.Parameters
9- import graphql.kickstart.tools.util.getDocumentation
10- import graphql.language.*
7+ import graphql.language.DirectiveDefinition
8+ import graphql.language.NamedNode
9+ import graphql.language.NodeParentTree
1110import graphql.schema.*
1211import graphql.schema.idl.RuntimeWiring
1312import graphql.schema.idl.SchemaDirectiveWiring
@@ -75,23 +74,20 @@ class DirectiveWiringHelper(
7574 }
7675
7776 private fun <T : GraphQLDirectiveContainer > wireDirectives (wrapper : WiringWrapper <T >): T {
78- val directivesContainer = wrapper.graphQlType.definition as DirectivesContainer <* >
79- val directives = buildDirectives(directivesContainer.directives, wrapper.directiveLocation)
80- val directivesByName = directives.associateBy { it.name }
8177 var output = wrapper.graphQlType
8278 // first the specific named directives
8379 wrapper.graphQlType.appliedDirectives.forEach { appliedDirective ->
84- val env = buildEnvironment(wrapper, directives, directivesByName[appliedDirective.name], appliedDirective)
80+ val env = buildEnvironment(wrapper, appliedDirective)
8581 val wiring = runtimeWiring.registeredDirectiveWiring[appliedDirective.name]
8682 wiring?.let { output = wrapper.invoker(it, env) }
8783 }
8884 // now call any statically added to the runtime
8985 runtimeWiring.directiveWiring.forEach { staticWiring ->
90- val env = buildEnvironment(wrapper, directives, null , null )
86+ val env = buildEnvironment(wrapper)
9187 output = wrapper.invoker(staticWiring, env)
9288 }
9389 // wiring factory is last (if present)
94- val env = buildEnvironment(wrapper, directives, null , null )
90+ val env = buildEnvironment(wrapper)
9591 if (runtimeWiring.wiringFactory.providesSchemaDirectiveWiring(env)) {
9692 val factoryWiring = runtimeWiring.wiringFactory.getSchemaDirectiveWiring(env)
9793 output = wrapper.invoker(factoryWiring, env)
@@ -100,102 +96,32 @@ class DirectiveWiringHelper(
10096 return output
10197 }
10298
103- fun buildDirectives (directives : List <Directive >, directiveLocation : Introspection .DirectiveLocation ): List <GraphQLDirective > {
104- val names = mutableSetOf<String >()
105- val output = mutableListOf<GraphQLDirective >()
106-
107- for (directive in directives) {
108- val repeatable = directiveDefinitions.find { it.name.equals(directive.name) }?.isRepeatable ? : false
109- if (repeatable || ! names.contains(directive.name)) {
110- names.add(directive.name)
111- output.add(
112- GraphQLDirective .newDirective()
113- .name(directive.name)
114- .description(getDocumentation(directive, options))
115- .comparatorRegistry(runtimeWiring.comparatorRegistry)
116- .validLocation(directiveLocation)
117- .repeatable(repeatable)
118- .apply {
119- directive.arguments.forEach { arg ->
120- argument(GraphQLArgument .newArgument()
121- .name(arg.name)
122- .type(buildDirectiveInputType(arg.value))
123- // TODO remove this once directives are fully replaced with applied directives
124- .valueLiteral(arg.value)
125- .build())
126- }
127- }
128- .build()
129- )
130- }
131- }
132-
133- return output
134- }
135-
136- private fun <T : GraphQLDirectiveContainer > buildEnvironment (wrapper : WiringWrapper <T >, directives : List <GraphQLDirective >, directive : GraphQLDirective ? , appliedDirective : GraphQLAppliedDirective ? ): SchemaDirectiveWiringEnvironmentImpl <T > {
99+ private fun <T : GraphQLDirectiveContainer > buildEnvironment (wrapper : WiringWrapper <T >, appliedDirective : GraphQLAppliedDirective ? = null): SchemaDirectiveWiringEnvironmentImpl <T > {
100+ val type = wrapper.graphQlType
101+ val directive = appliedDirective?.let { d -> type.directives.find { it.name == d.name } }
137102 val nodeParentTree = buildAstTree(* listOfNotNull(
138103 wrapper.fieldsContainer?.definition,
139104 wrapper.inputFieldsContainer?.definition,
140105 wrapper.enumType?.definition,
141106 wrapper.fieldDefinition?.definition,
142- wrapper.graphQlType .definition
107+ type .definition
143108 ).filterIsInstance<NamedNode <* >>()
144109 .toTypedArray())
145110 val elementParentTree = buildRuntimeTree(* listOfNotNull(
146111 wrapper.fieldsContainer,
147112 wrapper.inputFieldsContainer,
148113 wrapper.enumType,
149114 wrapper.fieldDefinition,
150- wrapper.graphQlType
115+ type
151116 ).toTypedArray())
152- val params = when (wrapper.graphQlType ) {
153- is GraphQLFieldDefinition -> schemaDirectiveParameters.newParams(wrapper.graphQlType , wrapper.fieldsContainer, nodeParentTree, elementParentTree)
117+ val params = when (type ) {
118+ is GraphQLFieldDefinition -> schemaDirectiveParameters.newParams(type , wrapper.fieldsContainer, nodeParentTree, elementParentTree)
154119 is GraphQLArgument -> schemaDirectiveParameters.newParams(wrapper.fieldDefinition, wrapper.fieldsContainer, nodeParentTree, elementParentTree)
155120 // object or interface
156- is GraphQLFieldsContainer -> schemaDirectiveParameters.newParams(wrapper.graphQlType , nodeParentTree, elementParentTree)
121+ is GraphQLFieldsContainer -> schemaDirectiveParameters.newParams(type , nodeParentTree, elementParentTree)
157122 else -> schemaDirectiveParameters.newParams(nodeParentTree, elementParentTree)
158123 }
159- return SchemaDirectiveWiringEnvironmentImpl (wrapper.graphQlType, directives, wrapper.graphQlType.appliedDirectives, directive, appliedDirective, params)
160- }
161-
162- fun buildDirectiveInputType (value : Value <* >): GraphQLInputType ? {
163- return when (value) {
164- is NullValue -> Scalars .GraphQLString
165- is FloatValue -> Scalars .GraphQLFloat
166- is StringValue -> Scalars .GraphQLString
167- is IntValue -> Scalars .GraphQLInt
168- is BooleanValue -> Scalars .GraphQLBoolean
169- is ArrayValue -> GraphQLList .list(buildDirectiveInputType(getArrayValueWrappedType(value)))
170- else -> throw SchemaError (" Directive values of type '${value::class .simpleName} ' are not supported yet." )
171- }
172- }
173-
174- private fun getArrayValueWrappedType (value : ArrayValue ): Value <* > {
175- // empty array [] is equivalent to [null]
176- if (value.values.isEmpty()) {
177- return NullValue .newNullValue().build()
178- }
179-
180- // get rid of null values
181- val nonNullValueList = value.values.filter { v -> v !is NullValue }
182-
183- // [null, null, ...] unwrapped is null
184- if (nonNullValueList.isEmpty()) {
185- return NullValue .newNullValue().build()
186- }
187-
188- // make sure the array isn't polymorphic
189- val distinctTypes = nonNullValueList
190- .map { it::class .java }
191- .distinct()
192-
193- if (distinctTypes.size > 1 ) {
194- throw SchemaError (" Arrays containing multiple types of values are not supported yet." )
195- }
196-
197- // peek at first value, value exists and is assured to be non-null
198- return nonNullValueList[0 ]
124+ return SchemaDirectiveWiringEnvironmentImpl (type, type.directives, type.appliedDirectives, directive, appliedDirective, params)
199125 }
200126
201127 private fun buildAstTree (vararg nodes : NamedNode <* >): NodeParentTree <NamedNode <* >> {
0 commit comments