graphql 1.8.0.pre10 → 1.8.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/install_generator.rb +14 -8
  3. data/lib/graphql.rb +1 -24
  4. data/lib/graphql/backtrace.rb +1 -1
  5. data/lib/graphql/deprecated_dsl.rb +2 -2
  6. data/lib/graphql/execution/execute.rb +6 -0
  7. data/lib/graphql/execution/lazy/lazy_method_map.rb +1 -1
  8. data/lib/graphql/introspection/base_object.rb +1 -0
  9. data/lib/graphql/language/document_from_schema_definition.rb +4 -1
  10. data/lib/graphql/language/nodes.rb +5 -2
  11. data/lib/graphql/language/parser.rb +288 -288
  12. data/lib/graphql/language/parser.y +1 -1
  13. data/lib/graphql/language/printer.rb +12 -2
  14. data/lib/graphql/non_null_type.rb +1 -1
  15. data/lib/graphql/query.rb +1 -1
  16. data/lib/graphql/query/arguments.rb +1 -1
  17. data/lib/graphql/query/context.rb +2 -2
  18. data/lib/graphql/query/null_context.rb +1 -1
  19. data/lib/graphql/query/result.rb +1 -1
  20. data/lib/graphql/query/variables.rb +21 -3
  21. data/lib/graphql/relay.rb +1 -0
  22. data/lib/graphql/relay/mongo_relation_connection.rb +40 -0
  23. data/lib/graphql/scalar_type.rb +14 -2
  24. data/lib/graphql/schema.rb +17 -1
  25. data/lib/graphql/schema/argument.rb +39 -7
  26. data/lib/graphql/schema/enum.rb +7 -0
  27. data/lib/graphql/schema/field.rb +145 -39
  28. data/lib/graphql/schema/finder.rb +4 -4
  29. data/lib/graphql/schema/input_object.rb +13 -2
  30. data/lib/graphql/schema/interface.rb +50 -16
  31. data/lib/graphql/schema/list.rb +28 -0
  32. data/lib/graphql/schema/member.rb +8 -106
  33. data/lib/graphql/schema/member/accepts_definition.rb +58 -24
  34. data/lib/graphql/schema/member/base_dsl_methods.rb +96 -0
  35. data/lib/graphql/schema/member/build_type.rb +15 -9
  36. data/lib/graphql/schema/member/cached_graphql_definition.rb +26 -0
  37. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  38. data/lib/graphql/schema/member/has_arguments.rb +1 -1
  39. data/lib/graphql/schema/member/has_fields.rb +91 -8
  40. data/lib/graphql/schema/member/type_system_helpers.rb +34 -0
  41. data/lib/graphql/schema/middleware_chain.rb +5 -1
  42. data/lib/graphql/schema/mutation.rb +24 -12
  43. data/lib/graphql/schema/non_null.rb +34 -0
  44. data/lib/graphql/schema/object.rb +24 -11
  45. data/lib/graphql/schema/relay_classic_mutation.rb +14 -11
  46. data/lib/graphql/schema/rescue_middleware.rb +8 -7
  47. data/lib/graphql/schema/scalar.rb +9 -2
  48. data/lib/graphql/schema/union.rb +4 -0
  49. data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
  50. data/lib/graphql/static_validation/literal_validator.rb +16 -4
  51. data/lib/graphql/static_validation/validation_context.rb +1 -1
  52. data/lib/graphql/subscriptions.rb +90 -16
  53. data/lib/graphql/upgrader/member.rb +27 -89
  54. data/lib/graphql/version.rb +1 -1
  55. data/spec/dummy/app/channels/graphql_channel.rb +1 -1
  56. data/spec/dummy/log/test.log +206 -0
  57. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-x/-xYZjAnuuzgR79fcznLTQtSdh6AARxu8FcQ_J6p7L3U.cache +0 -0
  58. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/13/13HiV12xyoQvT-1L39ZzLwMZxjyaGMiENmfw7f-QTIc.cache +0 -0
  59. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3W/3Wtf5pCWdqq0AB-iB0Y9uUNrTkruRxIEf1XFn_BETU0.cache +1 -0
  60. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5i/5iguGafb4hOn8262Kn8Q37ogNN9MxxQKGKNzHAzUcvI.cache +1 -0
  61. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8m/8mj2T6yy847Mc2Z7k3Xzh8O91hhVJt3NrPe8ASNDlIA.cache +1 -0
  62. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DT/DTQyMpr4ABZYQetsdRJ5A7S4jf1r3ie4FGOR7GZBNSs.cache +3 -0
  63. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Dq/DqJ5_yJPrP5iLlOQyTQsjAVI5FE5LCVDkED0f7GgsSo.cache +3 -0
  64. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -0
  65. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -0
  66. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rw/RwDuCV-XpnCtjNkvhpJfBuxXMk0b5AD3L9eR6M-wcy0.cache +3 -0
  67. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/UL/ULdjhhb0bRuqmaG7XSZlFYzGYCXTDnqZuJBTWRlzqgw.cache +0 -0
  68. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Up/UpPNgh0yYoUsyMDh5zWqe_U6qJIyTC6-dxMMAs1vvlM.cache +1 -0
  69. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wg/Wguh-szFGTI1gaL6npYwPekMXflugRei7F_mOyRucXg.cache +0 -0
  70. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/X-/X-khLYMA9mqFRPg3zAi86mREDxpKl4bdKYp3uF6WHos.cache +0 -0
  71. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bi/BIkdhfxsezxM4q-HZ4oCNTq97WEJTigcq0tpX2cDvbY.cache +0 -0
  72. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ff/FfxmA4CMHQZT7exx0G7NS1Wpcnny0vzp-Jhc2H36bp8.cache +1 -0
  73. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gE/gEiiG4GZNy_djEjK2pHm_NgA-gyhLZhdQvo0Yt96GqE.cache +0 -0
  74. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gn/gnA9ZSqpjccNL2m8pe_jBvY6SinXlCzXDWyop83Od8s.cache +1 -0
  75. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lO/lOAan3cMwCE_Hli6gsDML88xFNfn0nxPmvrSkW7eEOw.cache +1 -0
  76. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m1/M1pv8MJEPLXGLvS8QxVh3DSO9cI4mRt5FHFWdrvUj6o.cache +2 -0
  77. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m7/m77qH7ZqH0_0SmwJbiKGDd-aLau1Dav847DC6ge46zY.cache +1 -0
  78. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sj/sjRjnjRB37lH2vrgtkdJ8Cz84__IJ978IuKTM7HcztI.cache +0 -0
  79. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/um/um1JrirR4hJhK-1rE-HywlyCi5ibgxHVrReiujZBWJM.cache +1 -0
  80. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v4/v4fwVytD7ITcE0_GDbslZEYud8a5Okm85fV1o7SDl6g.cache +0 -0
  81. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v_/v_0PAQt0iipQjFP5zjgkkk9Stnpf4VzvnMv67d1Keuw.cache +1 -0
  82. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wd/wdT9U4MKxe1PyqNjVuCKMpCl3dxGCIRJIlwUTfh2DQU.cache +1 -0
  83. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xI/xIaxut_fEIhKBDqljTNwYaADK9kj3gG0ESrfHs-5_og.cache +3 -0
  84. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/y0/y0SJOqIx2fn1SKqOkAihsQow0trRJrSIyAswufVuoA8.cache +0 -0
  85. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zg/zgpzeaX-KZErHyGJ1aBH3ZusweNXMneVZule88XsIJI.cache +1 -0
  86. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zy/zYFltDy-8VC-uKq2BVEiJJyYXNFvVzAKuMlR3ZIYZsk.cache +0 -0
  87. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  88. data/spec/fixtures/upgrader/photo.original.rb +10 -0
  89. data/spec/fixtures/upgrader/photo.transformed.rb +12 -0
  90. data/spec/fixtures/upgrader/starrable.original.rb +4 -1
  91. data/spec/fixtures/upgrader/starrable.transformed.rb +25 -22
  92. data/spec/fixtures/upgrader/subscribable.transformed.rb +32 -33
  93. data/spec/graphql/execution_error_spec.rb +18 -0
  94. data/spec/graphql/introspection/schema_type_spec.rb +1 -0
  95. data/spec/graphql/object_type_spec.rb +2 -1
  96. data/spec/graphql/query/variables_spec.rb +41 -0
  97. data/spec/graphql/relay/mongo_relation_connection_spec.rb +474 -0
  98. data/spec/graphql/schema/argument_spec.rb +65 -9
  99. data/spec/graphql/schema/build_from_definition_spec.rb +4 -2
  100. data/spec/graphql/schema/enum_spec.rb +1 -1
  101. data/spec/graphql/schema/field_spec.rb +79 -4
  102. data/spec/graphql/schema/input_object_spec.rb +56 -0
  103. data/spec/graphql/schema/instrumentation_spec.rb +4 -3
  104. data/spec/graphql/schema/interface_spec.rb +40 -25
  105. data/spec/graphql/schema/member/accepts_definition_spec.rb +38 -11
  106. data/spec/graphql/schema/member/has_fields_spec.rb +129 -0
  107. data/spec/graphql/schema/member/type_system_helpers_spec.rb +63 -0
  108. data/spec/graphql/schema/mutation_spec.rb +48 -0
  109. data/spec/graphql/schema/object_spec.rb +34 -8
  110. data/spec/graphql/schema/relay_classic_mutation_spec.rb +10 -0
  111. data/spec/graphql/schema/rescue_middleware_spec.rb +11 -0
  112. data/spec/graphql/schema/scalar_spec.rb +55 -0
  113. data/spec/graphql/subscriptions_spec.rb +31 -4
  114. data/spec/graphql/upgrader/member_spec.rb +14 -6
  115. data/spec/spec_helper.rb +7 -0
  116. data/spec/support/dummy/schema.rb +8 -0
  117. data/spec/support/jazz.rb +89 -19
  118. data/spec/support/star_trek/data.rb +109 -0
  119. data/spec/support/star_trek/schema.rb +388 -0
  120. metadata +80 -7
  121. data/lib/graphql/schema/field/dynamic_resolve.rb +0 -70
  122. data/lib/graphql/schema/field/unwrapped_resolve.rb +0 -20
  123. data/lib/graphql/schema/member/list_type_proxy.rb +0 -25
  124. data/lib/graphql/schema/member/non_null_type_proxy.rb +0 -25
@@ -25,6 +25,17 @@ describe GraphQL::Schema::RescueMiddleware do
25
25
  assert_equal(GraphQL::ExecutionError, result.class)
26
26
  end
27
27
 
28
+ describe "rescue_from superclass" do
29
+ class ChildSpecExampleError < SpecExampleError; end
30
+
31
+ let(:error_class) { ChildSpecExampleError }
32
+ it "handles them as execution errors" do
33
+ result = middleware_chain.invoke([])
34
+ assert_equal("there was an example error: ChildSpecExampleError", result.message)
35
+ assert_equal(GraphQL::ExecutionError, result.class)
36
+ end
37
+ end
38
+
28
39
  describe "with multiple error classes" do
29
40
  let(:error_class) { SecondSpecExampleError }
30
41
  let(:rescue_middleware) do
@@ -36,5 +36,60 @@ describe GraphQL::Schema::Scalar do
36
36
  assert_equal true, key_info["isSharp"]
37
37
  assert_equal false, key_info["isFlat"]
38
38
  end
39
+
40
+ it "can be nested JSON" do
41
+ query_str = <<-GRAPHQL
42
+ {
43
+ echoJson(input: {foo: [{bar: "baz"}]})
44
+ }
45
+ GRAPHQL
46
+
47
+ res = Jazz::Schema.execute(query_str)
48
+ assert_equal({"foo" => [{"bar" => "baz"}]}, res["data"]["echoJson"])
49
+ end
50
+
51
+ it "can be a JSON array" do
52
+ query_str = <<-GRAPHQL
53
+ {
54
+ echoFirstJson(input: [{foo: "bar"}, {baz: "boo"}])
55
+ }
56
+ GRAPHQL
57
+
58
+ res = Jazz::Schema.execute(query_str)
59
+ assert_equal({"foo" => "bar"}, res["data"]["echoFirstJson"])
60
+ end
61
+
62
+ it "can be a JSON array even if the GraphQL type is not an array" do
63
+ query_str = <<-GRAPHQL
64
+ {
65
+ echoJson(input: [{foo: "bar"}])
66
+ }
67
+ GRAPHQL
68
+
69
+ res = Jazz::Schema.execute(query_str)
70
+ assert_equal([{"foo" => "bar"}], res["data"]["echoJson"])
71
+ end
72
+
73
+ it "can be JSON with a nested enum" do
74
+ query_str = <<-GRAPHQL
75
+ {
76
+ echoJson(input: [{foo: WOODWIND}])
77
+ }
78
+ GRAPHQL
79
+
80
+ res = Jazz::Schema.execute(query_str)
81
+ assert_equal([{"foo" => "WOODWIND"}], res["data"]["echoJson"])
82
+ end
83
+
84
+ it "cannot be JSON with a nested variable" do
85
+ query_str = <<-GRAPHQL
86
+ {
87
+ echoJson(input: [{foo: $var}])
88
+ }
89
+ GRAPHQL
90
+
91
+ res = Jazz::Schema.execute(query_str)
92
+ assert_includes(res["errors"][0]["message"], "Argument 'input' on Field 'echoJson' has an invalid value")
93
+ end
39
94
  end
40
95
  end
@@ -192,10 +192,10 @@ describe GraphQL::Subscriptions do
192
192
 
193
193
  # Application stuff happens.
194
194
  # The application signals graphql via `subscriptions.trigger`:
195
- schema.subscriptions.trigger("payload", {"id" => "100"}, root_object.payload)
195
+ schema.subscriptions.trigger(:payload, {"id" => "100"}, root_object.payload)
196
196
  schema.subscriptions.trigger("payload", {"id" => "200"}, root_object.payload)
197
197
  # Symobls are OK too
198
- schema.subscriptions.trigger("payload", {:id => "100"}, root_object.payload)
198
+ schema.subscriptions.trigger(:payload, {:id => "100"}, root_object.payload)
199
199
  schema.subscriptions.trigger("payload", {"id" => "300"}, nil)
200
200
 
201
201
  # Let's see what GraphQL sent over the wire:
@@ -267,8 +267,8 @@ describe GraphQL::Subscriptions do
267
267
  schema.subscriptions.trigger("event", { "stream" => {"type" => "ONE", "userId" => "3"} }, OpenStruct.new(str: "", int: 2))
268
268
  # This is a non-trigger
269
269
  schema.subscriptions.trigger("event", { "stream" => {"userId" => "3", "type" => "TWO"} }, OpenStruct.new(str: "", int: 3))
270
- # These get default value of ONE
271
- schema.subscriptions.trigger("event", { "stream" => {"userId" => "3"} }, OpenStruct.new(str: "", int: 4))
270
+ # These get default value of ONE (underscored / symbols are ok)
271
+ schema.subscriptions.trigger("event", { stream: { user_id: "3"} }, OpenStruct.new(str: "", int: 4))
272
272
  # Trigger with null updates subscriptionss to null
273
273
  schema.subscriptions.trigger("event", { "stream" => {"userId" => 3, "type" => nil} }, OpenStruct.new(str: "", int: 5))
274
274
 
@@ -391,4 +391,31 @@ describe GraphQL::Subscriptions do
391
391
  refute_equal schema.subscriptions.build_id, schema.subscriptions.build_id
392
392
  end
393
393
  end
394
+
395
+ describe ".trigger" do
396
+ it "raises when event name is not found" do
397
+ err = assert_raises(GraphQL::Subscriptions::InvalidTriggerError) do
398
+ schema.subscriptions.trigger(:nonsense_field, {}, nil)
399
+ end
400
+
401
+ assert_includes err.message, "trigger: nonsense_field"
402
+ assert_includes err.message, "Subscription.nonsenseField"
403
+ end
404
+
405
+ it "raises when argument is not found" do
406
+ err = assert_raises(GraphQL::Subscriptions::InvalidTriggerError) do
407
+ schema.subscriptions.trigger(:event, { scream: {"userId" => "😱"} }, nil)
408
+ end
409
+
410
+ assert_includes err.message, "arguments: scream"
411
+ assert_includes err.message, "arguments of Subscription.event"
412
+
413
+ err = assert_raises(GraphQL::Subscriptions::InvalidTriggerError) do
414
+ schema.subscriptions.trigger(:event, { stream: { user_id_number: "😱"} }, nil)
415
+ end
416
+
417
+ assert_includes err.message, "arguments: user_id_number"
418
+ assert_includes err.message, "arguments of StreamInput"
419
+ end
420
+ end
394
421
  end
@@ -111,10 +111,12 @@ describe GraphQL::Upgrader::Member do
111
111
  end
112
112
  }
113
113
  new = %{
114
- class UserInterface < Types::BaseInterface
114
+ module UserInterface
115
+ include Types::BaseInterface
115
116
  graphql_name "User"
116
117
  end
117
118
  }
119
+
118
120
  assert_equal new, upgrade(old)
119
121
 
120
122
  old = %{
@@ -139,10 +141,16 @@ describe GraphQL::Upgrader::Member do
139
141
  end}
140
142
  assert_equal new, upgrade(old)
141
143
 
142
- old = %{UserInterface = GraphQL::InterfaceType.define do
143
- end}
144
- new = %{class UserInterface < Types::BaseInterface
145
- end}
144
+ old = <<-RUBY
145
+ UserInterface = GraphQL::InterfaceType.define do
146
+ end
147
+ RUBY
148
+ new = <<-RUBY
149
+ module UserInterface
150
+ include Types::BaseInterface
151
+ end
152
+ RUBY
153
+
146
154
  assert_equal new, upgrade(old)
147
155
 
148
156
  old = %{UserUnion = GraphQL::UnionType.define do
@@ -474,7 +482,7 @@ describe GraphQL::Upgrader::Member do
474
482
  # Replace the default one with a custom one:
475
483
  type_transforms = GraphQL::Upgrader::Member::DEFAULT_TYPE_TRANSFORMS.map { |t|
476
484
  if t == GraphQL::Upgrader::TypeDefineToClassTransform
477
- GraphQL::Upgrader::TypeDefineToClassTransform.new(base_class_pattern: "Platform::\\2s::Base")
485
+ GraphQL::Upgrader::TypeDefineToClassTransform.new(base_class_pattern: "Platform::\\3s::Base")
478
486
  else
479
487
  t
480
488
  end
data/spec/spec_helper.rb CHANGED
@@ -16,6 +16,7 @@ if rails_should_be_installed?
16
16
  require "jdbc/sqlite3" if RUBY_ENGINE == 'jruby'
17
17
  require "sqlite3" if RUBY_ENGINE == 'ruby'
18
18
  require "pg" if RUBY_ENGINE == 'ruby'
19
+ require "mongoid" if RUBY_ENGINE == 'ruby'
19
20
  require "sequel"
20
21
  end
21
22
 
@@ -40,6 +41,7 @@ assign_metadata_key = ->(target, key, value) { target.metadata[key] = value }
40
41
  assign_metadata_flag = ->(target, flag) { target.metadata[flag] = true }
41
42
  GraphQL::Schema.accepts_definitions(set_metadata: assign_metadata_key)
42
43
  GraphQL::BaseType.accepts_definitions(metadata: assign_metadata_key)
44
+ GraphQL::BaseType.accepts_definitions(metadata2: assign_metadata_key)
43
45
  GraphQL::Field.accepts_definitions(metadata: assign_metadata_key)
44
46
  GraphQL::Argument.accepts_definitions(metadata: assign_metadata_key)
45
47
  GraphQL::Argument.accepts_definitions(metadata_flag: assign_metadata_flag)
@@ -62,11 +64,16 @@ NO_OP_RESOLVE_TYPE = ->(type, obj, ctx) {
62
64
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |f|
63
65
  unless rails_should_be_installed?
64
66
  next if f.end_with?('star_wars/data.rb')
67
+ next if f.end_with?('star_trek/data.rb')
65
68
  next if f.end_with?('base_generator_test.rb')
66
69
  end
67
70
  require f
68
71
  end
69
72
 
73
+ def star_trek_query(string, variables={}, context: {})
74
+ StarTrek::Schema.execute(string, variables: variables, context: context)
75
+ end
76
+
70
77
  def star_wars_query(string, variables={}, context: {})
71
78
  StarWars::Schema.execute(string, variables: variables, context: context)
72
79
  end
@@ -366,6 +366,14 @@ module Dummy
366
366
  }
367
367
  end
368
368
 
369
+ field :multipleErrorsOnNonNullableField do
370
+ type !GraphQL::STRING_TYPE
371
+ resolve ->(t, a, c) {
372
+ [GraphQL::ExecutionError.new("This is an error message for some error."),
373
+ GraphQL::ExecutionError.new("This is another error message for a different error.")]
374
+ }
375
+ end
376
+
369
377
  field :executionErrorWithOptions do
370
378
  type GraphQL::INT_TYPE
371
379
  resolve ->(t, a, c) {
data/spec/support/jazz.rb CHANGED
@@ -31,6 +31,7 @@ module Jazz
31
31
  ],
32
32
  "Ensemble" => [
33
33
  Models::Ensemble.new("Bela Fleck and the Flecktones"),
34
+ Models::Ensemble.new("Robert Glasper Experiment"),
34
35
  ],
35
36
  "Musician" => [
36
37
  Models::Musician.new("Herbie Hancock", Models::Key.from_notation("B♭")),
@@ -64,15 +65,13 @@ module Jazz
64
65
  super(*args, **options, &block)
65
66
  end
66
67
 
67
- def to_graphql
68
- field_defn = super
69
- if @upcase
70
- inner_resolve = field_defn.resolve_proc
71
- field_defn.resolve = ->(obj, args, ctx) {
72
- inner_resolve.call(obj, args, ctx).upcase
73
- }
68
+ def resolve_field(*)
69
+ result = super
70
+ if @upcase && result
71
+ result.upcase
72
+ else
73
+ result
74
74
  end
75
- field_defn
76
75
  end
77
76
  end
78
77
 
@@ -99,9 +98,17 @@ module Jazz
99
98
  end
100
99
  end
101
100
 
102
- class BaseInterface < GraphQL::Schema::Interface
101
+ module BaseInterface
102
+ include GraphQL::Schema::Interface
103
103
  # Use this overridden field class
104
104
  field_class BaseField
105
+
106
+ # These methods are available to child interfaces
107
+ definition_methods do
108
+ def upcased_field(*args, **kwargs, &block)
109
+ field(*args, upcase: true, **kwargs, &block)
110
+ end
111
+ end
105
112
  end
106
113
 
107
114
  class BaseEnumValue < GraphQL::Schema::EnumValue
@@ -123,15 +130,14 @@ module Jazz
123
130
 
124
131
  # Some arbitrary global ID scheme
125
132
  # *Type suffix is removed automatically
126
- class GloballyIdentifiableType < BaseInterface
133
+ module GloballyIdentifiableType
134
+ include BaseInterface
127
135
  description "A fetchable object in the system"
128
136
  field :id, ID, "A unique identifier for this object", null: false
129
- field :upcased_id, ID, null: false, upcase: true, method: :id
137
+ upcased_field :upcased_id, ID, null: false, method: :id # upcase: true added by helper
130
138
 
131
- module Implementation
132
- def id
133
- GloballyIdentifiableType.to_id(@object)
134
- end
139
+ def id
140
+ GloballyIdentifiableType.to_id(@object)
135
141
  end
136
142
 
137
143
  def self.to_id(object)
@@ -160,15 +166,25 @@ module Jazz
160
166
  end
161
167
  end
162
168
 
169
+ module HasMusicians
170
+ include BaseInterface
171
+ field :musicians, "[Jazz::Musician]", null: false
172
+ end
173
+
174
+
163
175
  # Here's a new-style GraphQL type definition
164
176
  class Ensemble < ObjectWithUpcasedName
165
- implements GloballyIdentifiableType, NamedEntity
177
+ # Test string type names
178
+ # This method should override inherited one
179
+ field :name, "String", null: false, method: :overridden_name
180
+ implements GloballyIdentifiableType, NamedEntity, HasMusicians
166
181
  description "A group of musicians playing together"
167
182
  config :config, :configged
168
- # Test string type names:
169
- field :name, "String", null: false
170
- field :musicians, "[Jazz::Musician]", null: false
171
183
  field :formed_at, String, null: true, hash_key: "formedAtDate"
184
+
185
+ def overridden_name
186
+ @object.name.sub("Robert Glasper", "ROBERT GLASPER")
187
+ end
172
188
  end
173
189
 
174
190
  class Family < BaseEnum
@@ -212,6 +228,16 @@ module Jazz
212
228
  end
213
229
  end
214
230
 
231
+ class RawJson < GraphQL::Schema::Scalar
232
+ def self.coerce_input(val, ctx)
233
+ val
234
+ end
235
+
236
+ def self.coerce_result(val, ctx)
237
+ val
238
+ end
239
+ end
240
+
215
241
  class Musician < BaseObject
216
242
  implements GloballyIdentifiableType
217
243
  implements NamedEntity
@@ -279,6 +305,10 @@ module Jazz
279
305
  end
280
306
  end
281
307
 
308
+ class HashKeyTest < BaseObject
309
+ field :falsey, Boolean, null: false
310
+ end
311
+
282
312
  # Another new-style definition, with method overrides
283
313
  class Query < BaseObject
284
314
  field :ensembles, [Ensemble], null: false
@@ -300,6 +330,14 @@ module Jazz
300
330
  field :inspect_context, [String], null: false
301
331
  field :hashyEnsemble, Ensemble, null: false
302
332
 
333
+ field :echo_json, RawJson, null: false do
334
+ argument :input, RawJson, required: true
335
+ end
336
+
337
+ field :echo_first_json, RawJson, null: false do
338
+ argument :input, [RawJson], required: true
339
+ end
340
+
303
341
  def ensembles
304
342
  Models.data["Ensemble"]
305
343
  end
@@ -358,6 +396,24 @@ module Jazz
358
396
  "formedAtDate" => "May 5, 1965",
359
397
  }
360
398
  end
399
+
400
+ def echo_json(input:)
401
+ input
402
+ end
403
+
404
+ def echo_first_json(input:)
405
+ input.first
406
+ end
407
+
408
+ field :hash_by_string, HashKeyTest, null: false
409
+ field :hash_by_sym, HashKeyTest, null: false
410
+ def hash_by_string
411
+ { "falsey" => false }
412
+ end
413
+
414
+ def hash_by_sym
415
+ { falsey: false }
416
+ end
361
417
  end
362
418
 
363
419
  class EnsembleInput < GraphQL::Schema::InputObject
@@ -365,6 +421,7 @@ module Jazz
365
421
  end
366
422
 
367
423
  class AddInstrument < GraphQL::Schema::Mutation
424
+ null true
368
425
  description "Register a new musical instrument in the database"
369
426
 
370
427
  argument :name, String, required: true
@@ -396,6 +453,19 @@ module Jazz
396
453
  Models.data["Ensemble"] << ens
397
454
  ens
398
455
  end
456
+
457
+ field :prepare_input, Integer, null: false do
458
+ argument :input, Integer, required: true, prepare: :square, as: :squared_input
459
+ end
460
+
461
+ def prepare_input(squared_input:)
462
+ # Test that `square` is called
463
+ squared_input
464
+ end
465
+
466
+ def square(value)
467
+ value ** 2
468
+ end
399
469
  end
400
470
 
401
471
  class MetadataPlugin
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+ require 'ostruct'
3
+
4
+ module StarTrek
5
+ names = [
6
+ 'USS Enterprise',
7
+ 'USS Excelsior',
8
+ 'USS Reliant',
9
+ 'IKS Koraga',
10
+ 'IKS Kronos One',
11
+ 'IRW Khazara',
12
+ 'IRW Praetus',
13
+ ]
14
+
15
+ MONGOID_CONFIG = {
16
+ clients: {
17
+ default: {
18
+ database: 'graphql_ruby_test',
19
+ hosts: ['localhost:27017']
20
+ }
21
+ },
22
+ sessions: {
23
+ default: {
24
+ database: 'graphql_ruby_test',
25
+ hosts: ['localhost:27017']
26
+ }
27
+ }
28
+ }.freeze
29
+
30
+ def db_name
31
+ MONGOID_CONFIG[:clients][:default][:database]
32
+ end
33
+ module_function :db_name
34
+
35
+ # Set up "Bases" in MongoDB
36
+ Mongoid.load_configuration(MONGOID_CONFIG)
37
+
38
+ class Base
39
+ include Mongoid::Document
40
+ field :name, type: String
41
+ field :sector, type: String
42
+ field :faction_id, type: Integer
43
+ end
44
+
45
+ Base.collection.drop
46
+ Base.create!(name: "Deep Space Station K-7", sector: "Mempa", faction_id: 1)
47
+ Base.create!(name: "Regula I", sector: "Mutara", faction_id: 1)
48
+ Base.create!(name: "Deep Space Nine", sector: "Bajoran", faction_id: 1)
49
+ Base.create!(name: "Firebase P'ok", sector: nil, faction_id: 2)
50
+ Base.create!(name: "Ganalda Space Station", sector: "Archanis", faction_id: 2)
51
+ Base.create!(name: "Rh'Ihho Station", sector: "Rator", faction_id: 3)
52
+
53
+ class FactionRecord
54
+ attr_reader :id, :name, :ships, :bases, :bases_clone
55
+ def initialize(id:, name:, ships:, bases:, bases_clone:)
56
+ @id = id
57
+ @name = name
58
+ @ships = ships
59
+ @bases = bases
60
+ @bases_clone = bases_clone
61
+ end
62
+ end
63
+
64
+ federation = FactionRecord.new({
65
+ id: '1',
66
+ name: 'United Federation of Planets',
67
+ ships: ['1', '2', '3'],
68
+ bases: Base.where(faction_id: 1),
69
+ bases_clone: Base.where(faction_id: 1),
70
+ })
71
+
72
+ klingon = FactionRecord.new({
73
+ id: '2',
74
+ name: 'Klingon Empire',
75
+ ships: ['4', '5'],
76
+ bases: Base.where(faction_id: 2),
77
+ bases_clone: Base.where(faction_id: 2),
78
+ })
79
+
80
+ romulan = FactionRecord.new({
81
+ id: '2',
82
+ name: 'Romulan Star Empire',
83
+ ships: ['6', '7'],
84
+ bases: Base.where(faction_id: 3),
85
+ bases_clone: Base.where(faction_id: 3),
86
+ })
87
+
88
+ DATA = {
89
+ "Faction" => {
90
+ "1" => federation,
91
+ "2" => klingon,
92
+ "3" => romulan,
93
+ },
94
+ "Ship" => names.each_with_index.reduce({}) do |memo, (name, idx)|
95
+ id = (idx + 1).to_s
96
+ memo[id] = OpenStruct.new(name: name, id: id)
97
+ memo
98
+ end,
99
+ "Base" => Hash.new { |h, k| h[k] = Base.find(k) }
100
+ }
101
+
102
+ def DATA.create_ship(name, faction_id)
103
+ new_id = (self["Ship"].keys.map(&:to_i).max + 1).to_s
104
+ new_ship = OpenStruct.new(id: new_id, name: name)
105
+ self["Ship"][new_id] = new_ship
106
+ self["Faction"][faction_id].ships << new_id
107
+ new_ship
108
+ end
109
+ end