graphql 1.12.6 → 1.12.11
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/install_generator.rb +1 -1
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -2
- data/lib/graphql.rb +10 -10
- data/lib/graphql/backtrace/table.rb +14 -2
- data/lib/graphql/dataloader.rb +44 -15
- data/lib/graphql/execution/errors.rb +109 -11
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/interpreter.rb +4 -8
- data/lib/graphql/execution/interpreter/runtime.rb +207 -188
- data/lib/graphql/introspection.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/pagination/active_record_relation_connection.rb +7 -0
- data/lib/graphql/pagination/connections.rb +1 -1
- data/lib/graphql/pagination/relation_connection.rb +8 -1
- data/lib/graphql/query.rb +1 -3
- data/lib/graphql/query/null_context.rb +7 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/rake_task.rb +3 -0
- data/lib/graphql/schema.rb +49 -237
- data/lib/graphql/schema/addition.rb +238 -0
- data/lib/graphql/schema/argument.rb +55 -36
- data/lib/graphql/schema/directive/transform.rb +13 -1
- data/lib/graphql/schema/input_object.rb +2 -2
- data/lib/graphql/schema/loader.rb +8 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +3 -15
- data/lib/graphql/schema/object.rb +19 -5
- data/lib/graphql/schema/resolver.rb +46 -24
- data/lib/graphql/schema/scalar.rb +3 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +3 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +6 -2
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +17 -8
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +5 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +0 -3
- data/lib/graphql/subscriptions/serialize.rb +11 -1
- data/lib/graphql/types/relay/base_connection.rb +4 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +21 -10
- data/lib/graphql/types/relay/edge_behaviors.rb +12 -1
- data/lib/graphql/version.rb +1 -1
- metadata +3 -3
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
@@ -333,30 +333,32 @@ module GraphQL
|
|
333
333
|
arg_defn = super(*args, from_resolver: true, **kwargs)
|
334
334
|
own_arguments_loads_as_type[arg_defn.keyword] = loads if loads
|
335
335
|
|
336
|
-
if
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
336
|
+
if !method_defined?(:"load_#{arg_defn.keyword}")
|
337
|
+
if loads && arg_defn.type.list?
|
338
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
339
|
+
def load_#{arg_defn.keyword}(values)
|
340
|
+
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
341
|
+
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
342
|
+
context.schema.after_lazy(values) do |values2|
|
343
|
+
GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value, context) })
|
344
|
+
end
|
343
345
|
end
|
346
|
+
RUBY
|
347
|
+
elsif loads
|
348
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
349
|
+
def load_#{arg_defn.keyword}(value)
|
350
|
+
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
351
|
+
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
352
|
+
load_application_object(argument, lookup_as_type, value, context)
|
353
|
+
end
|
354
|
+
RUBY
|
355
|
+
else
|
356
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
357
|
+
def load_#{arg_defn.keyword}(value)
|
358
|
+
value
|
359
|
+
end
|
360
|
+
RUBY
|
344
361
|
end
|
345
|
-
RUBY
|
346
|
-
elsif loads
|
347
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
348
|
-
def load_#{arg_defn.keyword}(value)
|
349
|
-
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
350
|
-
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
351
|
-
load_application_object(argument, lookup_as_type, value, context)
|
352
|
-
end
|
353
|
-
RUBY
|
354
|
-
else
|
355
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
356
|
-
def load_#{arg_defn.keyword}(value)
|
357
|
-
value
|
358
|
-
end
|
359
|
-
RUBY
|
360
362
|
end
|
361
363
|
|
362
364
|
arg_defn
|
@@ -372,16 +374,36 @@ module GraphQL
|
|
372
374
|
# @param extension [Class] Extension class
|
373
375
|
# @param options [Hash] Optional extension options
|
374
376
|
def extension(extension, **options)
|
375
|
-
|
377
|
+
@own_extensions ||= []
|
378
|
+
@own_extensions << {extension => options}
|
376
379
|
end
|
377
380
|
|
378
381
|
# @api private
|
379
382
|
def extensions
|
380
|
-
|
383
|
+
own_exts = @own_extensions
|
384
|
+
# Jump through some hoops to avoid creating arrays when we don't actually need them
|
385
|
+
if superclass.respond_to?(:extensions)
|
386
|
+
s_exts = superclass.extensions
|
387
|
+
if own_exts
|
388
|
+
if s_exts.any?
|
389
|
+
own_exts + s_exts
|
390
|
+
else
|
391
|
+
own_exts
|
392
|
+
end
|
393
|
+
else
|
394
|
+
s_exts
|
395
|
+
end
|
396
|
+
else
|
397
|
+
own_exts || EMPTY_ARRAY
|
398
|
+
end
|
381
399
|
end
|
382
400
|
|
383
401
|
private
|
384
402
|
|
403
|
+
def own_extensions
|
404
|
+
@own_extensions
|
405
|
+
end
|
406
|
+
|
385
407
|
def own_arguments_loads_as_type
|
386
408
|
@own_arguments_loads_as_type ||= {}
|
387
409
|
end
|
@@ -44,7 +44,9 @@ module GraphQL
|
|
44
44
|
def validate_non_null_input(value, ctx)
|
45
45
|
result = Query::InputValidationResult.new
|
46
46
|
coerced_result = begin
|
47
|
-
|
47
|
+
ctx.query.with_error_handling do
|
48
|
+
coerce_input(value, ctx)
|
49
|
+
end
|
48
50
|
rescue GraphQL::CoercionError => err
|
49
51
|
err
|
50
52
|
end
|
@@ -41,7 +41,9 @@ module GraphQL
|
|
41
41
|
error_options = {
|
42
42
|
nodes: parent,
|
43
43
|
type: kind_of_node,
|
44
|
-
|
44
|
+
argument_name: node.name,
|
45
|
+
argument: arg_defn,
|
46
|
+
value: node.value
|
45
47
|
}
|
46
48
|
if coerce_extensions
|
47
49
|
error_options[:coerce_extensions] = coerce_extensions
|
@@ -4,13 +4,17 @@ module GraphQL
|
|
4
4
|
class ArgumentLiteralsAreCompatibleError < StaticValidation::Error
|
5
5
|
attr_reader :type_name
|
6
6
|
attr_reader :argument_name
|
7
|
+
attr_reader :argument
|
8
|
+
attr_reader :value
|
7
9
|
|
8
|
-
def initialize(message, path: nil, nodes: [], type:,
|
10
|
+
def initialize(message, path: nil, nodes: [], type:, argument_name: nil, extensions: nil, coerce_extensions: nil, argument: nil, value: nil)
|
9
11
|
super(message, path: path, nodes: nodes)
|
10
12
|
@type_name = type
|
11
|
-
@argument_name =
|
13
|
+
@argument_name = argument_name
|
12
14
|
@extensions = extensions
|
13
15
|
@coerce_extensions = coerce_extensions
|
16
|
+
@argument = argument
|
17
|
+
@value = value
|
14
18
|
end
|
15
19
|
|
16
20
|
# A hash representation of this Message
|
@@ -5,12 +5,14 @@ module GraphQL
|
|
5
5
|
attr_reader :name
|
6
6
|
attr_reader :type_name
|
7
7
|
attr_reader :argument_name
|
8
|
+
attr_reader :parent
|
8
9
|
|
9
|
-
def initialize(message, path: nil, nodes: [], name:, type:,
|
10
|
+
def initialize(message, path: nil, nodes: [], name:, type:, argument_name:, parent:)
|
10
11
|
super(message, path: path, nodes: nodes)
|
11
12
|
@name = name
|
12
13
|
@type_name = type
|
13
|
-
@argument_name =
|
14
|
+
@argument_name = argument_name
|
15
|
+
@parent = parent
|
14
16
|
end
|
15
17
|
|
16
18
|
# A hash representation of this Message
|
@@ -373,17 +373,26 @@ module GraphQL
|
|
373
373
|
# In this context, `parents` represends the "self scope" of the field,
|
374
374
|
# what types may be found at this point in the query.
|
375
375
|
def mutually_exclusive?(parents1, parents2)
|
376
|
-
parents1.
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
376
|
+
if parents1.empty? || parents2.empty?
|
377
|
+
false
|
378
|
+
elsif parents1.length == parents2.length
|
379
|
+
parents1.length.times.any? do |i|
|
380
|
+
type1 = parents1[i - 1]
|
381
|
+
type2 = parents2[i - 1]
|
382
|
+
if type1 == type2
|
383
|
+
# If the types we're comparing are the same type,
|
384
|
+
# then they aren't mutually exclusive
|
385
|
+
false
|
386
|
+
else
|
387
|
+
# Check if these two scopes have _any_ types in common.
|
388
|
+
possible_right_types = context.query.possible_types(type1)
|
389
|
+
possible_left_types = context.query.possible_types(type2)
|
390
|
+
(possible_right_types & possible_left_types).empty?
|
382
391
|
end
|
383
392
|
end
|
393
|
+
else
|
394
|
+
true
|
384
395
|
end
|
385
|
-
|
386
|
-
false
|
387
396
|
end
|
388
397
|
end
|
389
398
|
end
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
23
23
|
defn = if arg_defn && arg_defn.type.unwrap.kind.input_object?
|
24
24
|
arg_defn.type.unwrap
|
25
25
|
else
|
26
|
-
context.field_definition
|
26
|
+
context.directive_definition || context.field_definition
|
27
27
|
end
|
28
28
|
|
29
29
|
parent_type = context.warden.get_argument(defn, parent_name(parent, defn))
|
@@ -34,12 +34,12 @@ module GraphQL
|
|
34
34
|
# channel: self,
|
35
35
|
# }
|
36
36
|
#
|
37
|
-
# result = MySchema.execute(
|
37
|
+
# result = MySchema.execute(
|
38
38
|
# query: query,
|
39
39
|
# context: context,
|
40
40
|
# variables: variables,
|
41
41
|
# operation_name: operation_name
|
42
|
-
#
|
42
|
+
# )
|
43
43
|
#
|
44
44
|
# payload = {
|
45
45
|
# result: result.to_h,
|
@@ -146,14 +146,15 @@ module GraphQL
|
|
146
146
|
def setup_stream(channel, initial_event)
|
147
147
|
topic = initial_event.topic
|
148
148
|
channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
|
149
|
-
object = @serializer.load(message)
|
150
149
|
events_by_fingerprint = @events[topic]
|
150
|
+
object = nil
|
151
151
|
events_by_fingerprint.each do |_fingerprint, events|
|
152
152
|
if events.any? && events.first == initial_event
|
153
153
|
# The fingerprint has told us that this response should be shared by all subscribers,
|
154
154
|
# so just run it once, then deliver the result to every subscriber
|
155
155
|
first_event = events.first
|
156
156
|
first_subscription_id = first_event.context.fetch(:subscription_id)
|
157
|
+
object ||= @serializer.load(message)
|
157
158
|
result = execute_update(first_subscription_id, first_event, object)
|
158
159
|
# Having calculated the result _once_, send the same payload to all subscribers
|
159
160
|
events.each do |event|
|
@@ -35,9 +35,6 @@ module GraphQL
|
|
35
35
|
pt = @query.possible_types(current_type)
|
36
36
|
pt.each do |object_type|
|
37
37
|
ot_field = @query.get_field(object_type, current_field.graphql_name)
|
38
|
-
if !ot_field
|
39
|
-
binding.pry
|
40
|
-
end
|
41
38
|
# Inherited fields would be exactly the same object;
|
42
39
|
# only check fields that are overrides of the inherited one
|
43
40
|
if ot_field && ot_field != current_field
|
@@ -55,7 +55,14 @@ module GraphQL
|
|
55
55
|
# @return [Object] An object that load Global::Identification recursive
|
56
56
|
def load_value(value)
|
57
57
|
if value.is_a?(Array)
|
58
|
-
value.
|
58
|
+
is_gids = (v1 = value[0]).is_a?(Hash) && v1.size == 1 && v1[GLOBALID_KEY]
|
59
|
+
if is_gids
|
60
|
+
# Assume it's an array of global IDs
|
61
|
+
ids = value.map { |v| v[GLOBALID_KEY] }
|
62
|
+
GlobalID::Locator.locate_many(ids)
|
63
|
+
else
|
64
|
+
value.map { |item| load_value(item) }
|
65
|
+
end
|
59
66
|
elsif value.is_a?(Hash)
|
60
67
|
if value.size == 1
|
61
68
|
case value.keys.first # there's only 1 key
|
@@ -70,6 +77,9 @@ module GraphQL
|
|
70
77
|
when OPEN_STRUCT_KEY
|
71
78
|
ostruct_values = load_value(value[OPEN_STRUCT_KEY])
|
72
79
|
OpenStruct.new(ostruct_values)
|
80
|
+
else
|
81
|
+
key = value.keys.first
|
82
|
+
{ key => load_value(value[key]) }
|
73
83
|
end
|
74
84
|
else
|
75
85
|
loaded_h = {}
|
@@ -24,6 +24,10 @@ module GraphQL
|
|
24
24
|
# end
|
25
25
|
# class Types::PostConnection < Types::BaseConnection
|
26
26
|
# edge_type(Types::PostEdge)
|
27
|
+
# edges_nullable(true)
|
28
|
+
# edge_nullable(true)
|
29
|
+
# node_nullable(true)
|
30
|
+
# has_nodes_field(true)
|
27
31
|
# end
|
28
32
|
#
|
29
33
|
# @see Relay::BaseEdge for edge types
|
@@ -11,6 +11,7 @@ module GraphQL
|
|
11
11
|
child_class.extend(ClassMethods)
|
12
12
|
child_class.extend(Relay::DefaultRelay)
|
13
13
|
child_class.default_relay(true)
|
14
|
+
child_class.has_nodes_field(true)
|
14
15
|
child_class.node_nullable(true)
|
15
16
|
child_class.edges_nullable(true)
|
16
17
|
child_class.edge_nullable(true)
|
@@ -34,7 +35,7 @@ module GraphQL
|
|
34
35
|
# It's called when you subclass this base connection, trying to use the
|
35
36
|
# class name to set defaults. You can call it again in the class definition
|
36
37
|
# to override the default (or provide a value, if the default lookup failed).
|
37
|
-
def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field:
|
38
|
+
def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: self.has_nodes_field, node_nullable: self.node_nullable, edges_nullable: self.edges_nullable, edge_nullable: self.edge_nullable)
|
38
39
|
# Set this connection's graphql name
|
39
40
|
node_type_name = node_type.graphql_name
|
40
41
|
|
@@ -79,9 +80,9 @@ module GraphQL
|
|
79
80
|
# Use `node_nullable(false)` in your base class to make non-null `node` and `nodes` fields.
|
80
81
|
def node_nullable(new_value = nil)
|
81
82
|
if new_value.nil?
|
82
|
-
@node_nullable
|
83
|
+
defined?(@node_nullable) ? @node_nullable : superclass.node_nullable
|
83
84
|
else
|
84
|
-
@node_nullable
|
85
|
+
@node_nullable = new_value
|
85
86
|
end
|
86
87
|
end
|
87
88
|
|
@@ -89,21 +90,31 @@ module GraphQL
|
|
89
90
|
# Use `edges_nullable(false)` in your base class to make non-null `edges` fields.
|
90
91
|
def edges_nullable(new_value = nil)
|
91
92
|
if new_value.nil?
|
92
|
-
@edges_nullable
|
93
|
+
defined?(@edges_nullable) ? @edges_nullable : superclass.edges_nullable
|
93
94
|
else
|
94
|
-
@edges_nullable
|
95
|
+
@edges_nullable = new_value
|
95
96
|
end
|
96
|
-
end
|
97
|
-
|
97
|
+
end
|
98
|
+
|
98
99
|
# Set the default `edge_nullable` for this class and its child classes. (Defaults to `true`.)
|
99
100
|
# Use `edge_nullable(false)` in your base class to make non-null `edge` fields.
|
100
101
|
def edge_nullable(new_value = nil)
|
101
102
|
if new_value.nil?
|
102
|
-
@edge_nullable
|
103
|
+
defined?(@edge_nullable) ? @edge_nullable : superclass.edge_nullable
|
104
|
+
else
|
105
|
+
@edge_nullable = new_value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Set the default `nodes_field` for this class and its child classes. (Defaults to `true`.)
|
110
|
+
# Use `nodes_field(false)` in your base class to prevent adding of a nodes field.
|
111
|
+
def has_nodes_field(new_value = nil)
|
112
|
+
if new_value.nil?
|
113
|
+
defined?(@nodes_field) ? @nodes_field : superclass.has_nodes_field
|
103
114
|
else
|
104
|
-
@
|
115
|
+
@nodes_field = new_value
|
105
116
|
end
|
106
|
-
end
|
117
|
+
end
|
107
118
|
|
108
119
|
private
|
109
120
|
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
child_class.description("An edge in a connection.")
|
9
9
|
child_class.field(:cursor, String, null: false, description: "A cursor for use in pagination.")
|
10
10
|
child_class.extend(ClassMethods)
|
11
|
+
child_class.node_nullable(true)
|
11
12
|
end
|
12
13
|
|
13
14
|
module ClassMethods
|
@@ -15,7 +16,7 @@ module GraphQL
|
|
15
16
|
#
|
16
17
|
# @param node_type [Class] A `Schema::Object` subclass
|
17
18
|
# @param null [Boolean]
|
18
|
-
def node_type(node_type = nil, null:
|
19
|
+
def node_type(node_type = nil, null: self.node_nullable)
|
19
20
|
if node_type
|
20
21
|
@node_type = node_type
|
21
22
|
# Add a default `node` field
|
@@ -35,6 +36,16 @@ module GraphQL
|
|
35
36
|
def visible?(ctx)
|
36
37
|
node_type.visible?(ctx)
|
37
38
|
end
|
39
|
+
|
40
|
+
# Set the default `node_nullable` for this class and its child classes. (Defaults to `true`.)
|
41
|
+
# Use `node_nullable(false)` in your base class to make non-null `node` field.
|
42
|
+
def node_nullable(new_value = nil)
|
43
|
+
if new_value.nil?
|
44
|
+
defined?(@node_nullable) ? @node_nullable : superclass.node_nullable
|
45
|
+
else
|
46
|
+
@node_nullable = new_value
|
47
|
+
end
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.12.
|
4
|
+
version: 1.12.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -369,7 +369,6 @@ files:
|
|
369
369
|
- lib/graphql/execution/interpreter/arguments_cache.rb
|
370
370
|
- lib/graphql/execution/interpreter/execution_errors.rb
|
371
371
|
- lib/graphql/execution/interpreter/handles_raw_value.rb
|
372
|
-
- lib/graphql/execution/interpreter/hash_response.rb
|
373
372
|
- lib/graphql/execution/interpreter/resolve.rb
|
374
373
|
- lib/graphql/execution/interpreter/runtime.rb
|
375
374
|
- lib/graphql/execution/lazy.rb
|
@@ -485,6 +484,7 @@ files:
|
|
485
484
|
- lib/graphql/runtime_type_error.rb
|
486
485
|
- lib/graphql/scalar_type.rb
|
487
486
|
- lib/graphql/schema.rb
|
487
|
+
- lib/graphql/schema/addition.rb
|
488
488
|
- lib/graphql/schema/argument.rb
|
489
489
|
- lib/graphql/schema/base_64_bp.rb
|
490
490
|
- lib/graphql/schema/base_64_encoder.rb
|