graphql 1.9.3 → 1.9.4

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.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/lib/graphql.rb +1 -0
  3. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  4. data/lib/graphql/analysis/ast/max_query_complexity.rb +1 -1
  5. data/lib/graphql/analysis/ast/query_complexity.rb +3 -2
  6. data/lib/graphql/execution/execute.rb +10 -6
  7. data/lib/graphql/execution/interpreter/runtime.rb +23 -12
  8. data/lib/graphql/execution/lookahead.rb +0 -4
  9. data/lib/graphql/integer_encoding_error.rb +9 -1
  10. data/lib/graphql/language/visitor.rb +6 -0
  11. data/lib/graphql/load_application_object_failed_error.rb +22 -0
  12. data/lib/graphql/query.rb +6 -1
  13. data/lib/graphql/query/arguments.rb +1 -1
  14. data/lib/graphql/query/variables.rb +14 -8
  15. data/lib/graphql/rake_task/validate.rb +1 -1
  16. data/lib/graphql/schema/field_extension.rb +2 -2
  17. data/lib/graphql/schema/input_object.rb +15 -6
  18. data/lib/graphql/schema/member/has_arguments.rb +80 -0
  19. data/lib/graphql/schema/member/instrumentation.rb +6 -0
  20. data/lib/graphql/schema/resolver.rb +8 -92
  21. data/lib/graphql/static_validation/base_visitor.rb +8 -0
  22. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  23. data/lib/graphql/tracing.rb +2 -2
  24. data/lib/graphql/tracing/data_dog_tracing.rb +19 -0
  25. data/lib/graphql/tracing/platform_tracing.rb +2 -1
  26. data/lib/graphql/types.rb +1 -0
  27. data/lib/graphql/types/big_int.rb +19 -0
  28. data/lib/graphql/types/int.rb +1 -0
  29. data/lib/graphql/version.rb +1 -1
  30. data/spec/graphql/authorization_spec.rb +26 -0
  31. data/spec/graphql/language/visitor_spec.rb +19 -0
  32. data/spec/graphql/schema/argument_spec.rb +26 -1
  33. data/spec/graphql/schema/field_extension_spec.rb +24 -2
  34. data/spec/graphql/schema/input_object_spec.rb +119 -10
  35. data/spec/graphql/schema/introspection_system_spec.rb +9 -0
  36. data/spec/graphql/schema/resolver_spec.rb +2 -2
  37. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +2 -2
  38. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +1 -1
  39. data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +16 -2
  40. data/spec/graphql/subscriptions_spec.rb +38 -0
  41. data/spec/graphql/tracing/new_relic_tracing_spec.rb +21 -0
  42. data/spec/graphql/types/big_int_spec.rb +24 -0
  43. data/spec/support/jazz.rb +11 -0
  44. metadata +13 -51
  45. data/spec/dummy/Gemfile.lock +0 -157
  46. data/spec/dummy/log/test.log +0 -199
  47. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4w/4wzXRZrAkwKdgYaSE0pid5eB-fer8vSfSku_NPg4rMA.cache +0 -0
  48. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7I/7IHVBiJT06QSpgLpLoJIxboQ0B-D_tMTxsvoezBTV3Q.cache +0 -1
  49. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8w/8wY_SKagj8wHuwGNAAf6JnQ8joMbC6cEYpHrTAI8Urc.cache +0 -1
  50. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/AK/AKzz1u6bGb4auXcrObA_g5LL-oV0ejNGa448AgAi_WQ.cache +0 -1
  51. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ET/ETW4uxvaYpruL8y6_ZptUH82ZowMaHIqvg5WexBFdEM.cache +0 -3
  52. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F1/F1TWpjjyA56k9Z90n5B3xRn7DUdGjX73QCkYC6k07JQ.cache +0 -0
  53. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -2
  54. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -2
  55. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ms/MsKSimH_UCB-H1tLvDABDHuvGciuoW6kVqQWDrXU5FQ.cache +0 -0
  56. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mt/Mtci-Kim50aPOmeClD4AIicKn1d1WJ0n454IjSd94sk.cache +0 -0
  57. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QH/QHt3Tc1Y6M66Oo_pDuMyWrQNs4Pp3SMeZR5K1wJj2Ts.cache +0 -1
  58. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/XU/XU4k1OXnfMils5SrirorPvDSyDSqiOWLZNtmAH1HH8k.cache +0 -0
  59. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ZI/ZIof7mZxWWCnraIFOCuV6a8QRWzKJXJnx2Xd7C0ZyX0.cache +0 -1
  60. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cG/cGc_puuPS5pZKgUcy1Y_i1L6jl5UtsiIrMH59rTzR6c.cache +0 -3
  61. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/df/dfro_B6bx3KP1Go-7jEOqqZ2j4hVRseXIc3es9PKQno.cache +0 -1
  62. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/jO/jO1DfbqnG0mTULsjJJANc3fefrG2zt7DIMmcptMT628.cache +0 -1
  63. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pE/pE7gO6pQ-z187Swb4hT554wmqsq-cNzgPWLrCz-LQQQ.cache +0 -0
  64. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/r9/r9iU1l58a6rxkZSW5RSC52_tD-_UQuHxoMVnkfJ7Mhs.cache +0 -1
  65. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xi/xitPPFfPIyDMpaznV0sBBcw8eSCV8PJcLLWin78sCgE.cache +0 -0
  66. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  67. data/spec/integration/tmp/app/graphql/types/family_type.rb +0 -9
@@ -37,9 +37,15 @@ describe GraphQL::Schema::InputObject do
37
37
  argument :arg3, Integer, required: true
38
38
  end
39
39
 
40
+ ensemble_class = Class.new(subclass) do
41
+ argument :ensemble_id, GraphQL::Types::ID, required: false, loads: Jazz::Ensemble
42
+ end
43
+
40
44
  assert_equal 3, subclass.arguments.size
41
45
  assert_equal ["arg1", "arg2", "arg3"], subclass.arguments.keys
42
46
  assert_equal ["String!", "Int!", "Int!"], subclass.arguments.values.map { |a| a.type.to_type_signature }
47
+ assert_equal ["String!", "Int!", "Int!", "ID"], ensemble_class.arguments.values.map { |a| a.type.to_type_signature }
48
+ assert_equal :ensemble, ensemble_class.arguments["ensembleId"].keyword
43
49
  end
44
50
  end
45
51
 
@@ -55,7 +61,7 @@ describe GraphQL::Schema::InputObject do
55
61
  end
56
62
  end
57
63
 
58
- describe "prepare: / as:" do
64
+ describe "prepare: / loads: / as:" do
59
65
  module InputObjectPrepareTest
60
66
  class InputObj < GraphQL::Schema::InputObject
61
67
  argument :a, Integer, required: true
@@ -63,6 +69,8 @@ describe GraphQL::Schema::InputObject do
63
69
  argument :c, Integer, required: true, prepare: :prep
64
70
  argument :d, Integer, required: true, prepare: :prep, as: :d2
65
71
  argument :e, Integer, required: true, prepare: ->(val, ctx) { val * ctx[:multiply_by] * 2 }, as: :e2
72
+ argument :instrument_id, ID, required: true, loads: Jazz::InstrumentType
73
+ argument :danger, Integer, required: false, prepare: ->(val, ctx) { raise GraphQL::ExecutionError.new('boom!') }
66
74
 
67
75
  def prep(val)
68
76
  val * context[:multiply_by]
@@ -70,12 +78,12 @@ describe GraphQL::Schema::InputObject do
70
78
  end
71
79
 
72
80
  class Query < GraphQL::Schema::Object
73
- field :inputs, String, null: false do
81
+ field :inputs, [String], null: false do
74
82
  argument :input, InputObj, required: true
75
83
  end
76
84
 
77
85
  def inputs(input:)
78
- input.to_kwargs.inspect
86
+ [input.to_kwargs.inspect, input.instrument.name]
79
87
  end
80
88
  end
81
89
 
@@ -84,18 +92,115 @@ describe GraphQL::Schema::InputObject do
84
92
  if TESTING_INTERPRETER
85
93
  use GraphQL::Execution::Interpreter
86
94
  end
95
+
96
+ def self.object_from_id(id, ctx)
97
+ Jazz::GloballyIdentifiableType.find(id)
98
+ end
99
+
100
+ def self.resolve_type(type, obj, ctx)
101
+ type
102
+ end
87
103
  end
88
104
  end
89
105
 
90
106
  it "calls methods on the input object" do
91
107
  query_str = <<-GRAPHQL
92
- { inputs(input: { a: 1, b: 2, c: 3, d: 4, e: 5 }) }
108
+ { inputs(input: { a: 1, b: 2, c: 3, d: 4, e: 5, instrumentId: "Instrument/Drum Kit" }) }
93
109
  GRAPHQL
94
110
 
95
111
  res = InputObjectPrepareTest::Schema.execute(query_str, context: { multiply_by: 3 })
96
- expected_obj = { a: 1, b2: 2, c: 9, d2: 12, e2: 30 }.inspect
112
+ expected_obj = [{ a: 1, b2: 2, c: 9, d2: 12, e2: 30, instrument: "Instrument/Drum Kit" }.inspect, "Drum Kit"]
97
113
  assert_equal expected_obj, res["data"]["inputs"]
98
114
  end
115
+
116
+ it "handles exceptions preparing variable input objects" do
117
+ query_str = <<-GRAPHQL
118
+ query($input: InputObj!){ inputs(input: $input) }
119
+ GRAPHQL
120
+
121
+ input = { "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "instrumentId" => "Instrument/Drum Kit", "danger" => 1 }
122
+ res = InputObjectPrepareTest::Schema.execute(query_str, context: { multiply_by: 3 },
123
+ variables: { input: input})
124
+ assert_nil(res["data"])
125
+ assert_equal("Variable input of type InputObj! was provided invalid value", res["errors"][0]["message"])
126
+ assert_equal([{ "line" => 1, "column" => 13 }], res["errors"][0]["locations"])
127
+ assert_equal("boom!", res["errors"][0]["extensions"]["problems"][0]["explanation"])
128
+ assert_equal(input, res["errors"][0]["extensions"]["value"])
129
+ end
130
+ end
131
+
132
+ describe "loading application object(s)" do
133
+ module InputObjectLoadsTest
134
+ class SingleLoadInputObj < GraphQL::Schema::InputObject
135
+ argument :instrument_id, ID, required: true, loads: Jazz::InstrumentType
136
+ end
137
+
138
+ class MultiLoadInputObj < GraphQL::Schema::InputObject
139
+ argument :instrument_ids, [ID], required: true, loads: Jazz::InstrumentType
140
+ end
141
+
142
+ class Query < GraphQL::Schema::Object
143
+ field :single_load_input, Jazz::InstrumentType, null: false do
144
+ argument :input, SingleLoadInputObj, required: true
145
+ end
146
+ field :multi_load_input, [Jazz::InstrumentType], null: false do
147
+ argument :input, MultiLoadInputObj, required: true
148
+ end
149
+
150
+ def single_load_input(input:)
151
+ input.instrument
152
+ end
153
+
154
+ def multi_load_input(input:)
155
+ input.instruments
156
+ end
157
+ end
158
+
159
+ class Schema < GraphQL::Schema
160
+ query(Query)
161
+ if TESTING_INTERPRETER
162
+ use GraphQL::Execution::Interpreter
163
+ end
164
+
165
+ def self.object_from_id(id, ctx)
166
+ Jazz::GloballyIdentifiableType.find(id)
167
+ end
168
+
169
+ def self.resolve_type(type, obj, ctx)
170
+ type
171
+ end
172
+ end
173
+ end
174
+
175
+ let(:single_query_str) {
176
+ <<-GRAPHQL
177
+ query($id: ID!) {
178
+ singleLoadInput(input: {instrumentId: $id}) {
179
+ id
180
+ }
181
+ }
182
+ GRAPHQL
183
+ }
184
+
185
+ let(:multi_query_str) {
186
+ <<-GRAPHQL
187
+ query($ids: [ID!]!) {
188
+ multiLoadInput(input: {instrumentIds: $ids}) {
189
+ id
190
+ }
191
+ }
192
+ GRAPHQL
193
+ }
194
+
195
+ it "loads arguments as objects of the given type and strips `_id` suffix off argument name" do
196
+ res = InputObjectLoadsTest::Schema.execute(single_query_str, variables: { id: "Ensemble/Robert Glasper Experiment" })
197
+ assert_equal "Ensemble/Robert Glasper Experiment", res["data"]["singleLoadInput"]["id"]
198
+ end
199
+
200
+ it "loads arguments as objects of the given type and strips `_ids` suffix off argument name and appends `s`" do
201
+ res = InputObjectLoadsTest::Schema.execute(multi_query_str, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
202
+ assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["multiLoadInput"].map { |e| e["id"] }
203
+ end
99
204
  end
100
205
 
101
206
  describe "in queries" do
@@ -105,7 +210,8 @@ describe GraphQL::Schema::InputObject do
105
210
  inspectInput(input: {
106
211
  stringValue: "ABC",
107
212
  legacyInput: { intValue: 4 },
108
- nestedInput: { stringValue: "xyz"}
213
+ nestedInput: { stringValue: "xyz"},
214
+ ensembleId: "Ensemble/Robert Glasper Experiment"
109
215
  })
110
216
  }
111
217
  GRAPHQL
@@ -118,6 +224,8 @@ describe GraphQL::Schema::InputObject do
118
224
  "ABC",
119
225
  "true",
120
226
  "ABC",
227
+ Jazz::Models::Ensemble.new("Robert Glasper Experiment").to_s,
228
+ "true",
121
229
  ]
122
230
  assert_equal expected_info, res["data"]["inspectInput"]
123
231
  end
@@ -136,6 +244,7 @@ describe GraphQL::Schema::InputObject do
136
244
  graphql_name "TestInput1"
137
245
  argument :d, Int, required: true
138
246
  argument :e, Int, required: true
247
+ argument :instrument_id, ID, required: true, loads: Jazz::InstrumentType
139
248
  end
140
249
 
141
250
  class TestInput2 < GraphQL::Schema::InputObject
@@ -150,7 +259,7 @@ describe GraphQL::Schema::InputObject do
150
259
  end
151
260
 
152
261
  it "returns a symbolized, aliased, ruby keyword style hash" do
153
- arg_values = {a: 1, b: 2, c: { d: 3, e: 4 }}
262
+ arg_values = {a: 1, b: 2, c: { d: 3, e: 4, instrumentId: "Instrument/Drum Kit"}}
154
263
 
155
264
  input_object = InputObjectToHTest::TestInput2.new(
156
265
  arg_values,
@@ -158,7 +267,7 @@ describe GraphQL::Schema::InputObject do
158
267
  defaults_used: Set.new
159
268
  )
160
269
 
161
- assert_equal({ a: 1, b: 2, input_object: { d: 3, e: 4 } }, input_object.to_h)
270
+ assert_equal({ a: 1, b: 2, input_object: { d: 3, e: 4, instrument: "Instrument/Drum Kit" } }, input_object.to_h)
162
271
  end
163
272
  end
164
273
 
@@ -228,11 +337,11 @@ describe GraphQL::Schema::InputObject do
228
337
  }
229
338
  }')
230
339
  # Test __type
231
- assert_equal ["stringValue", "nestedInput", "legacyInput"], res["data"]["__type"]["inputFields"].map { |f| f["name"] }
340
+ assert_equal ["ensembleId", "stringValue", "nestedInput", "legacyInput"], res["data"]["__type"]["inputFields"].map { |f| f["name"] }
232
341
  # Test __schema { types }
233
342
  # It's upcased to test custom introspection
234
343
  input_type = res["data"]["__schema"]["types"].find { |t| t["name"] == "INSPECTABLEINPUT" }
235
- assert_equal ["stringValue", "nestedInput", "legacyInput"], input_type["inputFields"].map { |f| f["name"] }
344
+ assert_equal ["ensembleId", "stringValue", "nestedInput", "legacyInput"], input_type["inputFields"].map { |f| f["name"] }
236
345
  end
237
346
  end
238
347
  end
@@ -18,6 +18,15 @@ describe GraphQL::Schema::IntrospectionSystem do
18
18
  assert_equal "Set", res["data"]["__classname"]
19
19
  end
20
20
 
21
+ it "calls authorization methods of those types" do
22
+ res = Jazz::Schema.execute(%|{ __type(name: "Ensemble") { name } }|)
23
+ assert_equal "ENSEMBLE", res["data"]["__type"]["name"]
24
+
25
+ unauth_res = Jazz::Schema.execute(%|{ __type(name: "Ensemble") { name } }|, context: { cant_introspect: true })
26
+ assert_nil unauth_res["data"].fetch("__type")
27
+ assert_equal ["You're not allowed to introspect here"], unauth_res["errors"].map { |e| e["message"] }
28
+ end
29
+
21
30
  it "serves custom dynamic fields" do
22
31
  res = Jazz::Schema.execute("{ nowPlaying { __typename __typenameLength __astNodeClass } }")
23
32
  assert_equal "Ensemble", res["data"]["nowPlaying"]["__typename"]
@@ -485,7 +485,7 @@ describe GraphQL::Schema::Resolver do
485
485
 
486
486
  res = exec_query(query_str)
487
487
  assert_nil res["data"].fetch("resolverWithErrorHandler")
488
- expected_err = "ResolverWithErrorHandler failed for id: \"failed_to_find\" (nil) (GraphQL::Schema::Resolver::LoadApplicationObjectFailedError)"
488
+ expected_err = "ResolverWithErrorHandler failed for id: \"failed_to_find\" (nil) (GraphQL::LoadApplicationObjectFailedError)"
489
489
  assert_equal [expected_err], res["errors"].map { |e| e["message"] }
490
490
  end
491
491
  end
@@ -500,7 +500,7 @@ describe GraphQL::Schema::Resolver do
500
500
 
501
501
  res = exec_query(query_str)
502
502
  assert_nil res["data"].fetch("resolverWithErrorHandler")
503
- expected_err = "ResolverWithErrorHandler failed for id: \"resolve_type_as_wrong_type\" (:resolve_type_as_wrong_type) (GraphQL::Schema::Resolver::LoadApplicationObjectFailedError)"
503
+ expected_err = "ResolverWithErrorHandler failed for id: \"resolve_type_as_wrong_type\" (:resolve_type_as_wrong_type) (GraphQL::LoadApplicationObjectFailedError)"
504
504
  assert_equal [expected_err], res["errors"].map { |e| e["message"] }
505
505
  end
506
506
  end
@@ -47,7 +47,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
47
47
  input_object_field_error = {
48
48
  "message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value. Expected type 'DairyAnimal!'.",
49
49
  "locations"=>[{"line"=>6, "column"=>39}],
50
- "path"=>["query getCheese", "badSource", "product", "source"],
50
+ "path"=>["query getCheese", "badSource", "product", 0, "source"],
51
51
  "extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"source"},
52
52
  }
53
53
  assert_includes(errors, input_object_field_error)
@@ -92,7 +92,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
92
92
  input_object_field_error = {
93
93
  "message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value. Expected type 'DairyAnimal!'.",
94
94
  "locations"=>[{"line"=>6, "column"=>39}],
95
- "path"=>["query getCheese", "badSource", "product", "source"],
95
+ "path"=>["query getCheese", "badSource", "product", 0, "source"],
96
96
  "extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"source"},
97
97
  }
98
98
  assert_includes(errors, input_object_field_error)
@@ -41,7 +41,7 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
41
41
  input_obj_record = {
42
42
  "message"=>"InputObject 'DairyProductInput' doesn't accept argument 'wacky'",
43
43
  "locations"=>[{"line"=>5, "column"=>30}],
44
- "path"=>["query getCheese", "searchDairy", "product", "wacky"],
44
+ "path"=>["query getCheese", "searchDairy", "product", 0, "wacky"],
45
45
  "extensions"=>{
46
46
  "code"=>"argumentNotAccepted",
47
47
  "name"=>"DairyProductInput",
@@ -13,6 +13,7 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
13
13
  badSource: searchDairy(product: [{source: 1.1}]) { __typename }
14
14
  missingSource: searchDairy(product: [{fatContent: 1.1}]) { __typename }
15
15
  missingNestedRequiredInputObjectAttribute: searchDairy(product: [{fatContent: 1.2, order_by: {}}]) { __typename }
16
+ errorAtIndexOne: searchDairy(product: [{source: COW, fatContent: 1.0}, {fatContent: 1.2, order_by: {}}]) { __typename }
16
17
  listCoerce: cheese(id: 1) { similarCheese(source: YAK) { __typename } }
17
18
  missingInputField: searchDairy(product: [{source: YAK, wacky: 1}]) { __typename }
18
19
  }
@@ -35,7 +36,7 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
35
36
  missing_source_error = {
36
37
  "message"=>"Argument 'source' on InputObject 'DairyProductInput' is required. Expected type DairyAnimal!",
37
38
  "locations"=>[{"line"=>7, "column"=>44}],
38
- "path"=>["query getCheese", "missingSource", "product", "source"],
39
+ "path"=>["query getCheese", "missingSource", "product", 0, "source"],
39
40
  "extensions"=>{
40
41
  "code"=>"missingRequiredInputObjectAttribute",
41
42
  "argumentName"=>"source",
@@ -46,7 +47,18 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
46
47
  missing_order_by_direction_error = {
47
48
  "message"=>"Argument 'direction' on InputObject 'ResourceOrderType' is required. Expected type String!",
48
49
  "locations"=>[{"line"=>8, "column"=>100}],
49
- "path"=>["query getCheese", "missingNestedRequiredInputObjectAttribute", "product", "order_by", "direction"],
50
+ "path"=>["query getCheese", "missingNestedRequiredInputObjectAttribute", "product", 0, "order_by", "direction"],
51
+ "extensions"=>{
52
+ "code"=>"missingRequiredInputObjectAttribute",
53
+ "argumentName"=>"direction",
54
+ "argumentType"=>"String!",
55
+ "inputObjectType"=>"ResourceOrderType"
56
+ }
57
+ }
58
+ missing_order_by_direction_index_one_error = {
59
+ "message"=>"Argument 'direction' on InputObject 'ResourceOrderType' is required. Expected type String!",
60
+ "locations"=>[{"line"=>9, "column"=>106}],
61
+ "path"=>["query getCheese", "errorAtIndexOne", "product", 1, "order_by", "direction"],
50
62
  "extensions"=>{
51
63
  "code"=>"missingRequiredInputObjectAttribute",
52
64
  "argumentName"=>"direction",
@@ -58,6 +70,7 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
58
70
  without_error_bubbling(schema) do
59
71
  assert_includes(errors, missing_source_error)
60
72
  assert_includes(errors, missing_order_by_direction_error)
73
+ assert_includes(errors, missing_order_by_direction_index_one_error)
61
74
  refute_includes(errors, missing_required_field_error)
62
75
  end
63
76
  end
@@ -66,6 +79,7 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
66
79
  assert_includes(errors, missing_required_field_error)
67
80
  assert_includes(errors, missing_source_error)
68
81
  assert_includes(errors, missing_order_by_direction_error)
82
+ assert_includes(errors, missing_order_by_direction_index_one_error)
69
83
  end
70
84
  end
71
85
  end
@@ -268,6 +268,44 @@ describe GraphQL::Subscriptions do
268
268
  end
269
269
  end
270
270
 
271
+ describe "passing a document into #execute" do
272
+ it "sends the updated data" do
273
+ query_str = <<-GRAPHQL
274
+ subscription ($id: ID!){
275
+ payload(id: $id) { str, int }
276
+ }
277
+ GRAPHQL
278
+
279
+ document = GraphQL.parse(query_str)
280
+
281
+ # Initial subscriptions
282
+ response = schema.execute(nil, document: document, context: { socket: "1" }, variables: { "id" => "100" }, root_value: root_object)
283
+
284
+ # This difference is because of how `SKIP` is handled.
285
+ # Honestly the new way is probably better, since it puts a value there.
286
+ empty_response = if TESTING_INTERPRETER && schema == ClassBasedInMemoryBackend::Schema
287
+ {}
288
+ else
289
+ nil
290
+ end
291
+
292
+ # Initial response is nil, no broadcasts yet
293
+ assert_equal(empty_response, response["data"])
294
+ assert_equal [], deliveries["1"]
295
+
296
+ # Application stuff happens.
297
+ # The application signals graphql via `subscriptions.trigger`:
298
+ schema.subscriptions.trigger(:payload, {"id" => "100"}, root_object.payload)
299
+ # Symobls are OK too
300
+ schema.subscriptions.trigger(:payload, {:id => "100"}, root_object.payload)
301
+ schema.subscriptions.trigger("payload", {"id" => "300"}, nil)
302
+
303
+ # Let's see what GraphQL sent over the wire:
304
+ assert_equal({"str" => "Update", "int" => 1}, deliveries["1"][0]["data"]["payload"])
305
+ assert_equal({"str" => "Update", "int" => 2}, deliveries["1"][1]["data"]["payload"])
306
+ end
307
+ end
308
+
271
309
  describe "subscribing" do
272
310
  it "doesn't call the subscriptions for invalid queries" do
273
311
  query_str = <<-GRAPHQL
@@ -3,7 +3,13 @@ require "spec_helper"
3
3
 
4
4
  describe GraphQL::Tracing::NewRelicTracing do
5
5
  module NewRelicTest
6
+ class Thing < GraphQL::Schema::Object
7
+ implements GraphQL::Types::Relay::Node
8
+ end
9
+
6
10
  class Query < GraphQL::Schema::Object
11
+ add_field GraphQL::Types::Relay::NodeField
12
+
7
13
  field :int, Integer, null: false
8
14
 
9
15
  def int
@@ -14,6 +20,16 @@ describe GraphQL::Tracing::NewRelicTracing do
14
20
  class SchemaWithoutTransactionName < GraphQL::Schema
15
21
  query(Query)
16
22
  use(GraphQL::Tracing::NewRelicTracing)
23
+ orphan_types(Thing)
24
+
25
+ def self.object_from_id(_id, _ctx)
26
+ :thing
27
+ end
28
+
29
+ def self.resolve_type(_type, _obj, _ctx)
30
+ Thing
31
+ end
32
+
17
33
  if TESTING_INTERPRETER
18
34
  use GraphQL::Execution::Interpreter
19
35
  end
@@ -37,6 +53,11 @@ describe GraphQL::Tracing::NewRelicTracing do
37
53
  NewRelic.clear_all
38
54
  end
39
55
 
56
+ it "works with the built-in node field, even though it doesn't have an @owner" do
57
+ res = NewRelicTest::SchemaWithoutTransactionName.execute '{ node(id: "1") { __typename } }'
58
+ assert_equal "Thing", res["data"]["node"]["__typename"]
59
+ end
60
+
40
61
  it "can leave the transaction name in place" do
41
62
  NewRelicTest::SchemaWithoutTransactionName.execute "query X { int }"
42
63
  assert_equal [], NewRelic::TRANSACTION_NAMES
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Types::BigInt do
5
+ it "encodes big and small integers as strings" do
6
+ big_integer_1 = 99**99
7
+ expected_str_1 = "369729637649726772657187905628805440595668764281741102430259972423552570455277523421410650010128232727940978889548326540119429996769494359451621570193644014418071060667659301384999779999159200499899"
8
+ assert_equal expected_str_1, GraphQL::Types::BigInt.coerce_result(big_integer_1, nil)
9
+ assert_equal big_integer_1, GraphQL::Types::BigInt.coerce_input(expected_str_1, nil)
10
+
11
+ big_integer_2 = -(88**88)
12
+ expected_str_2 = "-1301592834942972055182648307417315364538725075960067827915311484722452340966317215805106820959190833309704934346517741237438752456673499160125624414995891111204155079786496"
13
+ assert_equal expected_str_2, GraphQL::Types::BigInt.coerce_result(big_integer_2, nil)
14
+ assert_equal big_integer_2, GraphQL::Types::BigInt.coerce_input(expected_str_2, nil)
15
+
16
+ assert_equal "31", GraphQL::Types::BigInt.coerce_result(31, nil)
17
+ assert_equal -17, GraphQL::Types::BigInt.coerce_input("-17", nil)
18
+ end
19
+
20
+ it "returns `nil` for invalid inputs" do
21
+ assert_equal nil, GraphQL::Types::BigInt.coerce_input("xyz", nil)
22
+ assert_equal nil, GraphQL::Types::BigInt.coerce_input("2.2", nil)
23
+ end
24
+ end