graphql 1.10.5 → 1.10.6
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 +27 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +1 -1
- data/lib/graphql/execution/interpreter.rb +13 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +10 -1
- data/lib/graphql/execution/interpreter/runtime.rb +72 -64
- data/lib/graphql/interface_type.rb +5 -0
- data/lib/graphql/language/nodes.rb +4 -4
- data/lib/graphql/object_type.rb +44 -35
- data/lib/graphql/pagination/connection.rb +24 -4
- data/lib/graphql/pagination/relation_connection.rb +19 -11
- data/lib/graphql/query.rb +28 -1
- data/lib/graphql/query/arguments.rb +2 -1
- data/lib/graphql/query/fingerprint.rb +24 -0
- data/lib/graphql/rake_task.rb +9 -9
- data/lib/graphql/schema.rb +90 -92
- data/lib/graphql/schema/field.rb +33 -13
- data/lib/graphql/schema/field/connection_extension.rb +3 -1
- data/lib/graphql/schema/input_object.rb +14 -2
- data/lib/graphql/schema/interface.rb +10 -0
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +21 -9
- data/lib/graphql/schema/object.rb +53 -17
- data/lib/graphql/schema/possible_types.rb +9 -4
- data/lib/graphql/schema/warden.rb +48 -7
- data/lib/graphql/types/big_int.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +3 -2
@@ -35,7 +35,9 @@ module GraphQL
|
|
35
35
|
value.after_value ||= arguments[:after]
|
36
36
|
value.last_value ||= arguments[:last]
|
37
37
|
value.before_value ||= arguments[:before]
|
38
|
-
|
38
|
+
if field.has_max_page_size? && !value.has_max_page_size_override?
|
39
|
+
value.max_page_size = field.max_page_size
|
40
|
+
end
|
39
41
|
value
|
40
42
|
elsif context.schema.new_connections?
|
41
43
|
wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
@@ -21,6 +21,7 @@ module GraphQL
|
|
21
21
|
end
|
22
22
|
# Apply prepares, not great to have it duplicated here.
|
23
23
|
@arguments_by_keyword = {}
|
24
|
+
maybe_lazies = []
|
24
25
|
self.class.arguments.each do |name, arg_defn|
|
25
26
|
@arguments_by_keyword[arg_defn.keyword] = arg_defn
|
26
27
|
ruby_kwargs_key = arg_defn.keyword
|
@@ -31,11 +32,14 @@ module GraphQL
|
|
31
32
|
# With the interpreter, it's done during `coerce_arguments`
|
32
33
|
if loads && !arg_defn.from_resolver? && !context.interpreter?
|
33
34
|
value = @ruby_style_hash[ruby_kwargs_key]
|
34
|
-
|
35
|
+
loaded_value = if arg_defn.type.list?
|
35
36
|
value.map { |val| load_application_object(arg_defn, loads, val, context) }
|
36
37
|
else
|
37
38
|
load_application_object(arg_defn, loads, value, context)
|
38
39
|
end
|
40
|
+
maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
|
41
|
+
@ruby_style_hash[ruby_kwargs_key] = loaded_value
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
45
|
# Weirdly, procs are applied during coercion, but not methods.
|
@@ -45,6 +49,8 @@ module GraphQL
|
|
45
49
|
end
|
46
50
|
end
|
47
51
|
end
|
52
|
+
|
53
|
+
@maybe_lazies = maybe_lazies
|
48
54
|
end
|
49
55
|
|
50
56
|
# @return [GraphQL::Query::Context] The context for this query
|
@@ -67,7 +73,13 @@ module GraphQL
|
|
67
73
|
end
|
68
74
|
|
69
75
|
def prepare
|
70
|
-
|
76
|
+
if context
|
77
|
+
context.schema.after_any_lazies(@maybe_lazies) do
|
78
|
+
self
|
79
|
+
end
|
80
|
+
else
|
81
|
+
self
|
82
|
+
end
|
71
83
|
end
|
72
84
|
|
73
85
|
def unwrap_value(value)
|
@@ -37,6 +37,14 @@ module GraphQL
|
|
37
37
|
false
|
38
38
|
end
|
39
39
|
|
40
|
+
def type_membership_class(membership_class = nil)
|
41
|
+
if membership_class
|
42
|
+
@type_membership_class = membership_class
|
43
|
+
else
|
44
|
+
@type_membership_class || find_inherited_value(:type_membership_class, GraphQL::Schema::TypeMembership)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
40
48
|
# Here's the tricky part. Make sure behavior keeps making its way down the inheritance chain.
|
41
49
|
def included(child_class)
|
42
50
|
if !child_class.is_a?(Class)
|
@@ -46,6 +54,7 @@ module GraphQL
|
|
46
54
|
# We need this before we can call `own_interfaces`
|
47
55
|
child_class.extend(Schema::Interface::DefinitionMethods)
|
48
56
|
|
57
|
+
child_class.type_membership_class(self.type_membership_class)
|
49
58
|
child_class.own_interfaces << self
|
50
59
|
child_class.interfaces.reverse_each do |interface_defn|
|
51
60
|
child_class.extend(interface_defn::DefinitionMethods)
|
@@ -91,6 +100,7 @@ module GraphQL
|
|
91
100
|
type_defn.name = graphql_name
|
92
101
|
type_defn.description = description
|
93
102
|
type_defn.orphan_types = orphan_types
|
103
|
+
type_defn.type_membership_class = self.type_membership_class
|
94
104
|
type_defn.ast_node = ast_node
|
95
105
|
fields.each do |field_name, field_inst|
|
96
106
|
field_defn = field_inst.graphql_definition
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
schema.fetch("types").each do |type|
|
22
22
|
next if type.fetch("name").start_with?("__")
|
23
23
|
type_object = define_type(type, type_resolver)
|
24
|
-
types[
|
24
|
+
types[type["name"]] = type_object
|
25
25
|
end
|
26
26
|
|
27
27
|
kargs = { orphan_types: types.values, resolve_type: NullResolveType }
|
@@ -71,7 +71,8 @@ module GraphQL
|
|
71
71
|
# Cache this hash to avoid re-merging it
|
72
72
|
arg_defns = self.arguments
|
73
73
|
|
74
|
-
|
74
|
+
maybe_lazies = []
|
75
|
+
arg_lazies = arg_defns.map do |arg_name, arg_defn|
|
75
76
|
arg_key = arg_defn.keyword
|
76
77
|
has_value = false
|
77
78
|
if values.key?(arg_name)
|
@@ -96,20 +97,28 @@ module GraphQL
|
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
99
|
-
|
100
|
+
context.schema.after_lazy(loaded_value) do |loaded_value|
|
101
|
+
coerced_value = nil
|
102
|
+
prepared_value = context.schema.error_handler.with_error_handling(context) do
|
100
103
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
104
|
+
coerced_value = if loaded_value
|
105
|
+
loaded_value
|
106
|
+
else
|
107
|
+
arg_defn.type.coerce_input(value, context)
|
108
|
+
end
|
109
|
+
|
110
|
+
arg_defn.prepare_value(parent_object, coerced_value, context: context)
|
105
111
|
end
|
106
112
|
|
107
|
-
arg_defn.
|
113
|
+
kwarg_arguments[arg_defn.keyword] = prepared_value
|
108
114
|
end
|
109
|
-
kwarg_arguments[arg_defn.keyword] = prepared_value
|
110
115
|
end
|
111
116
|
end
|
112
|
-
|
117
|
+
|
118
|
+
maybe_lazies.concat(arg_lazies)
|
119
|
+
context.schema.after_any_lazies(maybe_lazies) do
|
120
|
+
kwarg_arguments
|
121
|
+
end
|
113
122
|
end
|
114
123
|
|
115
124
|
module ArgumentClassAccessor
|
@@ -138,6 +147,9 @@ module GraphQL
|
|
138
147
|
|
139
148
|
def load_application_object(argument, lookup_as_type, id, context)
|
140
149
|
# See if any object can be found for this ID
|
150
|
+
if id.nil?
|
151
|
+
return nil
|
152
|
+
end
|
141
153
|
loaded_application_object = object_from_id(lookup_as_type, id, context)
|
142
154
|
context.schema.after_lazy(loaded_application_object) do |application_object|
|
143
155
|
if application_object.nil?
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "graphql/query/null_context"
|
4
|
+
|
3
5
|
module GraphQL
|
4
6
|
class Schema
|
5
7
|
class Object < GraphQL::Schema::Member
|
@@ -69,47 +71,81 @@ module GraphQL
|
|
69
71
|
end
|
70
72
|
|
71
73
|
class << self
|
72
|
-
def implements(*new_interfaces)
|
74
|
+
def implements(*new_interfaces, **options)
|
75
|
+
new_memberships = []
|
73
76
|
new_interfaces.each do |int|
|
74
77
|
if int.is_a?(Module)
|
75
78
|
unless int.include?(GraphQL::Schema::Interface)
|
76
79
|
raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
77
80
|
end
|
78
81
|
|
82
|
+
new_memberships << int.type_membership_class.new(int, self, options)
|
83
|
+
|
79
84
|
# Include the methods here,
|
80
85
|
# `.fields` will use the inheritance chain
|
81
86
|
# to find inherited fields
|
82
87
|
include(int)
|
88
|
+
elsif int.is_a?(GraphQL::InterfaceType)
|
89
|
+
new_memberships << int.type_membership_class.new(int, self, options)
|
90
|
+
elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
|
91
|
+
if options.any?
|
92
|
+
raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
|
93
|
+
end
|
94
|
+
new_memberships << int
|
95
|
+
else
|
96
|
+
raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})"
|
83
97
|
end
|
84
98
|
end
|
99
|
+
|
85
100
|
# Remove any interfaces which are being replaced (late-bound types are updated in place this way)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
101
|
+
own_interface_type_memberships.reject! { |old_i_m|
|
102
|
+
old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m
|
103
|
+
old_name = Schema::Member::BuildType.to_type_name(old_int_type)
|
104
|
+
|
105
|
+
new_memberships.any? { |new_i_m|
|
106
|
+
new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m
|
107
|
+
new_name = Schema::Member::BuildType.to_type_name(new_int_type)
|
108
|
+
|
90
109
|
new_name == old_name
|
91
110
|
}
|
92
111
|
}
|
93
|
-
|
112
|
+
own_interface_type_memberships.concat(new_memberships)
|
94
113
|
end
|
95
114
|
|
96
|
-
def
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
115
|
+
def own_interface_type_memberships
|
116
|
+
@own_interface_type_memberships ||= []
|
117
|
+
end
|
118
|
+
|
119
|
+
def interface_type_memberships
|
120
|
+
own_interface_type_memberships + (superclass.respond_to?(:interface_type_memberships) ? superclass.interface_type_memberships : [])
|
103
121
|
end
|
104
122
|
|
105
|
-
|
106
|
-
|
123
|
+
# param context [Query::Context] If omitted, skip filtering.
|
124
|
+
def interfaces(context = GraphQL::Query::NullContext)
|
125
|
+
visible_interfaces = []
|
126
|
+
unfiltered = context == GraphQL::Query::NullContext
|
127
|
+
own_interface_type_memberships.each do |type_membership|
|
128
|
+
# During initialization, `type_memberships` can hold late-bound types
|
129
|
+
case type_membership
|
130
|
+
when String, Schema::LateBoundType
|
131
|
+
visible_interfaces << type_membership
|
132
|
+
when Schema::TypeMembership
|
133
|
+
if unfiltered || type_membership.visible?(context)
|
134
|
+
visible_interfaces << type_membership.abstract_type
|
135
|
+
end
|
136
|
+
else
|
137
|
+
raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
visible_interfaces + (superclass <= GraphQL::Schema::Object ? superclass.interfaces(context) : [])
|
107
141
|
end
|
108
142
|
|
109
|
-
#
|
143
|
+
# @return [Hash<String => GraphQL::Schema::Field>] All of this object's fields, indexed by name
|
144
|
+
# @see get_field A faster way to find one field by name ({#fields} merges hashes of inherited fields; {#get_field} just looks up one field.)
|
110
145
|
def fields
|
111
146
|
all_fields = super
|
112
147
|
interfaces.each do |int|
|
148
|
+
# Include legacy-style interfaces, too
|
113
149
|
if int.is_a?(GraphQL::InterfaceType)
|
114
150
|
int_f = {}
|
115
151
|
int.fields.each do |name, legacy_field|
|
@@ -126,7 +162,7 @@ module GraphQL
|
|
126
162
|
obj_type = GraphQL::ObjectType.new
|
127
163
|
obj_type.name = graphql_name
|
128
164
|
obj_type.description = description
|
129
|
-
obj_type.
|
165
|
+
obj_type.structural_interface_type_memberships = own_interface_type_memberships
|
130
166
|
obj_type.introspection = introspection
|
131
167
|
obj_type.mutation = mutation
|
132
168
|
obj_type.ast_node = ast_node
|
@@ -14,9 +14,10 @@ module GraphQL
|
|
14
14
|
class PossibleTypes
|
15
15
|
def initialize(schema)
|
16
16
|
@object_types = schema.types.values.select { |type| type.kind.object? }
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
@interface_implementers = Hash.new do |h1, ctx|
|
18
|
+
h1[ctx] = Hash.new do |h2, int_type|
|
19
|
+
h2[int_type] = @object_types.select { |type| type.interfaces(ctx).include?(int_type) }.sort_by(&:name)
|
20
|
+
end
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
@@ -27,13 +28,17 @@ module GraphQL
|
|
27
28
|
when GraphQL::UnionType
|
28
29
|
type_defn.possible_types(ctx)
|
29
30
|
when GraphQL::InterfaceType
|
30
|
-
|
31
|
+
interface_implementers(ctx, type_defn)
|
31
32
|
when GraphQL::BaseType
|
32
33
|
[type_defn]
|
33
34
|
else
|
34
35
|
raise "Unexpected possible_types object: #{type_defn.inspect}"
|
35
36
|
end
|
36
37
|
end
|
38
|
+
|
39
|
+
def interface_implementers(ctx, type_defn)
|
40
|
+
@interface_implementers[ctx][type_defn]
|
41
|
+
end
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
95
95
|
@visible_parent_fields ||= read_through do |type|
|
96
96
|
read_through do |f_name|
|
97
97
|
field_defn = @schema.get_field(type, f_name)
|
98
|
-
if field_defn && visible_field?(field_defn)
|
98
|
+
if field_defn && visible_field?(type, field_defn)
|
99
99
|
field_defn
|
100
100
|
else
|
101
101
|
nil
|
@@ -109,7 +109,8 @@ module GraphQL
|
|
109
109
|
# @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
|
110
110
|
def possible_types(type_defn)
|
111
111
|
@visible_possible_types ||= read_through { |type_defn|
|
112
|
-
@schema.possible_types(type_defn, @context)
|
112
|
+
pt = @schema.possible_types(type_defn, @context)
|
113
|
+
pt.select { |t| visible_type?(t) }
|
113
114
|
}
|
114
115
|
@visible_possible_types[type_defn]
|
115
116
|
end
|
@@ -117,14 +118,14 @@ module GraphQL
|
|
117
118
|
# @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
|
118
119
|
# @return [Array<GraphQL::Field>] Fields on `type_defn`
|
119
120
|
def fields(type_defn)
|
120
|
-
@visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(f) } }
|
121
|
+
@visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(t, f) } }
|
121
122
|
@visible_fields[type_defn]
|
122
123
|
end
|
123
124
|
|
124
125
|
# @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
|
125
126
|
# @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
|
126
127
|
def arguments(argument_owner)
|
127
|
-
@visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a|
|
128
|
+
@visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_argument?(a) } }
|
128
129
|
@visible_arguments[argument_owner]
|
129
130
|
end
|
130
131
|
|
@@ -136,7 +137,7 @@ module GraphQL
|
|
136
137
|
|
137
138
|
# @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
|
138
139
|
def interfaces(obj_type)
|
139
|
-
@visible_interfaces ||= read_through { |t| t.interfaces.select { |i| visible?(i) } }
|
140
|
+
@visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible?(i) } }
|
140
141
|
@visible_interfaces[obj_type]
|
141
142
|
end
|
142
143
|
|
@@ -160,8 +161,48 @@ module GraphQL
|
|
160
161
|
@unions[obj_type]
|
161
162
|
end
|
162
163
|
|
163
|
-
def
|
164
|
-
visible?(
|
164
|
+
def visible_argument?(arg_defn)
|
165
|
+
visible?(arg_defn) && visible_type?(arg_defn.type.unwrap)
|
166
|
+
end
|
167
|
+
|
168
|
+
def visible_field?(owner_type, field_defn)
|
169
|
+
visible?(field_defn) && visible_type?(field_defn.type.unwrap) && field_on_visible_interface?(field_defn, owner_type)
|
170
|
+
end
|
171
|
+
|
172
|
+
# We need this to tell whether a field was inherited by an interface
|
173
|
+
# even when that interface is hidden from `#interfaces`
|
174
|
+
def unfiltered_interfaces(type_defn)
|
175
|
+
@unfiltered_interfaces ||= read_through(&:interfaces)
|
176
|
+
@unfiltered_interfaces[type_defn]
|
177
|
+
end
|
178
|
+
|
179
|
+
# If this field was inherited from an interface, and the field on that interface is _hidden_,
|
180
|
+
# then treat this inherited field as hidden.
|
181
|
+
# (If it _wasn't_ inherited, then don't hide it for this reason.)
|
182
|
+
def field_on_visible_interface?(field_defn, type_defn)
|
183
|
+
if type_defn.kind.object?
|
184
|
+
any_interface_has_field = false
|
185
|
+
any_interface_has_visible_field = false
|
186
|
+
ints = unfiltered_interfaces(type_defn)
|
187
|
+
ints.each do |interface_type|
|
188
|
+
if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
|
189
|
+
any_interface_has_field = true
|
190
|
+
|
191
|
+
if visible?(interface_type) && visible_field?(interface_type, iface_field_defn)
|
192
|
+
any_interface_has_visible_field = true
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
if any_interface_has_field
|
198
|
+
any_interface_has_visible_field
|
199
|
+
else
|
200
|
+
# it's the object's own field
|
201
|
+
true
|
202
|
+
end
|
203
|
+
else
|
204
|
+
true
|
205
|
+
end
|
165
206
|
end
|
166
207
|
|
167
208
|
def visible_type?(type_defn)
|
@@ -6,7 +6,7 @@ module GraphQL
|
|
6
6
|
description "Represents non-fractional signed whole numeric values. Since the value may exceed the size of a 32-bit integer, it's encoded as a string."
|
7
7
|
|
8
8
|
def self.coerce_input(value, _ctx)
|
9
|
-
Integer(value)
|
9
|
+
value && Integer(value)
|
10
10
|
rescue ArgumentError
|
11
11
|
nil
|
12
12
|
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.10.
|
4
|
+
version: 1.10.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -500,6 +500,7 @@ files:
|
|
500
500
|
- lib/graphql/query/arguments_cache.rb
|
501
501
|
- lib/graphql/query/context.rb
|
502
502
|
- lib/graphql/query/executor.rb
|
503
|
+
- lib/graphql/query/fingerprint.rb
|
503
504
|
- lib/graphql/query/input_validation_result.rb
|
504
505
|
- lib/graphql/query/literal_input.rb
|
505
506
|
- lib/graphql/query/null_context.rb
|