graphql 1.6.4 → 1.6.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +47 -0
- data/lib/generators/graphql/install_generator.rb +15 -20
- data/lib/generators/graphql/mutation_generator.rb +31 -1
- data/lib/generators/graphql/templates/mutation.erb +2 -2
- data/lib/generators/graphql/templates/mutation_type.erb +5 -0
- data/lib/generators/graphql/templates/schema.erb +0 -1
- data/lib/graphql/argument.rb +6 -5
- data/lib/graphql/backwards_compatibility.rb +18 -4
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +1 -1
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +1 -1
- data/lib/graphql/compatibility/lazy_execution_specification.rb +9 -2
- data/lib/graphql/define.rb +1 -0
- data/lib/graphql/define/defined_object_proxy.rb +1 -1
- data/lib/graphql/define/no_definition_error.rb +7 -0
- data/lib/graphql/enum_type.rb +4 -0
- data/lib/graphql/execution/execute.rb +3 -3
- data/lib/graphql/execution/field_result.rb +1 -1
- data/lib/graphql/execution/lazy/resolve.rb +10 -9
- data/lib/graphql/execution/multiplex.rb +6 -5
- data/lib/graphql/input_object_type.rb +5 -1
- data/lib/graphql/interface_type.rb +12 -3
- data/lib/graphql/query.rb +21 -5
- data/lib/graphql/query/context.rb +11 -0
- data/lib/graphql/schema.rb +48 -27
- data/lib/graphql/schema/build_from_definition.rb +1 -1
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +2 -2
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/traversal.rb +91 -0
- data/lib/graphql/schema/validation.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +41 -7
- data/lib/graphql/union_type.rb +13 -2
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -3
- data/spec/generators/graphql/install_generator_spec.rb +3 -1
- data/spec/generators/graphql/mutation_generator_spec.rb +14 -0
- data/spec/graphql/analysis/max_query_complexity_spec.rb +12 -1
- data/spec/graphql/analysis/query_complexity_spec.rb +1 -1
- data/spec/graphql/argument_spec.rb +29 -0
- data/spec/graphql/define/assign_argument_spec.rb +4 -4
- data/spec/graphql/define/instance_definable_spec.rb +1 -1
- data/spec/graphql/enum_type_spec.rb +8 -0
- data/spec/graphql/execution/lazy_spec.rb +30 -3
- data/spec/graphql/interface_type_spec.rb +44 -0
- data/spec/graphql/introspection/schema_type_spec.rb +3 -0
- data/spec/graphql/introspection/type_type_spec.rb +1 -0
- data/spec/graphql/object_type_spec.rb +8 -3
- data/spec/graphql/query/context_spec.rb +18 -0
- data/spec/graphql/query/executor_spec.rb +1 -1
- data/spec/graphql/query/literal_input_spec.rb +31 -15
- data/spec/graphql/query/serial_execution/value_resolution_spec.rb +1 -1
- data/spec/graphql/query/variables_spec.rb +25 -1
- data/spec/graphql/query_spec.rb +24 -9
- data/spec/graphql/relay/mutation_spec.rb +1 -1
- data/spec/graphql/schema/build_from_definition_spec.rb +1 -1
- data/spec/graphql/schema/loader_spec.rb +1 -1
- data/spec/graphql/schema/printer_spec.rb +1 -1
- data/spec/graphql/schema/{reduce_types_spec.rb → traversal_spec.rb} +21 -4
- data/spec/graphql/schema/warden_spec.rb +1 -1
- data/spec/graphql/schema_spec.rb +23 -2
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +133 -0
- data/spec/graphql/union_type_spec.rb +53 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/dummy/data.rb +14 -5
- data/spec/support/dummy/schema.rb +46 -5
- data/spec/support/star_wars/data.rb +10 -6
- data/spec/support/star_wars/schema.rb +5 -2
- metadata +8 -7
- data/lib/graphql/schema/instrumented_field_map.rb +0 -40
- data/lib/graphql/schema/reduce_types.rb +0 -69
- data/lib/graphql/schema/type_map.rb +0 -31
@@ -35,17 +35,18 @@ module GraphQL
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class << self
|
38
|
-
def run_all(schema, query_options, *
|
38
|
+
def run_all(schema, query_options, *args)
|
39
39
|
queries = query_options.map { |opts| GraphQL::Query.new(schema, nil, opts) }
|
40
|
-
run_queries(schema, queries, *
|
40
|
+
run_queries(schema, queries, *args)
|
41
41
|
end
|
42
42
|
|
43
43
|
# @param schema [GraphQL::Schema]
|
44
44
|
# @param queries [Array<GraphQL::Query>]
|
45
45
|
# @param context [Hash]
|
46
|
-
# @param max_complexity [Integer]
|
46
|
+
# @param max_complexity [Integer, nil]
|
47
47
|
# @return [Array<Hash>] One result per query
|
48
|
-
def run_queries(schema, queries, context: {}, max_complexity:
|
48
|
+
def run_queries(schema, queries, context: {}, max_complexity: schema.max_complexity)
|
49
|
+
|
49
50
|
if has_custom_strategy?(schema)
|
50
51
|
if queries.length != 1
|
51
52
|
raise ArgumentError, "Multiplexing doesn't support custom execution strategies, run one query at a time instead"
|
@@ -161,7 +162,7 @@ module GraphQL
|
|
161
162
|
end
|
162
163
|
|
163
164
|
multiplex_analyzers = schema.multiplex_analyzers
|
164
|
-
if max_complexity
|
165
|
+
if max_complexity
|
165
166
|
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(max_complexity)]
|
166
167
|
end
|
167
168
|
|
@@ -105,6 +105,11 @@ module GraphQL
|
|
105
105
|
warden = ctx.warden
|
106
106
|
result = GraphQL::Query::InputValidationResult.new
|
107
107
|
|
108
|
+
if input.is_a?(Array)
|
109
|
+
result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
|
110
|
+
return result
|
111
|
+
end
|
112
|
+
|
108
113
|
# We're not actually _using_ the coerced result, we're just
|
109
114
|
# using these methods to make sure that the object will
|
110
115
|
# behave like a hash below, when we call `each` on it.
|
@@ -121,7 +126,6 @@ module GraphQL
|
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
124
|
-
|
125
129
|
visible_arguments_map = warden.arguments(self).reduce({}) { |m, f| m[f.name] = f; m}
|
126
130
|
|
127
131
|
# Items in the input that are unexpected
|
@@ -23,14 +23,15 @@ module GraphQL
|
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
class InterfaceType < GraphQL::BaseType
|
26
|
-
accepts_definitions :fields, field: GraphQL::Define::AssignObjectField
|
26
|
+
accepts_definitions :fields, :resolve_type, field: GraphQL::Define::AssignObjectField
|
27
27
|
|
28
|
-
attr_accessor :fields
|
29
|
-
ensure_defined :fields
|
28
|
+
attr_accessor :fields, :resolve_type_proc
|
29
|
+
ensure_defined :fields, :resolve_type_proc, :resolve_type
|
30
30
|
|
31
31
|
def initialize
|
32
32
|
super
|
33
33
|
@fields = {}
|
34
|
+
@resolve_type_proc = nil
|
34
35
|
end
|
35
36
|
|
36
37
|
def initialize_copy(other)
|
@@ -42,6 +43,14 @@ module GraphQL
|
|
42
43
|
GraphQL::TypeKinds::INTERFACE
|
43
44
|
end
|
44
45
|
|
46
|
+
def resolve_type(value, ctx)
|
47
|
+
ctx.query.resolve_type(self, value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def resolve_type=(resolve_type_callable)
|
51
|
+
@resolve_type_proc = resolve_type_callable
|
52
|
+
end
|
53
|
+
|
45
54
|
# @return [GraphQL::Field] The defined field for `field_name`
|
46
55
|
def get_field(field_name)
|
47
56
|
fields[field_name]
|
data/lib/graphql/query.rb
CHANGED
@@ -27,7 +27,10 @@ module GraphQL
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
attr_reader :schema, :context, :root_value, :warden, :provided_variables
|
30
|
+
attr_reader :schema, :context, :root_value, :warden, :provided_variables
|
31
|
+
|
32
|
+
# @return [nil, String] The operation name provided by client or the one inferred from the document. Used to determine which operation to run.
|
33
|
+
attr_accessor :operation_name
|
31
34
|
|
32
35
|
# @return [Boolean] if false, static validation is skipped (execution behavior for invalid queries is undefined)
|
33
36
|
attr_accessor :validate
|
@@ -74,7 +77,13 @@ module GraphQL
|
|
74
77
|
@query_string = query_string || query
|
75
78
|
@document = document
|
76
79
|
|
77
|
-
|
80
|
+
# A two-layer cache of type resolution:
|
81
|
+
# { abstract_type => { value => resolved_type } }
|
82
|
+
@resolved_types_cache = Hash.new do |h1, k1|
|
83
|
+
h1[k1] = Hash.new do |h2, k2|
|
84
|
+
h2[k2] = @schema.resolve_type(k1, k2, @context)
|
85
|
+
end
|
86
|
+
end
|
78
87
|
|
79
88
|
@arguments_cache = ArgumentsCache.build(self)
|
80
89
|
|
@@ -184,11 +193,17 @@ module GraphQL
|
|
184
193
|
|
185
194
|
def_delegators :warden, :get_type, :get_field, :possible_types, :root_type_for_operation
|
186
195
|
|
196
|
+
# @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
|
187
197
|
# @param value [Object] Any runtime value
|
188
198
|
# @return [GraphQL::ObjectType, nil] The runtime type of `value` from {Schema#resolve_type}
|
189
199
|
# @see {#possible_types} to apply filtering from `only` / `except`
|
190
|
-
def resolve_type(value)
|
191
|
-
|
200
|
+
def resolve_type(abstract_type, value = :__undefined__)
|
201
|
+
if value.is_a?(Symbol) && value == :__undefined__
|
202
|
+
# Old method signature
|
203
|
+
value = abstract_type
|
204
|
+
abstract_type = nil
|
205
|
+
end
|
206
|
+
@resolved_types_cache[abstract_type][value]
|
192
207
|
end
|
193
208
|
|
194
209
|
def mutation?
|
@@ -250,7 +265,8 @@ module GraphQL
|
|
250
265
|
elsif parse_error
|
251
266
|
# This will be handled later
|
252
267
|
else
|
253
|
-
|
268
|
+
parse_error = GraphQL::ExecutionError.new("No query string was present")
|
269
|
+
@context.add_error(parse_error)
|
254
270
|
end
|
255
271
|
|
256
272
|
# Trying to execute a document
|
@@ -87,6 +87,17 @@ module GraphQL
|
|
87
87
|
GraphQL::Execution::Execute::SKIP
|
88
88
|
end
|
89
89
|
|
90
|
+
# Add error at query-level.
|
91
|
+
# @param error [GraphQL::ExecutionError] an execution error
|
92
|
+
# @return [void]
|
93
|
+
def add_error(error)
|
94
|
+
if !error.is_a?(ExecutionError)
|
95
|
+
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
96
|
+
end
|
97
|
+
errors << error
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
90
101
|
class FieldResolutionContext
|
91
102
|
extend GraphQL::Delegate
|
92
103
|
|
data/lib/graphql/schema.rb
CHANGED
@@ -4,15 +4,13 @@ require "graphql/schema/catchall_middleware"
|
|
4
4
|
require "graphql/schema/default_parse_error"
|
5
5
|
require "graphql/schema/default_type_error"
|
6
6
|
require "graphql/schema/invalid_type_error"
|
7
|
-
require "graphql/schema/instrumented_field_map"
|
8
7
|
require "graphql/schema/middleware_chain"
|
9
8
|
require "graphql/schema/null_mask"
|
10
9
|
require "graphql/schema/possible_types"
|
11
10
|
require "graphql/schema/rescue_middleware"
|
12
|
-
require "graphql/schema/reduce_types"
|
13
11
|
require "graphql/schema/timeout_middleware"
|
12
|
+
require "graphql/schema/traversal"
|
14
13
|
require "graphql/schema/type_expression"
|
15
|
-
require "graphql/schema/type_map"
|
16
14
|
require "graphql/schema/unique_within_type"
|
17
15
|
require "graphql/schema/validation"
|
18
16
|
require "graphql/schema/warden"
|
@@ -128,6 +126,7 @@ module GraphQL
|
|
128
126
|
@mutation_execution_strategy = self.class.default_execution_strategy
|
129
127
|
@subscription_execution_strategy = self.class.default_execution_strategy
|
130
128
|
@default_mask = GraphQL::Schema::NullMask
|
129
|
+
@rebuilding_artifacts = false
|
131
130
|
end
|
132
131
|
|
133
132
|
def initialize_copy(other)
|
@@ -185,15 +184,15 @@ module GraphQL
|
|
185
184
|
def define(**kwargs, &block)
|
186
185
|
super
|
187
186
|
ensure_defined
|
188
|
-
build_types_map
|
189
187
|
# Assert that all necessary configs are present:
|
190
188
|
validation_error = Validation.validate(self)
|
191
189
|
validation_error && raise(NotImplementedError, validation_error)
|
192
|
-
|
190
|
+
rebuild_artifacts
|
191
|
+
|
193
192
|
@definition_error = nil
|
194
193
|
nil
|
195
194
|
rescue StandardError => err
|
196
|
-
if @raise_definition_error
|
195
|
+
if @raise_definition_error || err.is_a?(CyclicalDefinitionError)
|
197
196
|
raise
|
198
197
|
else
|
199
198
|
# Raise this error _later_ to avoid messing with Rails constant loading
|
@@ -209,14 +208,17 @@ module GraphQL
|
|
209
208
|
def instrument(instrumentation_type, instrumenter)
|
210
209
|
@instrumenters[instrumentation_type] << instrumenter
|
211
210
|
if instrumentation_type == :field
|
212
|
-
|
211
|
+
rebuild_artifacts
|
213
212
|
end
|
214
213
|
end
|
215
214
|
|
216
215
|
# @see [GraphQL::Schema::Warden] Restricted access to members of a schema
|
217
216
|
# @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
|
218
217
|
def types
|
219
|
-
@types ||=
|
218
|
+
@types ||= begin
|
219
|
+
rebuild_artifacts
|
220
|
+
@types
|
221
|
+
end
|
220
222
|
end
|
221
223
|
|
222
224
|
# Execute a query on itself. Raises an error if the schema definition is invalid.
|
@@ -226,7 +228,8 @@ module GraphQL
|
|
226
228
|
if query_str
|
227
229
|
kwargs[:query] = query_str
|
228
230
|
end
|
229
|
-
|
231
|
+
# Since we're running one query, don't run a multiplex-level complexity analyzer
|
232
|
+
all_results = multiplex([kwargs], max_complexity: nil)
|
230
233
|
all_results[0]
|
231
234
|
end
|
232
235
|
|
@@ -248,9 +251,9 @@ module GraphQL
|
|
248
251
|
# @param queries [Array<Hash>] Keyword arguments for each query
|
249
252
|
# @param context [Hash] Multiplex-level context
|
250
253
|
# @return [Array<Hash>] One result for each query in the input
|
251
|
-
def multiplex(
|
254
|
+
def multiplex(queries, **kwargs)
|
252
255
|
with_definition_error_check {
|
253
|
-
GraphQL::Execution::Multiplex.run_all(self,
|
256
|
+
GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
|
254
257
|
}
|
255
258
|
end
|
256
259
|
|
@@ -271,7 +274,7 @@ module GraphQL
|
|
271
274
|
raise "Unexpected parent_type: #{parent_type}"
|
272
275
|
end
|
273
276
|
|
274
|
-
defined_field = @instrumented_field_map
|
277
|
+
defined_field = @instrumented_field_map[parent_type_name][field_name]
|
275
278
|
if defined_field
|
276
279
|
defined_field
|
277
280
|
elsif field_name == "__typename"
|
@@ -289,7 +292,7 @@ module GraphQL
|
|
289
292
|
# Fields for this type, after instrumentation is applied
|
290
293
|
# @return [Hash<String, GraphQL::Field>]
|
291
294
|
def get_fields(type)
|
292
|
-
@instrumented_field_map
|
295
|
+
@instrumented_field_map[type.name]
|
293
296
|
end
|
294
297
|
|
295
298
|
def type_from_ast(ast_node)
|
@@ -335,15 +338,29 @@ module GraphQL
|
|
335
338
|
# Determine the GraphQL type for a given object.
|
336
339
|
# This is required for unions and interfaces (including Relay's `Node` interface)
|
337
340
|
# @see [GraphQL::Schema::Warden] Restricted access to members of a schema
|
341
|
+
# @param type [GraphQL::UnionType, GraphQL:InterfaceType] the abstract type which is being resolved
|
338
342
|
# @param object [Any] An application object which GraphQL is currently resolving on
|
339
343
|
# @param ctx [GraphQL::Query::Context] The context for the current query
|
340
344
|
# @return [GraphQL::ObjectType] The type for exposing `object` in GraphQL
|
341
|
-
def resolve_type(object, ctx)
|
342
|
-
if
|
343
|
-
|
345
|
+
def resolve_type(type, object, ctx = :__undefined__)
|
346
|
+
if ctx == :__undefined__
|
347
|
+
# Old method signature
|
348
|
+
ctx = object
|
349
|
+
object = type
|
350
|
+
type = nil
|
351
|
+
end
|
352
|
+
|
353
|
+
# Prefer a type-local function; fall back to the schema-level function
|
354
|
+
type_proc = type && type.resolve_type_proc
|
355
|
+
type_result = if type_proc
|
356
|
+
type_proc.call(object, ctx)
|
357
|
+
else
|
358
|
+
if @resolve_type_proc.nil?
|
359
|
+
raise(NotImplementedError, "Can't determine GraphQL type for: #{object.inspect}, define `resolve_type (obj, ctx) -> { ... }` inside `Schema.define`.")
|
360
|
+
end
|
361
|
+
@resolve_type_proc.call(type, object, ctx)
|
344
362
|
end
|
345
363
|
|
346
|
-
type_result = @resolve_type_proc.call(object, ctx)
|
347
364
|
if type_result.nil?
|
348
365
|
nil
|
349
366
|
elsif !type_result.is_a?(GraphQL::BaseType)
|
@@ -355,7 +372,8 @@ module GraphQL
|
|
355
372
|
end
|
356
373
|
|
357
374
|
def resolve_type=(new_resolve_type_proc)
|
358
|
-
|
375
|
+
callable = GraphQL::BackwardsCompatibility.wrap_arity(new_resolve_type_proc, from: 2, to: 3, last: true, name: "Schema#resolve_type(type, obj, ctx)")
|
376
|
+
@resolve_type_proc = callable
|
359
377
|
end
|
360
378
|
|
361
379
|
# Fetch an application object by its unique id
|
@@ -513,17 +531,20 @@ module GraphQL
|
|
513
531
|
GraphQL::Relay::Mutation::Instrumentation,
|
514
532
|
]
|
515
533
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
534
|
+
def rebuild_artifacts
|
535
|
+
if @rebuilding_artifacts
|
536
|
+
raise CyclicalDefinitionError, "Part of the schema build process re-triggered the schema build process, causing an infinite loop. Avoid using Schema#types, Schema#possible_types, and Schema#get_field during schema build."
|
537
|
+
else
|
538
|
+
@rebuilding_artifacts = true
|
539
|
+
traversal = Traversal.new(self)
|
540
|
+
@types = traversal.type_map
|
541
|
+
@instrumented_field_map = traversal.instrumented_field_map
|
542
|
+
end
|
543
|
+
ensure
|
544
|
+
@rebuilding_artifacts = false
|
522
545
|
end
|
523
546
|
|
524
|
-
|
525
|
-
all_types = orphan_types + [query, mutation, subscription, GraphQL::Introspection::SchemaType]
|
526
|
-
@types = GraphQL::Schema::ReduceTypes.reduce(all_types.compact)
|
547
|
+
class CyclicalDefinitionError < GraphQL::Error
|
527
548
|
end
|
528
549
|
|
529
550
|
def with_definition_error_check
|
@@ -46,8 +46,8 @@ module GraphQL
|
|
46
46
|
|
47
47
|
# Check the normalized hash, not the user input:
|
48
48
|
if @resolve_hash.key?("resolve_type")
|
49
|
-
define_singleton_method :resolve_type do |type, ctx|
|
50
|
-
@resolve_hash.fetch("resolve_type").call(type, ctx)
|
49
|
+
define_singleton_method :resolve_type do |type, obj, ctx|
|
50
|
+
@resolve_hash.fetch("resolve_type").call(type, obj, ctx)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -33,7 +33,7 @@ module GraphQL
|
|
33
33
|
Schema.define(**kargs, raise_definition_error: true)
|
34
34
|
end
|
35
35
|
|
36
|
-
NullResolveType = ->(obj, ctx) {
|
36
|
+
NullResolveType = ->(type, obj, ctx) {
|
37
37
|
raise(NotImplementedError, "This schema was loaded from string, so it can't resolve types for objects")
|
38
38
|
}
|
39
39
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
# Visit the members of this schema and build up artifacts for runtime.
|
5
|
+
# @api private
|
6
|
+
class Traversal
|
7
|
+
# @return [Hash<String => GraphQL::BaseType]
|
8
|
+
attr_reader :type_map
|
9
|
+
|
10
|
+
# @return [Hash<String => Hash<String => GraphQL::Field>>]
|
11
|
+
attr_reader :instrumented_field_map
|
12
|
+
|
13
|
+
# @param schema [GraphQL::Schema]
|
14
|
+
def initialize(schema, introspection: true)
|
15
|
+
@schema = schema
|
16
|
+
@introspection = introspection
|
17
|
+
@field_instrumenters =
|
18
|
+
schema.instrumenters[:field] +
|
19
|
+
Schema::BUILT_IN_INSTRUMENTERS +
|
20
|
+
schema.instrumenters[:field_after_built_ins]
|
21
|
+
|
22
|
+
@type_map = {}
|
23
|
+
@instrumented_field_map = Hash.new { |h, k| h[k] = {} }
|
24
|
+
visit(schema, nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def visit(member, context_description)
|
30
|
+
case member
|
31
|
+
when GraphQL::Schema
|
32
|
+
# Find the starting points, then visit them
|
33
|
+
visit_roots = [member.query, member.mutation, member.subscription]
|
34
|
+
if @introspection
|
35
|
+
visit_roots << GraphQL::Introspection::SchemaType
|
36
|
+
end
|
37
|
+
visit_roots.concat(member.orphan_types)
|
38
|
+
visit_roots.compact!
|
39
|
+
visit_roots.each { |t| visit(t, t.name) }
|
40
|
+
when GraphQL::BaseType
|
41
|
+
type_defn = member.unwrap
|
42
|
+
prev_type = @type_map[type_defn.name]
|
43
|
+
# Continue to visit this type if it's the first time we've seen it:
|
44
|
+
if prev_type.nil?
|
45
|
+
validate_type(type_defn, context_description)
|
46
|
+
@type_map[type_defn.name] = type_defn
|
47
|
+
case type_defn
|
48
|
+
when GraphQL::ObjectType
|
49
|
+
type_defn.interfaces.each { |i| visit(i, "Interface on #{type_defn.name}") }
|
50
|
+
visit_fields(type_defn)
|
51
|
+
when GraphQL::InterfaceType
|
52
|
+
visit_fields(type_defn)
|
53
|
+
when GraphQL::UnionType
|
54
|
+
type_defn.possible_types.each { |t| visit(t, "Possible type for #{type_defn.name}") }
|
55
|
+
when GraphQL::InputObjectType
|
56
|
+
type_defn.arguments.each do |name, arg|
|
57
|
+
visit(arg.type, "Input field #{type_defn.name}.#{name}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
elsif !prev_type.equal?(type_defn)
|
61
|
+
# If the previous entry in the map isn't the same object we just found, raise.
|
62
|
+
raise("Duplicate type definition found for name '#{type_defn.name}'")
|
63
|
+
end
|
64
|
+
else
|
65
|
+
message = "Unexpected schema traversal member: #{member} (#{member.class.name})"
|
66
|
+
raise GraphQL::Schema::InvalidTypeError.new(message)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_fields(type_defn)
|
71
|
+
type_defn.all_fields.each do |field_defn|
|
72
|
+
instrumented_field_defn = @field_instrumenters.reduce(field_defn) do |defn, inst|
|
73
|
+
inst.instrument(type_defn, defn)
|
74
|
+
end
|
75
|
+
@instrumented_field_map[type_defn.name][instrumented_field_defn.name] = instrumented_field_defn
|
76
|
+
visit(instrumented_field_defn.type, "Field #{type_defn.name}.#{instrumented_field_defn.name}'s return type")
|
77
|
+
instrumented_field_defn.arguments.each do |name, arg|
|
78
|
+
visit(arg.type, "Argument #{name} on #{type_defn.name}.#{instrumented_field_defn.name}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate_type(member, context_description)
|
84
|
+
error_message = GraphQL::Schema::Validation.validate(member)
|
85
|
+
if error_message
|
86
|
+
raise GraphQL::Schema::InvalidTypeError.new("#{context_description} is invalid: #{error_message}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|