graphql 1.8.7 → 1.8.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/enum_type.rb +4 -0
  3. data/lib/graphql/execution/multiplex.rb +1 -1
  4. data/lib/graphql/schema/argument.rb +1 -0
  5. data/lib/graphql/schema/enum_value.rb +1 -0
  6. data/lib/graphql/schema/field.rb +1 -0
  7. data/lib/graphql/schema/interface.rb +1 -0
  8. data/lib/graphql/schema/member.rb +2 -0
  9. data/lib/graphql/schema/member/has_path.rb +25 -0
  10. data/lib/graphql/schema/relay_classic_mutation.rb +3 -9
  11. data/lib/graphql/schema/resolver.rb +25 -5
  12. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -2
  13. data/lib/graphql/upgrader/member.rb +5 -0
  14. data/lib/graphql/version.rb +1 -1
  15. data/spec/dummy/Gemfile +1 -1
  16. data/spec/graphql/enum_type_spec.rb +5 -0
  17. data/spec/graphql/execution/execute_spec.rb +1 -1
  18. data/spec/graphql/execution/multiplex_spec.rb +28 -1
  19. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -0
  20. data/spec/graphql/schema/argument_spec.rb +5 -1
  21. data/spec/graphql/schema/enum_spec.rb +7 -0
  22. data/spec/graphql/schema/enum_value_spec.rb +11 -0
  23. data/spec/graphql/schema/field_spec.rb +7 -0
  24. data/spec/graphql/schema/input_object_spec.rb +11 -0
  25. data/spec/graphql/schema/interface_spec.rb +6 -0
  26. data/spec/graphql/schema/object_spec.rb +6 -0
  27. data/spec/graphql/schema/relay_classic_mutation_spec.rb +71 -0
  28. data/spec/graphql/schema/resolver_spec.rb +72 -8
  29. data/spec/graphql/schema/scalar_spec.rb +6 -0
  30. data/spec/graphql/schema/union_spec.rb +7 -0
  31. data/spec/graphql/upgrader/member_spec.rb +67 -0
  32. data/spec/{graphql → integration/mongoid/graphql}/relay/mongo_relation_connection_spec.rb +0 -11
  33. data/spec/integration/mongoid/spec_helper.rb +2 -0
  34. data/spec/{support → integration/mongoid}/star_trek/data.rb +0 -0
  35. data/spec/{support → integration/mongoid}/star_trek/schema.rb +0 -0
  36. data/spec/{support/star_wars → integration/rails}/data.rb +1 -0
  37. data/spec/{support → integration/rails/generators}/base_generator_test.rb +0 -0
  38. data/spec/{generators → integration/rails/generators}/graphql/enum_generator_spec.rb +0 -0
  39. data/spec/{generators → integration/rails/generators}/graphql/install_generator_spec.rb +0 -0
  40. data/spec/{generators → integration/rails/generators}/graphql/interface_generator_spec.rb +0 -0
  41. data/spec/{generators → integration/rails/generators}/graphql/loader_generator_spec.rb +0 -0
  42. data/spec/{generators → integration/rails/generators}/graphql/mutation_generator_spec.rb +0 -0
  43. data/spec/{generators → integration/rails/generators}/graphql/object_generator_spec.rb +0 -0
  44. data/spec/{generators → integration/rails/generators}/graphql/union_generator_spec.rb +0 -0
  45. data/spec/{graphql → integration/rails/graphql}/input_object_type_spec.rb +0 -0
  46. data/spec/{graphql → integration/rails/graphql}/query/variables_spec.rb +0 -0
  47. data/spec/{graphql → integration/rails/graphql}/relay/array_connection_spec.rb +0 -0
  48. data/spec/{graphql → integration/rails/graphql}/relay/base_connection_spec.rb +0 -0
  49. data/spec/{graphql → integration/rails/graphql}/relay/connection_instrumentation_spec.rb +0 -0
  50. data/spec/{graphql → integration/rails/graphql}/relay/connection_resolve_spec.rb +0 -0
  51. data/spec/{graphql → integration/rails/graphql}/relay/connection_type_spec.rb +0 -0
  52. data/spec/{graphql → integration/rails/graphql}/relay/edge_spec.rb +0 -0
  53. data/spec/{graphql → integration/rails/graphql}/relay/mutation_spec.rb +0 -0
  54. data/spec/{graphql → integration/rails/graphql}/relay/node_spec.rb +0 -0
  55. data/spec/{graphql → integration/rails/graphql}/relay/page_info_spec.rb +0 -0
  56. data/spec/{graphql → integration/rails/graphql}/relay/range_add_spec.rb +0 -0
  57. data/spec/{graphql → integration/rails/graphql}/relay/relation_connection_spec.rb +0 -0
  58. data/spec/{graphql → integration/rails/graphql}/schema_spec.rb +0 -0
  59. data/spec/{graphql → integration/rails/graphql}/tracing/active_support_notifications_tracing_spec.rb +0 -0
  60. data/spec/integration/rails/spec_helper.rb +15 -0
  61. data/spec/spec_helper.rb +16 -39
  62. data/spec/support/jazz.rb +48 -0
  63. metadata +260 -243
  64. data/spec/rails_dependency_sanity_spec.rb +0 -14
@@ -14,6 +14,12 @@ describe GraphQL::Schema::Object do
14
14
  assert object_class.respond_to?(:connection_type)
15
15
  end
16
16
 
17
+ describe "path" do
18
+ it "is the type name" do
19
+ assert_equal "Ensemble", object_class.path
20
+ end
21
+ end
22
+
17
23
  it "inherits fields and interfaces" do
18
24
  new_object_class = Class.new(object_class) do
19
25
  field :newField, String, null: true
@@ -52,6 +52,71 @@ describe GraphQL::Schema::RelayClassicMutation do
52
52
  end
53
53
  end
54
54
 
55
+ describe "loading multiple application objects" do
56
+ let(:query_str) {
57
+ <<-GRAPHQL
58
+ mutation($ids: [ID!]!) {
59
+ upvoteEnsembles(input: {ensembleIds: $ids}) {
60
+ ensembles {
61
+ id
62
+ }
63
+ }
64
+ }
65
+ GRAPHQL
66
+ }
67
+
68
+ it "loads arguments as objects of the given type and strips `_ids` suffix off argument name and appends `s`" do
69
+ res = Jazz::Schema.execute(query_str, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
70
+ assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["upvoteEnsembles"]["ensembles"].map { |e| e["id"] }
71
+ end
72
+
73
+ it "uses the `as:` name when loading" do
74
+ as_bands_query_str = query_str.sub("upvoteEnsembles", "upvoteEnsemblesAsBands")
75
+ res = Jazz::Schema.execute(as_bands_query_str, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
76
+ assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["upvoteEnsemblesAsBands"]["ensembles"].map { |e| e["id"] }
77
+ end
78
+
79
+ it "doesn't append `s` to argument names that already end in `s`" do
80
+ query = <<-GRAPHQL
81
+ mutation($ids: [ID!]!) {
82
+ upvoteEnsemblesIds(input: {ensemblesIds: $ids}) {
83
+ ensembles {
84
+ id
85
+ }
86
+ }
87
+ }
88
+ GRAPHQL
89
+
90
+ res = Jazz::Schema.execute(query, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
91
+ assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["upvoteEnsemblesIds"]["ensembles"].map { |e| e["id"] }
92
+ end
93
+
94
+ it "returns an error instead when the ID resolves to nil" do
95
+ res = Jazz::Schema.execute(query_str, variables: {
96
+ ids: ["Ensemble/Nonexistant Name"],
97
+ })
98
+ assert_nil res["data"].fetch("upvoteEnsembles")
99
+ assert_equal ['No object found for `ensembleIds: "Ensemble/Nonexistant Name"`'], res["errors"].map { |e| e["message"] }
100
+ end
101
+
102
+ it "returns an error instead when the ID resolves to an object of the wrong type" do
103
+ res = Jazz::Schema.execute(query_str, variables: {
104
+ ids: ["Instrument/Organ"],
105
+ })
106
+ assert_nil res["data"].fetch("upvoteEnsembles")
107
+ assert_equal ["No object found for `ensembleIds: \"Instrument/Organ\"`"], res["errors"].map { |e| e["message"] }
108
+ end
109
+
110
+ it "raises an authorization error when the type's auth fails" do
111
+ res = Jazz::Schema.execute(query_str, variables: {
112
+ ids: ["Ensemble/Spinal Tap"],
113
+ })
114
+ assert_nil res["data"].fetch("upvoteEnsembles")
115
+ # Failed silently
116
+ refute res.key?("errors")
117
+ end
118
+ end
119
+
55
120
  describe "loading application objects" do
56
121
  let(:query_str) {
57
122
  <<-GRAPHQL
@@ -70,6 +135,12 @@ describe GraphQL::Schema::RelayClassicMutation do
70
135
  assert_equal "August Greene", res["data"]["renameEnsemble"]["ensemble"]["name"]
71
136
  end
72
137
 
138
+ it "uses the `as:` name when loading" do
139
+ band_query_str = query_str.sub("renameEnsemble", "renameEnsembleAsBand")
140
+ res = Jazz::Schema.execute(band_query_str, variables: { id: "Ensemble/Robert Glasper Experiment", newName: "August Greene"})
141
+ assert_equal "August Greene", res["data"]["renameEnsembleAsBand"]["ensemble"]["name"]
142
+ end
143
+
73
144
  it "returns an error instead when the ID resolves to nil" do
74
145
  res = Jazz::Schema.execute(query_str, variables: {
75
146
  id: "Ensemble/Nonexistant Name",
@@ -192,28 +192,45 @@ describe GraphQL::Schema::Resolver do
192
192
  end
193
193
  end
194
194
 
195
+ class PrepResolver9Array < BaseResolver
196
+ argument :int_ids, [ID], required: true, loads: HasValue, as: :ints
197
+ # Make sure the lazy object is resolved properly:
198
+ type [HasValue], null: false
199
+ def object_from_id(type, id, ctx)
200
+ # Make sure a lazy object is handled appropriately
201
+ LazyBlock.new {
202
+ # Make sure that the right type ends up here
203
+ id.to_i + type.graphql_name.length
204
+ }
205
+ end
206
+
207
+ def resolve(ints:)
208
+ ints.map { |int| int * 3}
209
+ end
210
+ end
211
+
195
212
  class PrepResolver10 < BaseResolver
196
213
  argument :int1, Integer, required: true
197
- argument :int2, Integer, required: true
214
+ argument :int2, Integer, required: true, as: :integer_2
198
215
  type Integer, null: true
199
- def authorized?(int1:, int2:)
200
- if int1 + int2 > context[:max_int]
216
+ def authorized?(int1:, integer_2:)
217
+ if int1 + integer_2 > context[:max_int]
201
218
  raise GraphQL::ExecutionError, "Inputs too big"
202
- elsif context[:min_int] && (int1 + int2 < context[:min_int])
219
+ elsif context[:min_int] && (int1 + integer_2 < context[:min_int])
203
220
  false
204
221
  else
205
222
  true
206
223
  end
207
224
  end
208
225
 
209
- def resolve(int1:, int2:)
210
- int1 + int2
226
+ def resolve(int1:, integer_2:)
227
+ int1 + integer_2
211
228
  end
212
229
  end
213
230
 
214
231
  class PrepResolver11 < PrepResolver10
215
- def authorized?(int1:, int2:)
216
- LazyBlock.new { super(int1: int1 * 2, int2: int2) }
232
+ def authorized?(int1:, integer_2:)
233
+ LazyBlock.new { super(int1: int1 * 2, integer_2: integer_2) }
217
234
  end
218
235
  end
219
236
 
@@ -242,6 +259,17 @@ describe GraphQL::Schema::Resolver do
242
259
  end
243
260
  end
244
261
 
262
+ class PrepResolver14 < GraphQL::Schema::RelayClassicMutation
263
+ field :number, Integer, null: false
264
+
265
+ def authorized?
266
+ true
267
+ end
268
+
269
+ def resolve
270
+ { number: 1 }
271
+ end
272
+ end
245
273
 
246
274
  class Query < GraphQL::Schema::Object
247
275
  class CustomField < GraphQL::Schema::Field
@@ -274,10 +302,12 @@ describe GraphQL::Schema::Resolver do
274
302
  field :prep_resolver_6, resolver: PrepResolver6
275
303
  field :prep_resolver_7, resolver: PrepResolver7
276
304
  field :prep_resolver_9, resolver: PrepResolver9
305
+ field :prep_resolver_9_array, resolver: PrepResolver9Array
277
306
  field :prep_resolver_10, resolver: PrepResolver10
278
307
  field :prep_resolver_11, resolver: PrepResolver11
279
308
  field :prep_resolver_12, resolver: PrepResolver12
280
309
  field :prep_resolver_13, resolver: PrepResolver13
310
+ field :prep_resolver_14, resolver: PrepResolver14
281
311
  end
282
312
 
283
313
  class Schema < GraphQL::Schema
@@ -291,6 +321,22 @@ describe GraphQL::Schema::Resolver do
291
321
  ResolverTest::Schema.execute(*args)
292
322
  end
293
323
 
324
+ describe ".path" do
325
+ it "is the name" do
326
+ assert_equal "Resolver1", ResolverTest::Resolver1.path
327
+ end
328
+
329
+ it "is used for arguments and fields" do
330
+ assert_equal "Resolver1.value", ResolverTest::Resolver1.arguments["value"].path
331
+ assert_equal "PrepResolver7.int", ResolverTest::PrepResolver7.fields["int"].path
332
+ end
333
+
334
+ it "works on instances" do
335
+ r = ResolverTest::Resolver1.new(object: nil, context: nil)
336
+ assert_equal "Resolver1", r.path
337
+ end
338
+ end
339
+
294
340
  it "gets initialized for each resolution" do
295
341
  # State isn't shared between calls:
296
342
  res = exec_query " { r1: resolver1(value: 1) r2: resolver1 }"
@@ -426,6 +472,11 @@ describe GraphQL::Schema::Resolver do
426
472
  assert_equal 11, res["data"]["prepResolver10"]
427
473
  end
428
474
 
475
+ it "uses the argument name provided in `as:`" do
476
+ res = exec_query("{ prepResolver10(int1: 5, int2: 6) }", context: { max_int: 90 })
477
+ assert_equal 11, res["data"]["prepResolver10"]
478
+ end
479
+
429
480
  it "can return a lazy object" do
430
481
  # This is too big because it's modified in the overridden authorized? hook:
431
482
  res = exec_query("{ prepResolver11(int1: 3, int2: 5) }", context: { max_int: 9 })
@@ -462,6 +513,11 @@ describe GraphQL::Schema::Resolver do
462
513
  res = exec_query(str, context: { max_int: 100, min_int: 20 })
463
514
  assert_equal({ "prepResolver10" => nil, "prepResolver11" => nil }, res["data"])
464
515
  end
516
+
517
+ it "works with no arguments for RelayClassicMutation" do
518
+ res = exec_query("{ prepResolver14(input: {}) { number } }")
519
+ assert_equal 1, res["data"]["prepResolver14"]["number"]
520
+ end
465
521
  end
466
522
  end
467
523
 
@@ -471,6 +527,14 @@ describe GraphQL::Schema::Resolver do
471
527
  # (5 + 8) * 3
472
528
  assert_equal 39, res["data"]["prepResolver9"]["value"]
473
529
  end
530
+
531
+ it "supports loading array of ids" do
532
+ res = exec_query('{ prepResolver9Array(intIds: ["1", "10", "100"]) { value } }')
533
+ # (1 + 8) * 3
534
+ # (10 + 8) * 3
535
+ # (100 + 8) * 3
536
+ assert_equal [27, 54, 324], res["data"]["prepResolver9Array"].map { |v| v["value"] }
537
+ end
474
538
  end
475
539
  end
476
540
  end
@@ -2,6 +2,12 @@
2
2
  require "spec_helper"
3
3
 
4
4
  describe GraphQL::Schema::Scalar do
5
+ describe ".path" do
6
+ it "is the name" do
7
+ assert_equal "String", GraphQL::Types::String.path
8
+ end
9
+ end
10
+
5
11
  describe "in queries" do
6
12
  it "becomes output" do
7
13
  query_str = <<-GRAPHQL
@@ -3,6 +3,13 @@ require "spec_helper"
3
3
 
4
4
  describe GraphQL::Schema::Union do
5
5
  let(:union) { Jazz::PerformingAct }
6
+
7
+ describe ".path" do
8
+ it "is the name" do
9
+ assert_equal "PerformingAct", union.path
10
+ end
11
+ end
12
+
6
13
  describe "type info" do
7
14
  it "has some" do
8
15
  assert_equal 2, union.possible_types.size
@@ -292,6 +292,10 @@ RUBY
292
292
  new = %{field :name, String, null: false}
293
293
  assert_equal new, upgrade(old)
294
294
 
295
+ old = %{field :name, types.String.to_non_null_type}
296
+ new = %{field :name, String, null: false}
297
+ assert_equal new, upgrade(old)
298
+
295
299
  old = %{field :name, !types.String, "description", method: :name_full}
296
300
  new = %{field :name, String, "description", method: :name_full, null: false}
297
301
  assert_equal new, upgrade(old)
@@ -300,6 +304,10 @@ RUBY
300
304
  new = %{field :name, String, null: false}
301
305
  assert_equal new, upgrade(old)
302
306
 
307
+ old = %{field :name, -> { types.String.to_non_null_type }}
308
+ new = %{field :name, String, null: false}
309
+ assert_equal new, upgrade(old)
310
+
303
311
  old = %{connection :name, Name.connection_type, "names"}
304
312
  new = %{field :name, Name.connection_type, "names", null: true, connection: true}
305
313
  assert_equal new, upgrade(old)
@@ -308,10 +316,18 @@ RUBY
308
316
  new = %{field :name, Name.connection_type, "names", null: false, connection: true}
309
317
  assert_equal new, upgrade(old)
310
318
 
319
+ old = %{connection :name, Name.connection_type.to_non_null_type, "names"}
320
+ new = %{field :name, Name.connection_type, "names", null: false, connection: true}
321
+ assert_equal new, upgrade(old)
322
+
311
323
  old = %{field :names, types[!types.String]}
312
324
  new = %{field :names, [String], null: true}
313
325
  assert_equal new, upgrade(old)
314
326
 
327
+ old = %{field :names, types[types.String.to_non_null_type]}
328
+ new = %{field :names, [String], null: true}
329
+ assert_equal new, upgrade(old)
330
+
315
331
  old = %{field :names, !types[types.String]}
316
332
  new = %{field :names, [String, null: true], null: false}
317
333
  assert_equal new, upgrade(old)
@@ -341,6 +357,22 @@ RUBY
341
357
  }
342
358
  assert_equal new, upgrade(old)
343
359
 
360
+ old = %{
361
+ field :name, types.String.to_non_null_type do
362
+ description "abc"
363
+ end
364
+
365
+ field :name2, types.Int.to_non_null_type do
366
+ description "def"
367
+ end
368
+ }
369
+ new = %{
370
+ field :name, String, description: "abc", null: false
371
+
372
+ field :name2, Integer, description: "def", null: false
373
+ }
374
+ assert_equal new, upgrade(old)
375
+
344
376
  old = %{
345
377
  field :name, -> { !types.String } do
346
378
  end
@@ -350,6 +382,15 @@ RUBY
350
382
  }
351
383
  assert_equal new, upgrade(old)
352
384
 
385
+ old = %{
386
+ field :name, -> { types.String.to_non_null_type } do
387
+ end
388
+ }
389
+ new = %{
390
+ field :name, String, null: false
391
+ }
392
+ assert_equal new, upgrade(old)
393
+
353
394
  old = %{
354
395
  field :name do
355
396
  type -> { String }
@@ -376,6 +417,22 @@ RUBY
376
417
  }
377
418
  assert_equal new, upgrade(old)
378
419
 
420
+ old = %{
421
+ field :name do
422
+ type String.to_non_null_type
423
+ end
424
+
425
+ field :name2 do
426
+ type String.to_non_null_type
427
+ end
428
+ }
429
+ new = %{
430
+ field :name, String, null: false
431
+
432
+ field :name2, String, null: false
433
+ }
434
+ assert_equal new, upgrade(old)
435
+
379
436
  old = %{
380
437
  field :name, -> { types.String },
381
438
  "newline description" do
@@ -396,6 +453,16 @@ RUBY
396
453
  }
397
454
  assert_equal new, upgrade(old)
398
455
 
456
+ old = %{
457
+ field :name, -> { types.String.to_non_null_type },
458
+ "newline description" do
459
+ end
460
+ }
461
+ new = %{
462
+ field :name, String, "newline description", null: false
463
+ }
464
+ assert_equal new, upgrade(old)
465
+
399
466
  old = %{
400
467
  field :name, String,
401
468
  field: SomeField do
@@ -1,18 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'spec_helper'
3
3
 
4
- if MONGO_DETECTED
5
- require "support/star_trek/data"
6
- require "support/star_trek/schema"
7
- end
8
-
9
4
  describe GraphQL::Relay::MongoRelationConnection do
10
- before do
11
- if !MONGO_DETECTED
12
- skip("Mongo not detected")
13
- end
14
- end
15
-
16
5
  def get_names(result)
17
6
  ships = result["data"]["federation"]["bases"]["edges"]
18
7
  ships.map { |e| e["node"]["name"] }
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require "mongoid" if RUBY_ENGINE == 'ruby'
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require_relative 'spec_helper'
2
3
  require 'ostruct'
3
4
 
4
5
  # platform helper