graphql 1.9.0.pre2 → 1.9.0.pre3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7576d86b005acda0b6db32b531eb5ee5994b46d5
4
- data.tar.gz: 5d27e5a79e0b2ab1d8c64edd8aa4ed5bc66b72e8
3
+ metadata.gz: d574e6bf6c3c4644fd85e279ddf56ab3128bef2d
4
+ data.tar.gz: a6227d0ec2ee59131b2de45ccf87c2541279543d
5
5
  SHA512:
6
- metadata.gz: 001ec87271eaefae504d24deac1c3b6d241f1b944b2297050e3d35a8a72c6a016ec9dd6a48f3c1bdef1ab5f9eb6aac2f5f31ca745bfdf9edb077abbc6f8158d7
7
- data.tar.gz: fafac00586652e70e096f68a92dd20bcf712e09343d721638987f3ba8d69e77250fd25de5743ca521687c1a22a8df5355b4f7b84f500ff898446c29fe08e4bed
6
+ metadata.gz: 3d91a0736831fa204240513b2cff401bf51a8ffb09739662fd95415bf2a91f53f088dcbcb026ff883d43476980260d1ab222d6bd5c969060ce279e9badf7ecb5
7
+ data.tar.gz: 0eaa0115f08cae059cb4d514fc572cf064956dda38ac87919d841b72103aa67df7f871e761375704ade18b5e77c474c99e5e514dcb098608bba7b3e629a0241d
@@ -40,6 +40,7 @@ module GraphQL
40
40
  @field = field
41
41
  @root_type = root_type
42
42
  @query = query
43
+ @selected_type = @field ? @field.type.unwrap : root_type
43
44
  end
44
45
 
45
46
  # @return [Array<GraphQL::Language::Nodes::Field>]
@@ -72,16 +73,10 @@ module GraphQL
72
73
  # Like {#selects?}, but can be used for chaining.
73
74
  # It returns a null object (check with {#selected?})
74
75
  # @return [GraphQL::Execution::Lookahead]
75
- def selection(field_name, arguments: nil)
76
+ def selection(field_name, selected_type: @selected_type, arguments: nil)
76
77
  next_field_name = normalize_name(field_name)
77
78
 
78
- next_field_owner = if @field
79
- @field.type.unwrap
80
- else
81
- @root_type
82
- end
83
-
84
- next_field_defn = FieldHelpers.get_field(@query.schema, next_field_owner, next_field_name)
79
+ next_field_defn = FieldHelpers.get_field(@query.schema, selected_type, next_field_name)
85
80
  if next_field_defn
86
81
  next_nodes = []
87
82
  @ast_nodes.each do |ast_node|
@@ -118,7 +113,7 @@ module GraphQL
118
113
  def selections(arguments: nil)
119
114
  subselections_by_name = {}
120
115
  @ast_nodes.each do |node|
121
- find_selections(subselections_by_name, node.selections, arguments)
116
+ find_selections(subselections_by_name, @selected_type, node.selections, arguments)
122
117
  end
123
118
 
124
119
  # Items may be filtered out if `arguments` doesn't match
@@ -139,6 +134,10 @@ module GraphQL
139
134
  @field && @field.original_name
140
135
  end
141
136
 
137
+ def inspect
138
+ "#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>"
139
+ end
140
+
142
141
  # This is returned for {Lookahead#selection} when a non-existent field is passed
143
142
  class NullLookahead < Lookahead
144
143
  # No inputs required here.
@@ -160,6 +159,10 @@ module GraphQL
160
159
  def selections(*)
161
160
  []
162
161
  end
162
+
163
+ def inspect
164
+ "#<GraphQL::Execution::Lookahead::NullLookahead>"
165
+ end
163
166
  end
164
167
 
165
168
  # A singleton, so that misses don't come with overhead.
@@ -184,16 +187,22 @@ module GraphQL
184
187
  end
185
188
  end
186
189
 
187
- def find_selections(subselections_by_name, ast_selections, arguments)
190
+ def find_selections(subselections_by_name, selected_type, ast_selections, arguments)
188
191
  ast_selections.each do |ast_selection|
189
192
  case ast_selection
190
193
  when GraphQL::Language::Nodes::Field
191
- subselections_by_name[ast_selection.name] ||= selection(ast_selection.name, arguments: arguments)
194
+ subselections_by_name[ast_selection.name] ||= selection(ast_selection.name, selected_type: selected_type, arguments: arguments)
192
195
  when GraphQL::Language::Nodes::InlineFragment
193
- find_selections(subselections_by_name, ast_selection.selections, arguments)
196
+ if (t = ast_selection.type)
197
+ # Assuming this is valid, that `t` will be found.
198
+ selected_type = @query.schema.types[t.name].metadata[:type_class]
199
+ end
200
+ find_selections(subselections_by_name, selected_type, ast_selection.selections, arguments)
194
201
  when GraphQL::Language::Nodes::FragmentSpread
195
202
  frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
196
- find_selections(subselections_by_name, frag_defn.selections, arguments)
203
+ # Again, assuming a valid AST
204
+ selected_type = @query.schema.types[frag_defn.type.name].metadata[:type_class]
205
+ find_selections(subselections_by_name, selected_type, frag_defn.selections, arguments)
197
206
  else
198
207
  raise "Invariant: Unexpected selection type: #{ast_selection.class}"
199
208
  end
@@ -65,9 +65,16 @@ module GraphQL
65
65
  # Visit `document` and all children, applying hooks as you go
66
66
  # @return [void]
67
67
  def visit
68
- @result, _nil_parent = on_node_with_modifications(@document, nil)
68
+ result = on_node_with_modifications(@document, nil)
69
+ @result = if result.is_a?(Array)
70
+ result.first
71
+ else
72
+ # The node wasn't modified
73
+ @document
74
+ end
69
75
  end
70
76
 
77
+ # Call the user-defined handler for `node`.
71
78
  def visit_node(node, parent)
72
79
  public_send(node.visit_method, node, parent)
73
80
  end
@@ -80,77 +87,81 @@ module GraphQL
80
87
  # For compatibility, it calls hook procs, too.
81
88
  # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
82
89
  # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
83
- # @return [void]
90
+ # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
84
91
  def on_abstract_node(node, parent)
85
92
  if node == DELETE_NODE
86
93
  # This might be passed to `super(DELETE_NODE, ...)`
87
94
  # by a user hook, don't want to keep visiting in that case.
88
- [node, parent]
95
+ nil
89
96
  else
90
97
  # Run hooks if there are any
91
- begin_hooks_ok = @visitors.none? || begin_visit(node, parent)
98
+ new_node = node
99
+ begin_hooks_ok = @visitors.none? || begin_visit(new_node, parent)
92
100
  if begin_hooks_ok
93
101
  node.children.each do |child_node|
94
- new_child_and_node = on_node_with_modifications(child_node, node)
102
+ new_child_and_node = on_node_with_modifications(child_node, new_node)
95
103
  # Reassign `node` in case the child hook makes a modification
96
104
  if new_child_and_node.is_a?(Array)
97
- node = new_child_and_node[1]
105
+ new_node = new_child_and_node[1]
98
106
  end
99
107
  end
100
108
  end
101
- @visitors.any? && end_visit(node, parent)
102
- [node, parent]
109
+ @visitors.any? && end_visit(new_node, parent)
110
+
111
+ if new_node.equal?(node)
112
+ nil
113
+ else
114
+ [new_node, parent]
115
+ end
103
116
  end
104
117
  end
105
118
 
106
119
  # We don't use `alias` here because it breaks `super`
107
- def self.make_visit_method(node_method, super_method)
108
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
120
+ def self.make_visit_method(node_method)
121
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
109
122
  def #{node_method}(node, parent)
110
- #{super_method}(node, parent)
123
+ child_mod = on_abstract_node(node, parent)
124
+ # If visiting the children returned changes, continue passing those.
125
+ child_mod || [node, parent]
111
126
  end
112
- EOS
127
+ RUBY
113
128
  end
114
129
 
115
- make_visit_method :on_argument, :on_abstract_node
116
- make_visit_method :on_directive, :on_abstract_node
117
- make_visit_method :on_directive_definition, :on_abstract_node
118
- make_visit_method :on_directive_location, :on_abstract_node
119
- make_visit_method :on_document, :on_abstract_node
120
- make_visit_method :on_enum, :on_abstract_node
121
- make_visit_method :on_enum_type_definition, :on_abstract_node
122
- make_visit_method :on_enum_type_extension, :on_abstract_node
123
- make_visit_method :on_enum_value_definition, :on_abstract_node
124
- make_visit_method :on_field, :on_abstract_node
125
- make_visit_method :on_field_definition, :on_abstract_node
126
- make_visit_method :on_fragment_definition, :on_abstract_node
127
- make_visit_method :on_fragment_spread, :on_abstract_node
128
- make_visit_method :on_inline_fragment, :on_abstract_node
129
- make_visit_method :on_input_object, :on_abstract_node
130
- make_visit_method :on_input_object_type_definition, :on_abstract_node
131
- make_visit_method :on_input_object_type_extension, :on_abstract_node
132
- make_visit_method :on_input_value_definition, :on_abstract_node
133
- make_visit_method :on_interface_type_definition, :on_abstract_node
134
- make_visit_method :on_interface_type_extension, :on_abstract_node
135
- make_visit_method :on_list_type, :on_abstract_node
136
- make_visit_method :on_non_null_type, :on_abstract_node
137
- make_visit_method :on_null_value, :on_abstract_node
138
- make_visit_method :on_object_type_definition, :on_abstract_node
139
- make_visit_method :on_object_type_extension, :on_abstract_node
140
- make_visit_method :on_operation_definition, :on_abstract_node
141
- make_visit_method :on_scalar_type_definition, :on_abstract_node
142
- make_visit_method :on_scalar_type_extension, :on_abstract_node
143
- make_visit_method :on_schema_definition, :on_abstract_node
144
- make_visit_method :on_schema_extension, :on_abstract_node
145
- make_visit_method :on_type_name, :on_abstract_node
146
- make_visit_method :on_union_type_definition, :on_abstract_node
147
- make_visit_method :on_union_type_extension, :on_abstract_node
148
- make_visit_method :on_variable_definition, :on_abstract_node
149
- make_visit_method :on_variable_identifier, :on_abstract_node
150
-
151
- def visit_node(node, parent)
152
- public_send(node.visit_method, node, parent)
153
- end
130
+ make_visit_method :on_argument
131
+ make_visit_method :on_directive
132
+ make_visit_method :on_directive_definition
133
+ make_visit_method :on_directive_location
134
+ make_visit_method :on_document
135
+ make_visit_method :on_enum
136
+ make_visit_method :on_enum_type_definition
137
+ make_visit_method :on_enum_type_extension
138
+ make_visit_method :on_enum_value_definition
139
+ make_visit_method :on_field
140
+ make_visit_method :on_field_definition
141
+ make_visit_method :on_fragment_definition
142
+ make_visit_method :on_fragment_spread
143
+ make_visit_method :on_inline_fragment
144
+ make_visit_method :on_input_object
145
+ make_visit_method :on_input_object_type_definition
146
+ make_visit_method :on_input_object_type_extension
147
+ make_visit_method :on_input_value_definition
148
+ make_visit_method :on_interface_type_definition
149
+ make_visit_method :on_interface_type_extension
150
+ make_visit_method :on_list_type
151
+ make_visit_method :on_non_null_type
152
+ make_visit_method :on_null_value
153
+ make_visit_method :on_object_type_definition
154
+ make_visit_method :on_object_type_extension
155
+ make_visit_method :on_operation_definition
156
+ make_visit_method :on_scalar_type_definition
157
+ make_visit_method :on_scalar_type_extension
158
+ make_visit_method :on_schema_definition
159
+ make_visit_method :on_schema_extension
160
+ make_visit_method :on_type_name
161
+ make_visit_method :on_union_type_definition
162
+ make_visit_method :on_union_type_extension
163
+ make_visit_method :on_variable_definition
164
+ make_visit_method :on_variable_identifier
154
165
 
155
166
  private
156
167
 
@@ -177,7 +188,7 @@ module GraphQL
177
188
  # The user-provided hook didn't make any modifications.
178
189
  # In fact, the hook might have returned who-knows-what, so
179
190
  # ignore the return value and use the original values.
180
- [node, parent]
191
+ new_node_and_new_parent
181
192
  end
182
193
  end
183
194
 
@@ -14,7 +14,16 @@ module GraphQL
14
14
  end
15
15
 
16
16
  def to_h
17
- super.merge({ "value" => value, "problems" => validation_result.problems })
17
+ # It is possible there are other extension items in this error, so handle
18
+ # a one level deep merge explicitly. However beyond that only show the
19
+ # latest value and problems.
20
+ super.merge({ "extensions" => { "value" => value, "problems" => validation_result.problems }}) do |key, oldValue, newValue|
21
+ if oldValue.respond_to? merge
22
+ oldValue.merge(newValue)
23
+ else
24
+ newValue
25
+ end
26
+ end
18
27
  end
19
28
  end
20
29
  end
@@ -42,6 +42,7 @@ require "graphql/schema/directive/transform"
42
42
  require "graphql/schema/resolver"
43
43
  require "graphql/schema/mutation"
44
44
  require "graphql/schema/relay_classic_mutation"
45
+ require "graphql/schema/subscription"
45
46
 
46
47
  module GraphQL
47
48
  # A GraphQL schema which may be queried with {GraphQL::Query}.
@@ -184,7 +185,7 @@ module GraphQL
184
185
  @introspection_namespace = nil
185
186
  @introspection_system = nil
186
187
  @interpeter = false
187
- @error_bubbling = true
188
+ @error_bubbling = false
188
189
  end
189
190
 
190
191
  # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
@@ -915,11 +916,15 @@ module GraphQL
915
916
  end
916
917
 
917
918
  def resolve_type(type, obj, ctx)
918
- raise NotImplementedError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
919
+ if type.kind.object?
920
+ type
921
+ else
922
+ raise NotImplementedError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
923
+ end
919
924
  end
920
925
 
921
926
  def object_from_id(node_id, ctx)
922
- raise NotImplementedError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to use the `node` field (tried to load from id `#{node_id}`)"
927
+ raise NotImplementedError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
923
928
  end
924
929
 
925
930
  def id_from_object(object, type, ctx)
@@ -59,10 +59,11 @@ module GraphQL
59
59
  # It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
60
60
  # @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
61
61
  # @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
62
+ # @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
62
63
  # @return [GraphQL::Schema:Field] an instance of `self
63
64
  # @see {.initialize} for other options
64
- def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, **kwargs, &block)
65
- if (parent_config = resolver || mutation)
65
+ def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
66
+ if (parent_config = resolver || mutation || subscription)
66
67
  # Get the parent config, merge in local overrides
67
68
  kwargs = parent_config.field_options.merge(kwargs)
68
69
  # Add a reference to that parent class
@@ -60,6 +60,7 @@ module GraphQL
60
60
  #
61
61
  class Mutation < GraphQL::Schema::Resolver
62
62
  extend GraphQL::Schema::Member::HasFields
63
+ extend GraphQL::Schema::Resolver::HasPayloadType
63
64
 
64
65
  class << self
65
66
  # Override this method to handle legacy-style usages of `MyMutation.field`
@@ -71,62 +72,17 @@ module GraphQL
71
72
  end
72
73
  end
73
74
 
74
- # Call this method to get the derived return type of the mutation,
75
- # or use it as a configuration method to assign a return type
76
- # instead of generating one.
77
- # @param new_payload_type [Class, nil] If a type definition class is provided, it will be used as the return type of the mutation field
78
- # @return [Class] The object type which this mutation returns.
79
- def payload_type(new_payload_type = nil)
80
- if new_payload_type
81
- @payload_type = new_payload_type
82
- end
83
- @payload_type ||= generate_payload_type
84
- end
85
-
86
- alias :type :payload_type
87
- alias :type_expr :payload_type
88
-
89
- def field_class(new_class = nil)
90
- if new_class
91
- @field_class = new_class
92
- else
93
- @field_class || find_inherited_method(:field_class, GraphQL::Schema::Field)
94
- end
95
- end
96
-
97
- # An object class to use for deriving return types
98
- # @param new_class [Class, nil] Defaults to {GraphQL::Schema::Object}
99
- # @return [Class]
100
- def object_class(new_class = nil)
101
- if new_class
102
- @object_class = new_class
103
- end
104
- @object_class || (superclass.respond_to?(:object_class) ? superclass.object_class : GraphQL::Schema::Object)
105
- end
106
-
107
75
  def visible?(context)
108
76
  true
109
77
  end
110
78
 
111
79
  private
112
80
 
113
- # Build a subclass of {.object_class} based on `self`.
114
- # This value will be cached as `{.payload_type}`.
115
- # Override this hook to customize return type generation.
81
+ # Override this to attach self as `mutation`
116
82
  def generate_payload_type
117
- mutation_name = graphql_name
118
- mutation_fields = fields
119
- mutation_class = self
120
- Class.new(object_class) do
121
- graphql_name("#{mutation_name}Payload")
122
- description("Autogenerated return type of #{mutation_name}")
123
- mutation(mutation_class)
124
- mutation_fields.each do |name, f|
125
- # Reattach the already-defined field here
126
- # (The field's `.owner` will still point to the mutation, not the object type, I think)
127
- add_field(f)
128
- end
129
- end
83
+ payload_class = super
84
+ payload_class.mutation(self)
85
+ payload_class
130
86
  end
131
87
  end
132
88
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/schema/resolver/has_payload_type"
2
3
 
3
4
  module GraphQL
4
5
  class Schema
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Resolver
6
+ # Adds `field(...)` helper to resolvers so that they can
7
+ # generate payload types.
8
+ #
9
+ # Or, an already-defined one can be attached with `payload_type(...)`.
10
+ module HasPayloadType
11
+ # Call this method to get the derived return type of the mutation,
12
+ # or use it as a configuration method to assign a return type
13
+ # instead of generating one.
14
+ # @param new_payload_type [Class, nil] If a type definition class is provided, it will be used as the return type of the mutation field
15
+ # @return [Class] The object type which this mutation returns.
16
+ def payload_type(new_payload_type = nil)
17
+ if new_payload_type
18
+ @payload_type = new_payload_type
19
+ end
20
+ @payload_type ||= generate_payload_type
21
+ end
22
+
23
+ alias :type :payload_type
24
+ alias :type_expr :payload_type
25
+
26
+ def field_class(new_class = nil)
27
+ if new_class
28
+ @field_class = new_class
29
+ else
30
+ @field_class || find_inherited_method(:field_class, GraphQL::Schema::Field)
31
+ end
32
+ end
33
+
34
+ # An object class to use for deriving return types
35
+ # @param new_class [Class, nil] Defaults to {GraphQL::Schema::Object}
36
+ # @return [Class]
37
+ def object_class(new_class = nil)
38
+ if new_class
39
+ @object_class = new_class
40
+ end
41
+ @object_class || (superclass.respond_to?(:object_class) ? superclass.object_class : GraphQL::Schema::Object)
42
+ end
43
+
44
+ private
45
+
46
+ # Build a subclass of {.object_class} based on `self`.
47
+ # This value will be cached as `{.payload_type}`.
48
+ # Override this hook to customize return type generation.
49
+ def generate_payload_type
50
+ resolver_name = graphql_name
51
+ resolver_fields = fields
52
+ Class.new(object_class) do
53
+ graphql_name("#{resolver_name}Payload")
54
+ description("Autogenerated return type of #{resolver_name}")
55
+ resolver_fields.each do |name, f|
56
+ # Reattach the already-defined field here
57
+ # (The field's `.owner` will still point to the mutation, not the object type, I think)
58
+ add_field(f)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end