graphql 1.6.1 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7468a99f3a243670d95dc1b6423d6de98aeacd20
4
- data.tar.gz: ac2dd7a0264557a07349df7a5a7c84eca070d27c
3
+ metadata.gz: dcc96523b4e6938e39aeae90c15728985618c95b
4
+ data.tar.gz: da73ad353f7891bf9f4146e8941200747a928c39
5
5
  SHA512:
6
- metadata.gz: 43b4b7f8618b984e00f6c646d3ed547f2cdbc0417ea83c8bed1461d10482d48ade39938facde49636c7f73f945b360eb964516bd8eeb648b03af4941db0098f1
7
- data.tar.gz: a82abfa0257bae1387fffb3fb06eead5010236b4670c9093db38e0343f81b9bccb08a3140887f4a109be67da773eb97593b669752cb17b124494bdf555fd27c4
6
+ metadata.gz: b709b3b6833dd60102f6bfa7ebc7efd7a1a36ef7af8b0aa5723b1b421cc4c14516ef2480d058c885d9fb9de34013c0e3d0e6d0bbaa22850df7ff2bf9c50535c8
7
+ data.tar.gz: 0a4b99378090cfbce6916a725ccadb8b91238cbeaf8a00c28e0fcbcedc43d1b8c238b3f2c502ece948e5379a2d33cb2265e8210c2e44ae466d570f177fb44059
@@ -40,7 +40,27 @@ module GraphQL
40
40
  run_queries(schema, queries, *rest)
41
41
  end
42
42
 
43
+ # @param schema [GraphQL::Schema]
44
+ # @param queries [Array<GraphQL::Query>]
45
+ # @param context [Hash]
46
+ # @param max_complexity [Integer]
47
+ # @return [Array<Hash>] One result per query
43
48
  def run_queries(schema, queries, context: {}, max_complexity: nil)
49
+ has_custom_strategy = schema.query_execution_strategy || schema.mutation_execution_strategy || schema.subscription_execution_strategy
50
+ if has_custom_strategy
51
+ if queries.length == 1
52
+ return [run_one_legacy(schema, queries.first)]
53
+ else
54
+ raise ArgumentError, "Multiplexing doesn't support custom execution strategies, run one query at a time instead"
55
+ end
56
+ else
57
+ run_as_multiplex(schema, queries, context: context, max_complexity: max_complexity)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def run_as_multiplex(schema, queries, context:, max_complexity:)
44
64
  query_instrumenters = schema.instrumenters[:query]
45
65
  multiplex_instrumenters = schema.instrumenters[:multiplex]
46
66
  multiplex = self.new(schema: schema, queries: queries, context: context)
@@ -80,8 +100,6 @@ module GraphQL
80
100
  multiplex_instrumenters.reverse_each { |i| i.after_multiplex(multiplex) }
81
101
  end
82
102
 
83
- private
84
-
85
103
  # @param query [GraphQL::Query]
86
104
  # @return [Hash] The initial result (may not be finished if there are lazy values)
87
105
  def begin_query(query)
@@ -128,6 +146,24 @@ module GraphQL
128
146
  result
129
147
  end
130
148
  end
149
+
150
+ # use the old `query_execution_strategy` etc to run this query
151
+ def run_one_legacy(schema, query)
152
+ instrumenters = schema.instrumenters[:query]
153
+ instrumenters.each { |i| i.before_query(query) }
154
+ query.result = if !query.valid?
155
+ all_errors = query.validation_errors + query.analysis_errors + query.context.errors
156
+ if all_errors.any?
157
+ { "errors" => all_errors.map(&:to_h) }
158
+ else
159
+ nil
160
+ end
161
+ else
162
+ GraphQL::Query::Executor.new(query).result
163
+ end
164
+ ensure
165
+ instrumenters.reverse_each { |i| i.after_query(query) }
166
+ end
131
167
  end
132
168
  end
133
169
  end
data/lib/graphql/query.rb CHANGED
@@ -188,7 +188,11 @@ module GraphQL
188
188
  end
189
189
 
190
190
  def mutation?
191
- @mutation
191
+ with_prepared_ast { @mutation }
192
+ end
193
+
194
+ def query?
195
+ with_prepared_ast { @query }
192
196
  end
193
197
 
194
198
  # @return [void]
@@ -260,6 +264,7 @@ module GraphQL
260
264
  end
261
265
  @ast_variables = @selected_operation.variables
262
266
  @mutation = @selected_operation.operation_type == "mutation"
267
+ @query = @selected_operation.operation_type == "query"
263
268
  end
264
269
  end
265
270
 
@@ -8,7 +8,6 @@ module GraphQL
8
8
  attr_reader :query
9
9
 
10
10
  def initialize(query)
11
- warn("Executor is deprecated; use Schema#execute")
12
11
  @query = query
13
12
  end
14
13
 
@@ -57,13 +57,13 @@ module GraphQL
57
57
  # @param parent [Object] The object which this collection belongs to
58
58
  # @param context [GraphQL::Query::Context] The context from the field being resolved
59
59
  def initialize(nodes, arguments, field: nil, max_page_size: nil, parent: nil, context: nil)
60
+ @context = context
60
61
  @nodes = nodes
61
62
  @arguments = arguments
62
- @max_page_size = max_page_size
63
63
  @field = field
64
64
  @parent = parent
65
- @context = context
66
65
  @encoder = context ? @context.schema.cursor_encoder : GraphQL::Schema::Base64Encoder
66
+ @max_page_size = max_page_size.nil? && context ? @context.schema.default_max_page_size : max_page_size
67
67
  end
68
68
 
69
69
  def encode(data)
@@ -83,19 +83,17 @@ module GraphQL
83
83
  end
84
84
 
85
85
  def relation_offset(relation)
86
- case relation
87
- when ActiveRecord::Relation
86
+ if relation.respond_to?(:offset_value)
88
87
  relation.offset_value
89
- when Sequel::Dataset
88
+ else
90
89
  relation.opts[:offset]
91
90
  end
92
91
  end
93
92
 
94
93
  def relation_limit(relation)
95
- case relation
96
- when ActiveRecord::Relation
94
+ if relation.respond_to?(:limit_value)
97
95
  relation.limit_value
98
- when Sequel::Dataset
96
+ else
99
97
  relation.opts[:limit]
100
98
  end
101
99
  end
@@ -55,7 +55,7 @@ module GraphQL
55
55
  accepts_definitions \
56
56
  :query, :mutation, :subscription,
57
57
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
58
- :max_depth, :max_complexity,
58
+ :max_depth, :max_complexity, :default_max_page_size,
59
59
  :orphan_types, :resolve_type, :type_error, :parse_error,
60
60
  :raise_definition_error,
61
61
  :object_from_id, :id_from_object,
@@ -77,7 +77,7 @@ module GraphQL
77
77
  attr_accessor \
78
78
  :query, :mutation, :subscription,
79
79
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
80
- :max_depth, :max_complexity,
80
+ :max_depth, :max_complexity, :default_max_page_size,
81
81
  :orphan_types, :directives,
82
82
  :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
83
83
  :cursor_encoder,
@@ -98,7 +98,7 @@ module GraphQL
98
98
  GraphQL::Filter.new(except: default_mask)
99
99
  end
100
100
 
101
- self.default_execution_strategy = GraphQL::Execution::Execute
101
+ self.default_execution_strategy = nil
102
102
 
103
103
  BUILT_IN_TYPES = Hash[[INT_TYPE, STRING_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, ID_TYPE].map{ |type| [type.name, type] }]
104
104
  DIRECTIVES = [GraphQL::Directive::IncludeDirective, GraphQL::Directive::SkipDirective, GraphQL::Directive::DeprecatedDirective]
@@ -256,11 +256,22 @@ module GraphQL
256
256
 
257
257
  # Resolve field named `field_name` for type `parent_type`.
258
258
  # Handles dynamic fields `__typename`, `__type` and `__schema`, too
259
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
259
+ # @param parent_type [String, GraphQL::BaseType]
260
+ # @param field_name [String]
260
261
  # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`
262
+ # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
261
263
  def get_field(parent_type, field_name)
262
264
  with_definition_error_check do
263
- defined_field = @instrumented_field_map.get(parent_type.name, field_name)
265
+ parent_type_name = case parent_type
266
+ when GraphQL::BaseType
267
+ parent_type.name
268
+ when String
269
+ parent_type
270
+ else
271
+ raise "Unexpected parent_type: #{parent_type}"
272
+ end
273
+
274
+ defined_field = @instrumented_field_map.get(parent_type_name, field_name)
264
275
  if defined_field
265
276
  defined_field
266
277
  elsif field_name == "__typename"
@@ -15,18 +15,18 @@ module GraphQL
15
15
  # @example Rescue from not-found by telling the user
16
16
  # MySchema.rescue_from(ActiveRecord::RecordNotFound) { "An item could not be found" }
17
17
  #
18
- # @param error_class [Class] a class of error to rescue from
19
- # @yield [err] A handler to return a message for this error instance
18
+ # @param error_classes [Class] one or more classes of errors to rescue from
19
+ # @yield [err] A handler to return a message for these error instances
20
20
  # @yieldparam [Exception] an error that was rescued
21
21
  # @yieldreturn [String] message to put in GraphQL response
22
- def rescue_from(error_class, &block)
23
- rescue_table[error_class] = block
22
+ def rescue_from(*error_classes, &block)
23
+ error_classes.map{ |error_class| rescue_table[error_class] = block }
24
24
  end
25
25
 
26
- # Remove the handler for `error_class`
26
+ # Remove the handler for `error_classs`
27
27
  # @param error_class [Class] the error class whose handler should be removed
28
- def remove_handler(error_class)
29
- rescue_table.delete(error_class)
28
+ def remove_handler(*error_classes)
29
+ error_classes.map{ |error_class| rescue_table.delete(error_class) }
30
30
  end
31
31
 
32
32
  # Implement the requirement for {GraphQL::Schema::MiddlewareChain}
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.6.1"
3
+ VERSION = "1.6.2"
4
4
  end
@@ -500,6 +500,24 @@ describe GraphQL::Query do
500
500
  end
501
501
  end
502
502
 
503
+ describe "#mutation?" do
504
+ let(:query_string) { <<-GRAPHQL
505
+ query Q { __typename }
506
+ mutation M { pushValue(value: 1) }
507
+ GRAPHQL
508
+ }
509
+
510
+ it "returns true if the selected operation is a mutation" do
511
+ query_query = GraphQL::Query.new(schema, query_string, operation_name: "Q")
512
+ assert_equal false, query_query.mutation?
513
+ assert_equal true, query_query.query?
514
+
515
+ mutation_query = GraphQL::Query.new(schema, query_string, operation_name: "M")
516
+ assert_equal true, mutation_query.mutation?
517
+ assert_equal false, mutation_query.query?
518
+ end
519
+ end
520
+
503
521
  describe 'NullValue type arguments' do
504
522
  let(:schema_definition) {
505
523
  <<-GRAPHQL
@@ -578,4 +596,30 @@ describe GraphQL::Query do
578
596
  assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["dairyFields"]
579
597
  end
580
598
  end
599
+
600
+ describe "query_execution_strategy" do
601
+ let(:custom_execution_schema) { schema.redefine(query_execution_strategy: DummyStrategy) }
602
+
603
+ class DummyStrategy
604
+ def execute(ast_operation, root_type, query_object)
605
+ { "dummy" => true }
606
+ end
607
+ end
608
+
609
+ it "is used for running a query, if it's present and not the default" do
610
+ result = custom_execution_schema.execute(" { __typename }")
611
+ assert_equal({"data"=>{"dummy"=>true}}, result)
612
+ end
613
+
614
+ it "can't run a multiplex" do
615
+ err = assert_raises ArgumentError do
616
+ custom_execution_schema.multiplex([
617
+ {query: " { __typename }"},
618
+ {query: " { __typename }"},
619
+ ])
620
+ end
621
+ msg = "Multiplexing doesn't support custom execution strategies, run one query at a time instead"
622
+ assert_equal msg, err.message
623
+ end
624
+ end
581
625
  end
@@ -196,5 +196,65 @@ describe GraphQL::Relay::ArrayConnection do
196
196
  assert_equal(first_and_second_names, get_names(result))
197
197
  end
198
198
  end
199
+
200
+ describe "applying default_max_page_size" do
201
+ def get_names(result)
202
+ result["data"]["rebels"]["bases"]["edges"].map { |e| e["node"]["name"] }
203
+ end
204
+
205
+ def get_page_info(result)
206
+ result["data"]["rebels"]["bases"]["pageInfo"]
207
+ end
208
+
209
+ let(:query_string) {%|
210
+ query getShips($first: Int, $after: String, $last: Int, $before: String){
211
+ rebels {
212
+ bases: basesWithDefaultMaxLimitArray(first: $first, after: $after, last: $last, before: $before) {
213
+ edges {
214
+ cursor
215
+ node {
216
+ name
217
+ }
218
+ }
219
+ pageInfo {
220
+ hasNextPage
221
+ hasPreviousPage
222
+ }
223
+ }
224
+ }
225
+ }
226
+ |}
227
+
228
+ it "applies to queries by `first`" do
229
+ result = star_wars_query(query_string, "first" => 100)
230
+ assert_equal(["Yavin", "Echo Base", "Secret Hideout"], get_names(result))
231
+ assert_equal(true, get_page_info(result)["hasNextPage"])
232
+
233
+ # Max page size is applied _without_ `first`, also
234
+ result = star_wars_query(query_string)
235
+ assert_equal(["Yavin", "Echo Base", "Secret Hideout"], get_names(result))
236
+ assert_equal(false, get_page_info(result)["hasNextPage"], "hasNextPage is false when first is not specified")
237
+ end
238
+
239
+ it "applies to queries by `last`" do
240
+ last_cursor = "Ng=="
241
+
242
+ result = star_wars_query(query_string, "last" => 100, "before" => last_cursor)
243
+ assert_equal(["Secret Hideout", "Death Star", "Shield Generator"], get_names(result))
244
+ assert_equal(true, get_page_info(result)["hasPreviousPage"])
245
+
246
+ result = star_wars_query(query_string, "before" => last_cursor)
247
+ assert_equal(["Yavin", "Echo Base", "Secret Hideout"], get_names(result))
248
+ assert_equal(false, get_page_info(result)["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
249
+
250
+ fourth_cursor = "NA=="
251
+ first_second_and_third_names = ["Yavin", "Echo Base", "Secret Hideout"]
252
+ result = star_wars_query(query_string, "last" => 100, "before" => fourth_cursor)
253
+ assert_equal(first_second_and_third_names, get_names(result))
254
+
255
+ result = star_wars_query(query_string, "before" => fourth_cursor)
256
+ assert_equal(first_second_and_third_names, get_names(result))
257
+ end
258
+ end
199
259
  end
200
260
  end
@@ -225,6 +225,65 @@ describe GraphQL::Relay::RelationConnection do
225
225
  assert_equal(first_and_second_names, get_names(result))
226
226
  end
227
227
  end
228
+
229
+ describe "applying default_max_page_size" do
230
+ let(:query_string) {%|
231
+ query getBases($first: Int, $after: String, $last: Int, $before: String){
232
+ empire {
233
+ bases: basesWithDefaultMaxLimitRelation(first: $first, after: $after, last: $last, before: $before) {
234
+ ... basesConnection
235
+ }
236
+ }
237
+ }
238
+
239
+ fragment basesConnection on BaseConnection {
240
+ edges {
241
+ cursor
242
+ node {
243
+ name
244
+ }
245
+ },
246
+ pageInfo {
247
+ hasNextPage
248
+ hasPreviousPage
249
+ startCursor
250
+ endCursor
251
+ }
252
+ }
253
+ |}
254
+
255
+ it "applies to queries by `first`" do
256
+ result = star_wars_query(query_string, "first" => 100)
257
+ assert_equal(3, result["data"]["empire"]["bases"]["edges"].size)
258
+ assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"])
259
+
260
+ # Max page size is applied _without_ `first`, also
261
+ result = star_wars_query(query_string)
262
+ assert_equal(3, result["data"]["empire"]["bases"]["edges"].size)
263
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"], "hasNextPage is false when first is not specified")
264
+ end
265
+
266
+ it "applies to queries by `last`" do
267
+ second_to_last_three_names = ["Secret Hideout", "Death Star", "Shield Generator"]
268
+ first_second_and_third_names = ["Yavin", "Echo Base", "Secret Hideout"]
269
+
270
+ last_cursor = "Ng=="
271
+ result = star_wars_query(query_string, "last" => 100, "before" => last_cursor)
272
+ assert_equal(second_to_last_three_names, get_names(result))
273
+ assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
274
+
275
+ result = star_wars_query(query_string, "before" => last_cursor)
276
+ assert_equal(first_second_and_third_names, get_names(result))
277
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
278
+
279
+ fourth_cursor = "NA=="
280
+ result = star_wars_query(query_string, "last" => 100, "before" => fourth_cursor)
281
+ assert_equal(first_second_and_third_names, get_names(result))
282
+
283
+ result = star_wars_query(query_string, "before" => fourth_cursor)
284
+ assert_equal(first_second_and_third_names, get_names(result))
285
+ end
286
+ end
228
287
  end
229
288
 
230
289
  describe "without a block" do
@@ -2,6 +2,7 @@
2
2
  require "spec_helper"
3
3
 
4
4
  class SpecExampleError < StandardError; end
5
+ class SecondSpecExampleError < StandardError; end
5
6
 
6
7
  describe GraphQL::Schema::RescueMiddleware do
7
8
  let(:error_middleware) { ->{ raise(error_class) } }
@@ -23,6 +24,20 @@ describe GraphQL::Schema::RescueMiddleware do
23
24
  assert_equal("there was an example error: SpecExampleError", result.message)
24
25
  assert_equal(GraphQL::ExecutionError, result.class)
25
26
  end
27
+
28
+ describe "with multiple error classes" do
29
+ let(:error_class) { SecondSpecExampleError }
30
+ let(:rescue_middleware) do
31
+ middleware = GraphQL::Schema::RescueMiddleware.new
32
+ middleware.rescue_from(SpecExampleError, SecondSpecExampleError) { |err| "there was an example error: #{err.class.name}" }
33
+ middleware
34
+ end
35
+
36
+ it "handles errors for all of the classes" do
37
+ result = middleware_chain.invoke([])
38
+ assert_equal("there was an example error: SecondSpecExampleError", result.message)
39
+ end
40
+ end
26
41
  end
27
42
 
28
43
  describe "unknown errors" do
@@ -31,4 +46,18 @@ describe GraphQL::Schema::RescueMiddleware do
31
46
  assert_raises(RuntimeError) { middleware_chain.invoke([]) }
32
47
  end
33
48
  end
49
+
50
+ describe "removing multiple error handlers" do
51
+ let(:error_class) { SpecExampleError }
52
+ let(:rescue_middleware) do
53
+ middleware = GraphQL::Schema::RescueMiddleware.new
54
+ middleware.rescue_from(SpecExampleError, SecondSpecExampleError) { |err| "there was an example error: #{err.class.name}" }
55
+ middleware.remove_handler(SpecExampleError, SecondSpecExampleError)
56
+ middleware
57
+ end
58
+
59
+ it "no longer handles those errors" do
60
+ assert_raises(SpecExampleError) { middleware_chain.invoke([]) }
61
+ end
62
+ end
34
63
  end
@@ -360,4 +360,13 @@ type Query {
360
360
  assert_equal result, JSON.parse(schema.to_json)
361
361
  end
362
362
  end
363
+
364
+ describe "#get_field" do
365
+ it "returns fields by type or type name" do
366
+ field = schema.get_field("Cheese", "id")
367
+ assert_instance_of GraphQL::Field, field
368
+ field_2 = schema.get_field(Dummy::CheeseType, "id")
369
+ assert_equal field, field_2
370
+ end
371
+ end
363
372
  end
@@ -96,7 +96,7 @@ module StarWars
96
96
 
97
97
  field :id, !types.ID, resolve: GraphQL::Relay::GlobalIdResolve.new(type: Faction)
98
98
  field :name, types.String
99
- connection :ships, ShipConnectionWithParentType do
99
+ connection :ships, ShipConnectionWithParentType, max_page_size: 1000 do
100
100
  resolve ->(obj, args, ctx) {
101
101
  all_ships = obj.ships.map {|ship_id| StarWars::DATA["Ship"][ship_id] }
102
102
  if args[:nameIncludes]
@@ -159,7 +159,15 @@ module StarWars
159
159
  resolve ->(object, args, context) { Base.all.to_a }
160
160
  end
161
161
 
162
- connection :basesAsSequelDataset, BaseConnectionWithTotalCountType do
162
+ connection :basesWithDefaultMaxLimitRelation, BaseType.connection_type do
163
+ resolve ->(object, args, context) { Base.all }
164
+ end
165
+
166
+ connection :basesWithDefaultMaxLimitArray, BaseType.connection_type do
167
+ resolve ->(object, args, context) { Base.all.to_a }
168
+ end
169
+
170
+ connection :basesAsSequelDataset, BaseConnectionWithTotalCountType, max_page_size: 1000 do
163
171
  argument :nameIncludes, types.String
164
172
  resolve ->(obj, args, ctx) {
165
173
  all_bases = SequelBase.where(faction_id: obj.id)
@@ -332,6 +340,7 @@ module StarWars
332
340
  Schema = GraphQL::Schema.define do
333
341
  query(QueryType)
334
342
  mutation(MutationType)
343
+ default_max_page_size 3
335
344
 
336
345
  resolve_type ->(object, ctx) {
337
346
  if object == :test_error
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.6.1
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-28 00:00:00.000000000 Z
11
+ date: 2017-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips