graphql 1.10.7 → 1.10.12
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/object_generator.rb +50 -8
- data/lib/graphql.rb +4 -4
- data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
- data/lib/graphql/execution/instrumentation.rb +1 -1
- data/lib/graphql/execution/interpreter.rb +2 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +36 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -7
- data/lib/graphql/execution/interpreter/runtime.rb +38 -35
- data/lib/graphql/execution/lookahead.rb +3 -1
- data/lib/graphql/internal_representation/scope.rb +2 -2
- data/lib/graphql/internal_representation/visit.rb +2 -2
- data/lib/graphql/object_type.rb +1 -1
- data/lib/graphql/relay/base_connection.rb +0 -2
- data/lib/graphql/schema.rb +17 -11
- data/lib/graphql/schema/argument.rb +6 -0
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/enum.rb +9 -1
- data/lib/graphql/schema/field.rb +30 -21
- data/lib/graphql/schema/input_object.rb +10 -9
- data/lib/graphql/schema/interface.rb +5 -0
- data/lib/graphql/schema/list.rb +2 -1
- data/lib/graphql/schema/loader.rb +3 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/member/has_arguments.rb +33 -13
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/object.rb +9 -2
- data/lib/graphql/schema/resolver.rb +1 -1
- data/lib/graphql/schema/union.rb +6 -0
- data/lib/graphql/schema/warden.rb +7 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/subscription_root.rb +10 -2
- data/lib/graphql/tracing.rb +5 -4
- data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
- data/lib/graphql/tracing/platform_tracing.rb +14 -0
- data/lib/graphql/tracing/scout_tracing.rb +11 -0
- data/lib/graphql/types/iso_8601_date.rb +2 -2
- data/lib/graphql/types/iso_8601_date_time.rb +19 -15
- data/lib/graphql/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 840c0d49f547bfb3a44878e0631df87f3b287179a077806169480a8d2820b30b
|
4
|
+
data.tar.gz: 498c331da5d7f3818054def18b1a964f1ff97699b79b7be1635eefbad0dcc946
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cc7bdbeff9a1c70f28c50ffa93740020173a969d34e9aa8872073f721555ebe1c67fa74e13d012d6c547bc4aa6515ab7dafe173dd75bfdea9fe0c4a2b256ac8
|
7
|
+
data.tar.gz: a466d22dd0e14f95b7273faedc68d3ff214e0f72411e491f454d037b1b74034a67b1eb6334cd567334cf5856c1b91e65a38024955426079c317c43eee47557c7
|
@@ -15,20 +15,62 @@ module Graphql
|
|
15
15
|
desc "Create a GraphQL::ObjectType with the given name and fields"
|
16
16
|
source_root File.expand_path('../templates', __FILE__)
|
17
17
|
|
18
|
-
argument :
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
argument :custom_fields,
|
19
|
+
type: :array,
|
20
|
+
default: [],
|
21
|
+
banner: "name:type name:type ...",
|
22
|
+
desc: "Fields for this object (type may be expressed as Ruby or GraphQL)"
|
23
23
|
|
24
24
|
class_option :node,
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
type: :boolean,
|
26
|
+
default: false,
|
27
|
+
desc: "Include the Relay Node interface"
|
28
28
|
|
29
29
|
def create_type_file
|
30
30
|
template "object.erb", "#{options[:directory]}/types/#{type_file_name}.rb"
|
31
31
|
end
|
32
|
+
|
33
|
+
def fields
|
34
|
+
columns = []
|
35
|
+
columns += klass.columns.map { |c| generate_column_string(c) } if class_exists?
|
36
|
+
columns + custom_fields
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.normalize_type_expression(type_expression, mode:, null: true)
|
40
|
+
case type_expression
|
41
|
+
when "Text"
|
42
|
+
["String", null]
|
43
|
+
when "DateTime", "Datetime"
|
44
|
+
["GraphQL::Types::ISO8601DateTime", null]
|
45
|
+
when "Date"
|
46
|
+
["GraphQL::Types::ISO8601Date", null]
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def generate_column_string(column)
|
55
|
+
name = column.name
|
56
|
+
required = column.null ? "" : "!"
|
57
|
+
type = column_type_string(column)
|
58
|
+
"#{name}:#{required}#{type}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def column_type_string(column)
|
62
|
+
column.name == "id" ? "ID" : column.type.to_s.camelize
|
63
|
+
end
|
64
|
+
|
65
|
+
def class_exists?
|
66
|
+
klass.is_a?(Class) && klass.ancestors.include?(ActiveRecord::Base)
|
67
|
+
rescue NameError
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
|
71
|
+
def klass
|
72
|
+
@klass ||= Module.const_get(type_name.camelize)
|
73
|
+
end
|
32
74
|
end
|
33
75
|
end
|
34
76
|
end
|
data/lib/graphql.rb
CHANGED
@@ -89,12 +89,15 @@ require "graphql/name_validator"
|
|
89
89
|
require "graphql/language"
|
90
90
|
require "graphql/analysis"
|
91
91
|
require "graphql/tracing"
|
92
|
-
require "graphql/execution"
|
93
92
|
require "graphql/dig"
|
93
|
+
require "graphql/execution"
|
94
94
|
require "graphql/schema"
|
95
95
|
require "graphql/query"
|
96
96
|
require "graphql/directive"
|
97
97
|
require "graphql/execution"
|
98
|
+
require "graphql/runtime_type_error"
|
99
|
+
require "graphql/unresolved_type_error"
|
100
|
+
require "graphql/invalid_null_error"
|
98
101
|
require "graphql/types"
|
99
102
|
require "graphql/relay"
|
100
103
|
require "graphql/boolean_type"
|
@@ -109,10 +112,7 @@ require "graphql/introspection"
|
|
109
112
|
|
110
113
|
require "graphql/analysis_error"
|
111
114
|
require "graphql/coercion_error"
|
112
|
-
require "graphql/runtime_type_error"
|
113
|
-
require "graphql/invalid_null_error"
|
114
115
|
require "graphql/invalid_name_error"
|
115
|
-
require "graphql/unresolved_type_error"
|
116
116
|
require "graphql/integer_encoding_error"
|
117
117
|
require "graphql/string_encoding_error"
|
118
118
|
require "graphql/internal_representation"
|
@@ -54,7 +54,7 @@ module GraphQL
|
|
54
54
|
case defined_complexity
|
55
55
|
when Proc
|
56
56
|
arguments = @query.arguments_for(@node, @field_definition)
|
57
|
-
defined_complexity.call(@query.context, arguments, child_complexity)
|
57
|
+
defined_complexity.call(@query.context, arguments.keyword_arguments, child_complexity)
|
58
58
|
when Numeric
|
59
59
|
defined_complexity + child_complexity
|
60
60
|
else
|
@@ -77,7 +77,7 @@ module GraphQL
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
80
|
-
instrumenters.
|
80
|
+
instrumenters.reverse_each do |instrumenter|
|
81
81
|
begin
|
82
82
|
instrumenter.public_send(after_hook_name, object)
|
83
83
|
rescue => e
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "graphql/execution/interpreter/argument_value"
|
3
|
+
require "graphql/execution/interpreter/arguments"
|
2
4
|
require "graphql/execution/interpreter/arguments_cache"
|
3
5
|
require "graphql/execution/interpreter/execution_errors"
|
4
6
|
require "graphql/execution/interpreter/hash_response"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Execution
|
5
|
+
class Interpreter
|
6
|
+
# A container for metadata regarding arguments present in a GraphQL query.
|
7
|
+
# @see Interpreter::Arguments#argument_values for a hash of these objects.
|
8
|
+
class ArgumentValue
|
9
|
+
def initialize(definition:, value:, default_used:)
|
10
|
+
@definition = definition
|
11
|
+
@value = value
|
12
|
+
@default_used = default_used
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Object] The Ruby-ready value for this Argument
|
16
|
+
attr_reader :value
|
17
|
+
|
18
|
+
# @return [GraphQL::Schema::Argument] The definition instance for this argument
|
19
|
+
attr_reader :definition
|
20
|
+
|
21
|
+
# @return [Boolean] `true` if the schema-defined `default_value:` was applied in this case. (No client-provided value was present.)
|
22
|
+
def default_used?
|
23
|
+
@default_used
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Execution
|
5
|
+
class Interpreter
|
6
|
+
# A wrapper for argument hashes in GraphQL queries.
|
7
|
+
#
|
8
|
+
# @see GraphQL::Query#arguments_for to get access to these objects.
|
9
|
+
class Arguments
|
10
|
+
extend Forwardable
|
11
|
+
include GraphQL::Dig
|
12
|
+
|
13
|
+
# The Ruby-style arguments hash, ready for a resolver.
|
14
|
+
# This hash is the one used at runtime.
|
15
|
+
#
|
16
|
+
# @return [Hash<Symbol, Object>]
|
17
|
+
attr_reader :keyword_arguments
|
18
|
+
|
19
|
+
def initialize(keyword_arguments:, argument_values:)
|
20
|
+
@keyword_arguments = keyword_arguments
|
21
|
+
@argument_values = argument_values
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Hash{Symbol => ArgumentValue}]
|
25
|
+
attr_reader :argument_values
|
26
|
+
|
27
|
+
def_delegators :@keyword_arguments, :key?, :[], :keys, :each, :values
|
28
|
+
def_delegators :@argument_values, :each_value
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -14,13 +14,9 @@ module GraphQL
|
|
14
14
|
# Then call into the schema to coerce those incoming values
|
15
15
|
args = arg_owner.coerce_arguments(parent_object, args_hash, query.context)
|
16
16
|
|
17
|
-
h3[parent_object] =
|
18
|
-
|
19
|
-
|
20
|
-
h3[parent_object] = resolved_args
|
21
|
-
}
|
22
|
-
else
|
23
|
-
args
|
17
|
+
h3[parent_object] = @query.schema.after_lazy(args) do |resolved_args|
|
18
|
+
# when this promise is resolved, update the cache with the resolved value
|
19
|
+
h3[parent_object] = resolved_args
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
@@ -174,9 +174,14 @@ module GraphQL
|
|
174
174
|
next
|
175
175
|
end
|
176
176
|
|
177
|
-
after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |
|
178
|
-
|
179
|
-
|
177
|
+
after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |resolved_arguments|
|
178
|
+
if resolved_arguments.is_a? GraphQL::ExecutionError
|
179
|
+
continue_value(next_path, resolved_arguments, field_defn, return_type.non_null?, ast_node)
|
180
|
+
next
|
181
|
+
end
|
182
|
+
|
183
|
+
kwarg_arguments = resolved_arguments.keyword_arguments
|
184
|
+
|
180
185
|
field_defn.extras.each do |extra|
|
181
186
|
case extra
|
182
187
|
when :ast_node
|
@@ -194,6 +199,8 @@ module GraphQL
|
|
194
199
|
ast_nodes: field_ast_nodes,
|
195
200
|
field: field_defn,
|
196
201
|
)
|
202
|
+
when :argument_details
|
203
|
+
kwarg_arguments[:argument_details] = resolved_arguments
|
197
204
|
else
|
198
205
|
kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
|
199
206
|
end
|
@@ -247,7 +254,7 @@ module GraphQL
|
|
247
254
|
def continue_value(path, value, field, is_non_null, ast_node)
|
248
255
|
if value.nil?
|
249
256
|
if is_non_null
|
250
|
-
err =
|
257
|
+
err = field.owner::InvalidNullError.new(field.owner, field, value)
|
251
258
|
write_invalid_null_in_response(path, err)
|
252
259
|
else
|
253
260
|
write_in_response(path, nil)
|
@@ -297,18 +304,21 @@ module GraphQL
|
|
297
304
|
write_in_response(path, r)
|
298
305
|
r
|
299
306
|
when "UNION", "INTERFACE"
|
300
|
-
resolved_type_or_lazy = resolve_type(type, value, path)
|
307
|
+
resolved_type_or_lazy, resolved_value = resolve_type(type, value, path)
|
308
|
+
resolved_value ||= value
|
309
|
+
|
301
310
|
after_lazy(resolved_type_or_lazy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
302
311
|
possible_types = query.possible_types(type)
|
303
312
|
|
304
313
|
if !possible_types.include?(resolved_type)
|
305
314
|
parent_type = field.owner
|
306
|
-
|
315
|
+
err_class = type::UnresolvedTypeError
|
316
|
+
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
307
317
|
schema.type_error(type_error, context)
|
308
318
|
write_in_response(path, nil)
|
309
319
|
nil
|
310
320
|
else
|
311
|
-
continue_field(path,
|
321
|
+
continue_field(path, resolved_value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
|
312
322
|
end
|
313
323
|
end
|
314
324
|
when "OBJECT"
|
@@ -369,11 +379,12 @@ module GraphQL
|
|
369
379
|
end
|
370
380
|
end
|
371
381
|
|
372
|
-
def resolve_with_directives(object, ast_node)
|
373
|
-
|
382
|
+
def resolve_with_directives(object, ast_node, &block)
|
383
|
+
return yield if ast_node.directives.empty?
|
384
|
+
run_directive(object, ast_node, 0, &block)
|
374
385
|
end
|
375
386
|
|
376
|
-
def run_directive(object, ast_node, idx)
|
387
|
+
def run_directive(object, ast_node, idx, &block)
|
377
388
|
dir_node = ast_node.directives[idx]
|
378
389
|
if !dir_node
|
379
390
|
yield
|
@@ -382,9 +393,9 @@ module GraphQL
|
|
382
393
|
if !dir_defn.is_a?(Class)
|
383
394
|
dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
|
384
395
|
end
|
385
|
-
dir_args = arguments(nil, dir_defn, dir_node)
|
396
|
+
dir_args = arguments(nil, dir_defn, dir_node).keyword_arguments
|
386
397
|
dir_defn.resolve(object, dir_args, context) do
|
387
|
-
run_directive(object, ast_node, idx + 1)
|
398
|
+
run_directive(object, ast_node, idx + 1, &block)
|
388
399
|
end
|
389
400
|
end
|
390
401
|
end
|
@@ -393,7 +404,7 @@ module GraphQL
|
|
393
404
|
def directives_include?(node, graphql_object, parent_type)
|
394
405
|
node.directives.each do |dir_node|
|
395
406
|
dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
|
396
|
-
args = arguments(graphql_object, dir_defn, dir_node)
|
407
|
+
args = arguments(graphql_object, dir_defn, dir_node).keyword_arguments
|
397
408
|
if !dir_defn.include?(graphql_object, args, context)
|
398
409
|
return false
|
399
410
|
end
|
@@ -407,7 +418,7 @@ module GraphQL
|
|
407
418
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
408
419
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
409
420
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
410
|
-
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true)
|
421
|
+
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
|
411
422
|
@interpreter_context[:current_object] = owner_object
|
412
423
|
@interpreter_context[:current_arguments] = arguments
|
413
424
|
@interpreter_context[:current_path] = path
|
@@ -432,11 +443,9 @@ module GraphQL
|
|
432
443
|
end
|
433
444
|
end
|
434
445
|
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
435
|
-
|
436
|
-
end
|
437
|
-
after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager) do |really_inner_obj|
|
438
|
-
yield(really_inner_obj)
|
446
|
+
err
|
439
447
|
end
|
448
|
+
after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager, trace: trace, &block)
|
440
449
|
end
|
441
450
|
|
442
451
|
if eager
|
@@ -451,7 +460,13 @@ module GraphQL
|
|
451
460
|
end
|
452
461
|
|
453
462
|
def arguments(graphql_object, arg_owner, ast_node)
|
454
|
-
|
463
|
+
# Don't cache arguments if field extras are requested since extras mutate the argument data structure
|
464
|
+
if arg_owner.arguments_statically_coercible? && (!arg_owner.is_a?(GraphQL::Schema::Field) || arg_owner.extras.empty?)
|
465
|
+
query.arguments_for(ast_node, arg_owner)
|
466
|
+
else
|
467
|
+
# The arguments must be prepared in the context of the given object
|
468
|
+
query.arguments_for(ast_node, arg_owner, parent_object: graphql_object)
|
469
|
+
end
|
455
470
|
end
|
456
471
|
|
457
472
|
def write_invalid_null_in_response(path, invalid_null_error)
|
@@ -491,23 +506,11 @@ module GraphQL
|
|
491
506
|
# at previous parts of the response.
|
492
507
|
# This hash matches the response
|
493
508
|
def type_at(path)
|
494
|
-
|
495
|
-
path.each do |part|
|
496
|
-
t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
|
497
|
-
end
|
498
|
-
t = t[:__type]
|
499
|
-
t
|
509
|
+
@types_at_paths.fetch(path)
|
500
510
|
end
|
501
511
|
|
502
512
|
def set_type_at_path(path, type)
|
503
|
-
|
504
|
-
path.each do |part|
|
505
|
-
types = types[part] ||= {}
|
506
|
-
end
|
507
|
-
# Use this magic key so that the hash contains:
|
508
|
-
# - string keys for nested fields
|
509
|
-
# - :__type for the object type of a selection
|
510
|
-
types[:__type] ||= type
|
513
|
+
@types_at_paths[path] = type
|
511
514
|
nil
|
512
515
|
end
|
513
516
|
|
@@ -537,7 +540,7 @@ module GraphQL
|
|
537
540
|
|
538
541
|
def resolve_type(type, value, path)
|
539
542
|
trace_payload = { context: context, type: type, object: value, path: path }
|
540
|
-
resolved_type = query.trace("resolve_type", trace_payload) do
|
543
|
+
resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
|
541
544
|
query.resolve_type(type, value)
|
542
545
|
end
|
543
546
|
|
@@ -548,7 +551,7 @@ module GraphQL
|
|
548
551
|
end
|
549
552
|
end
|
550
553
|
else
|
551
|
-
resolved_type
|
554
|
+
[resolved_type, resolved_value]
|
552
555
|
end
|
553
556
|
end
|
554
557
|
|
@@ -55,7 +55,9 @@ module GraphQL
|
|
55
55
|
@arguments
|
56
56
|
else
|
57
57
|
@arguments = if @field
|
58
|
-
@query.arguments_for(@ast_nodes.first, @field)
|
58
|
+
@query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
|
59
|
+
args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
|
60
|
+
end
|
59
61
|
else
|
60
62
|
nil
|
61
63
|
end
|
@@ -66,11 +66,11 @@ module GraphQL
|
|
66
66
|
# Call the block for each type in `self`.
|
67
67
|
# This uses the simplest possible expression of `self`,
|
68
68
|
# so if this scope is defined by an abstract type, it gets yielded.
|
69
|
-
def each
|
69
|
+
def each(&block)
|
70
70
|
if @abstract_type
|
71
71
|
yield(@type)
|
72
72
|
else
|
73
|
-
@types.each
|
73
|
+
@types.each(&block)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -23,11 +23,11 @@ module GraphQL
|
|
23
23
|
|
24
24
|
# Traverse a node in a rewritten query tree,
|
25
25
|
# visiting the node itself and each of its typed children.
|
26
|
-
def each_node(node)
|
26
|
+
def each_node(node, &block)
|
27
27
|
yield(node)
|
28
28
|
node.typed_children.each do |obj_type, children|
|
29
29
|
children.each do |name, node|
|
30
|
-
each_node(node
|
30
|
+
each_node(node, &block)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|