graphql 1.9.3 → 1.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/graphql.rb +1 -0
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +1 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +3 -2
- data/lib/graphql/execution/execute.rb +10 -6
- data/lib/graphql/execution/interpreter/runtime.rb +23 -12
- data/lib/graphql/execution/lookahead.rb +0 -4
- data/lib/graphql/integer_encoding_error.rb +9 -1
- data/lib/graphql/language/visitor.rb +6 -0
- data/lib/graphql/load_application_object_failed_error.rb +22 -0
- data/lib/graphql/query.rb +6 -1
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/variables.rb +14 -8
- data/lib/graphql/rake_task/validate.rb +1 -1
- data/lib/graphql/schema/field_extension.rb +2 -2
- data/lib/graphql/schema/input_object.rb +15 -6
- data/lib/graphql/schema/member/has_arguments.rb +80 -0
- data/lib/graphql/schema/member/instrumentation.rb +6 -0
- data/lib/graphql/schema/resolver.rb +8 -92
- data/lib/graphql/static_validation/base_visitor.rb +8 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/tracing/data_dog_tracing.rb +19 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -1
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/types/big_int.rb +19 -0
- data/lib/graphql/types/int.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/authorization_spec.rb +26 -0
- data/spec/graphql/language/visitor_spec.rb +19 -0
- data/spec/graphql/schema/argument_spec.rb +26 -1
- data/spec/graphql/schema/field_extension_spec.rb +24 -2
- data/spec/graphql/schema/input_object_spec.rb +119 -10
- data/spec/graphql/schema/introspection_system_spec.rb +9 -0
- data/spec/graphql/schema/resolver_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +16 -2
- data/spec/graphql/subscriptions_spec.rb +38 -0
- data/spec/graphql/tracing/new_relic_tracing_spec.rb +21 -0
- data/spec/graphql/types/big_int_spec.rb +24 -0
- data/spec/support/jazz.rb +11 -0
- metadata +13 -51
- data/spec/dummy/Gemfile.lock +0 -157
- data/spec/dummy/log/test.log +0 -199
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4w/4wzXRZrAkwKdgYaSE0pid5eB-fer8vSfSku_NPg4rMA.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7I/7IHVBiJT06QSpgLpLoJIxboQ0B-D_tMTxsvoezBTV3Q.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8w/8wY_SKagj8wHuwGNAAf6JnQ8joMbC6cEYpHrTAI8Urc.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/AK/AKzz1u6bGb4auXcrObA_g5LL-oV0ejNGa448AgAi_WQ.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ET/ETW4uxvaYpruL8y6_ZptUH82ZowMaHIqvg5WexBFdEM.cache +0 -3
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F1/F1TWpjjyA56k9Z90n5B3xRn7DUdGjX73QCkYC6k07JQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -2
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -2
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ms/MsKSimH_UCB-H1tLvDABDHuvGciuoW6kVqQWDrXU5FQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mt/Mtci-Kim50aPOmeClD4AIicKn1d1WJ0n454IjSd94sk.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QH/QHt3Tc1Y6M66Oo_pDuMyWrQNs4Pp3SMeZR5K1wJj2Ts.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/XU/XU4k1OXnfMils5SrirorPvDSyDSqiOWLZNtmAH1HH8k.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ZI/ZIof7mZxWWCnraIFOCuV6a8QRWzKJXJnx2Xd7C0ZyX0.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cG/cGc_puuPS5pZKgUcy1Y_i1L6jl5UtsiIrMH59rTzR6c.cache +0 -3
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/df/dfro_B6bx3KP1Go-7jEOqqZ2j4hVRseXIc3es9PKQno.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/jO/jO1DfbqnG0mTULsjJJANc3fefrG2zt7DIMmcptMT628.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pE/pE7gO6pQ-z187Swb4hT554wmqsq-cNzgPWLrCz-LQQQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/r9/r9iU1l58a6rxkZSW5RSC52_tD-_UQuHxoMVnkfJ7Mhs.cache +0 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xi/xitPPFfPIyDMpaznV0sBBcw8eSCV8PJcLLWin78sCgE.cache +0 -0
- data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
- 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::
|
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::
|
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",
|
data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb
CHANGED
@@ -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
|