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.
@@ -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
- value.max_page_size ||= field.max_page_size
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
- @ruby_style_hash[ruby_kwargs_key] = if arg_defn.type.list?
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
- self
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[type_object.name] = type_object
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
- arg_defns.each do |arg_name, arg_defn|
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
- prepared_value = context.schema.error_handler.with_error_handling(context) do
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
- coerced_value = if loaded_value
102
- loaded_value
103
- else
104
- arg_defn.type.coerce_input(value, context)
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.prepare_value(parent_object, coerced_value, context: context)
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
- kwarg_arguments
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
- own_interfaces.reject! { |i|
87
- new_interfaces.any? { |new_i|
88
- new_name = new_i.is_a?(String) ? new_i : new_i.graphql_name
89
- old_name = i.is_a?(String) ? i : i.graphql_name
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
- own_interfaces.concat(new_interfaces)
112
+ own_interface_type_memberships.concat(new_memberships)
94
113
  end
95
114
 
96
- def interfaces
97
- inherited_interfaces = (superclass.respond_to?(:interfaces) ? superclass.interfaces : nil)
98
- if inherited_interfaces && !inherited_interfaces.empty?
99
- own_interfaces + inherited_interfaces
100
- else
101
- own_interfaces
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
- def own_interfaces
106
- @own_interfaces ||= []
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
- # Include legacy-style interfaces, too
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.interfaces = interfaces
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
- @interface_implementers = Hash.new do |hash, key|
19
- hash[key] = @object_types.select { |type| type.interfaces.include?(key) }.sort_by(&:name)
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
- @interface_implementers[type_defn]
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).select { |t| visible_type?(t) }
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| visible_field?(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 visible_field?(field_defn)
164
- visible?(field_defn) && visible_type?(field_defn.type.unwrap)
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.10.5"
3
+ VERSION = "1.10.6"
4
4
  end
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.5
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-03-12 00:00:00.000000000 Z
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