graphql 1.10.5 → 1.10.6
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/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
|