1
1
package graphql.kickstart.tools.directive
2
2
3
- import graphql.Scalars
4
3
import graphql.introspection.Introspection
5
4
import graphql.introspection.Introspection.DirectiveLocation.*
6
- import graphql.kickstart.tools.SchemaError
7
5
import graphql.kickstart.tools.SchemaParserOptions
8
6
import 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
11
10
import graphql.schema.*
12
11
import graphql.schema.idl.RuntimeWiring
13
12
import graphql.schema.idl.SchemaDirectiveWiring
@@ -75,23 +74,20 @@ class DirectiveWiringHelper(
75
74
}
76
75
77
76
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 }
81
77
var output = wrapper.graphQlType
82
78
// first the specific named directives
83
79
wrapper.graphQlType.appliedDirectives.forEach { appliedDirective ->
84
- val env = buildEnvironment(wrapper, directives, directivesByName[appliedDirective.name], appliedDirective)
80
+ val env = buildEnvironment(wrapper, appliedDirective)
85
81
val wiring = runtimeWiring.registeredDirectiveWiring[appliedDirective.name]
86
82
wiring?.let { output = wrapper.invoker(it, env) }
87
83
}
88
84
// now call any statically added to the runtime
89
85
runtimeWiring.directiveWiring.forEach { staticWiring ->
90
- val env = buildEnvironment(wrapper, directives, null , null )
86
+ val env = buildEnvironment(wrapper)
91
87
output = wrapper.invoker(staticWiring, env)
92
88
}
93
89
// wiring factory is last (if present)
94
- val env = buildEnvironment(wrapper, directives, null , null )
90
+ val env = buildEnvironment(wrapper)
95
91
if (runtimeWiring.wiringFactory.providesSchemaDirectiveWiring(env)) {
96
92
val factoryWiring = runtimeWiring.wiringFactory.getSchemaDirectiveWiring(env)
97
93
output = wrapper.invoker(factoryWiring, env)
@@ -100,102 +96,32 @@ class DirectiveWiringHelper(
100
96
return output
101
97
}
102
98
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 } }
137
102
val nodeParentTree = buildAstTree(* listOfNotNull(
138
103
wrapper.fieldsContainer?.definition,
139
104
wrapper.inputFieldsContainer?.definition,
140
105
wrapper.enumType?.definition,
141
106
wrapper.fieldDefinition?.definition,
142
- wrapper.graphQlType .definition
107
+ type .definition
143
108
).filterIsInstance<NamedNode <* >>()
144
109
.toTypedArray())
145
110
val elementParentTree = buildRuntimeTree(* listOfNotNull(
146
111
wrapper.fieldsContainer,
147
112
wrapper.inputFieldsContainer,
148
113
wrapper.enumType,
149
114
wrapper.fieldDefinition,
150
- wrapper.graphQlType
115
+ type
151
116
).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)
154
119
is GraphQLArgument -> schemaDirectiveParameters.newParams(wrapper.fieldDefinition, wrapper.fieldsContainer, nodeParentTree, elementParentTree)
155
120
// object or interface
156
- is GraphQLFieldsContainer -> schemaDirectiveParameters.newParams(wrapper.graphQlType , nodeParentTree, elementParentTree)
121
+ is GraphQLFieldsContainer -> schemaDirectiveParameters.newParams(type , nodeParentTree, elementParentTree)
157
122
else -> schemaDirectiveParameters.newParams(nodeParentTree, elementParentTree)
158
123
}
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)
199
125
}
200
126
201
127
private fun buildAstTree (vararg nodes : NamedNode <* >): NodeParentTree <NamedNode <* >> {
0 commit comments