graphql 1.10.5 → 1.10.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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