graphql 1.9.3 → 1.9.4

Sign up to get free protection for your applications and to get access to all the features.
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