graphql 1.6.4 → 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|