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.
- 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
|