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
@@ -28,6 +28,12 @@ module GraphQL
|
|
28
28
|
wrapper_class = root_type.metadata[:type_class]
|
29
29
|
if wrapper_class
|
30
30
|
new_root_value = wrapper_class.authorized_new(query.root_value, query.context)
|
31
|
+
new_root_value = query.schema.sync_lazy(new_root_value)
|
32
|
+
if new_root_value.nil?
|
33
|
+
# This is definitely a hack,
|
34
|
+
# but we need some way to tell execute.rb not to run.
|
35
|
+
query.context[:__root_unauthorized] = true
|
36
|
+
end
|
31
37
|
query.root_value = new_root_value
|
32
38
|
end
|
33
39
|
end
|
@@ -168,78 +168,6 @@ module GraphQL
|
|
168
168
|
public_send("load_#{name}", value)
|
169
169
|
end
|
170
170
|
|
171
|
-
class LoadApplicationObjectFailedError < GraphQL::ExecutionError
|
172
|
-
# @return [GraphQL::Schema::Argument] the argument definition for the argument that was looked up
|
173
|
-
attr_reader :argument
|
174
|
-
# @return [String] The ID provided by the client
|
175
|
-
attr_reader :id
|
176
|
-
# @return [Object] The value found with this ID
|
177
|
-
attr_reader :object
|
178
|
-
def initialize(argument:, id:, object:)
|
179
|
-
@id = id
|
180
|
-
@argument = argument
|
181
|
-
@object = object
|
182
|
-
super("No object found for `#{argument.graphql_name}: #{id.inspect}`")
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Look up the corresponding object for a provided ID.
|
187
|
-
# By default, it uses Relay-style {Schema.object_from_id},
|
188
|
-
# override this to find objects another way.
|
189
|
-
#
|
190
|
-
# @param type [Class, Module] A GraphQL type definition
|
191
|
-
# @param id [String] A client-provided to look up
|
192
|
-
# @param context [GraphQL::Query::Context] the current context
|
193
|
-
def object_from_id(type, id, context)
|
194
|
-
context.schema.object_from_id(id, context)
|
195
|
-
end
|
196
|
-
|
197
|
-
def load_application_object(arg_kwarg, id)
|
198
|
-
argument = @arguments_by_keyword[arg_kwarg]
|
199
|
-
lookup_as_type = @arguments_loads_as_type[arg_kwarg]
|
200
|
-
# See if any object can be found for this ID
|
201
|
-
loaded_application_object = object_from_id(lookup_as_type, id, context)
|
202
|
-
context.schema.after_lazy(loaded_application_object) do |application_object|
|
203
|
-
if application_object.nil?
|
204
|
-
err = LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
205
|
-
load_application_object_failed(err)
|
206
|
-
end
|
207
|
-
# Double-check that the located object is actually of this type
|
208
|
-
# (Don't want to allow arbitrary access to objects this way)
|
209
|
-
resolved_application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
|
210
|
-
context.schema.after_lazy(resolved_application_object_type) do |application_object_type|
|
211
|
-
possible_object_types = context.schema.possible_types(lookup_as_type)
|
212
|
-
if !possible_object_types.include?(application_object_type)
|
213
|
-
err = LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
214
|
-
load_application_object_failed(err)
|
215
|
-
else
|
216
|
-
# This object was loaded successfully
|
217
|
-
# and resolved to the right type,
|
218
|
-
# now apply the `.authorized?` class method if there is one
|
219
|
-
if (class_based_type = application_object_type.metadata[:type_class])
|
220
|
-
context.schema.after_lazy(class_based_type.authorized?(application_object, context)) do |authed|
|
221
|
-
if authed
|
222
|
-
application_object
|
223
|
-
else
|
224
|
-
raise GraphQL::UnauthorizedError.new(
|
225
|
-
object: application_object,
|
226
|
-
type: class_based_type,
|
227
|
-
context: context,
|
228
|
-
)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
else
|
232
|
-
application_object
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def load_application_object_failed(err)
|
240
|
-
raise err
|
241
|
-
end
|
242
|
-
|
243
171
|
class << self
|
244
172
|
# Default `:resolve` set below.
|
245
173
|
# @return [Symbol] The method to call on instances of this object to resolve the field
|
@@ -327,36 +255,24 @@ module GraphQL
|
|
327
255
|
# also add some preparation hook methods which will be used for this argument
|
328
256
|
# @see {GraphQL::Schema::Argument#initialize} for the signature
|
329
257
|
def argument(name, type, *rest, loads: nil, **kwargs, &block)
|
330
|
-
|
331
|
-
name_as_string = name.to_s
|
332
|
-
|
333
|
-
inferred_arg_name = case name_as_string
|
334
|
-
when /_id$/
|
335
|
-
name_as_string.sub(/_id$/, "").to_sym
|
336
|
-
when /_ids$/
|
337
|
-
name_as_string.sub(/_ids$/, "")
|
338
|
-
.sub(/([^s])$/, "\\1s")
|
339
|
-
.to_sym
|
340
|
-
else
|
341
|
-
name
|
342
|
-
end
|
343
|
-
|
344
|
-
kwargs[:as] ||= inferred_arg_name
|
345
|
-
own_arguments_loads_as_type[kwargs[:as]] = loads
|
346
|
-
end
|
258
|
+
arg_defn = super(*argument_with_loads(name, type, *rest, loads: loads, **kwargs, &block))
|
347
259
|
|
348
|
-
arg_defn =
|
260
|
+
own_arguments_loads_as_type[arg_defn.keyword] = loads if loads
|
349
261
|
|
350
262
|
if loads && arg_defn.type.list?
|
351
263
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
352
264
|
def load_#{arg_defn.keyword}(values)
|
353
|
-
|
265
|
+
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
266
|
+
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
267
|
+
GraphQL::Execution::Lazy.all(values.map { |value| load_application_object(argument, lookup_as_type, value) })
|
354
268
|
end
|
355
269
|
RUBY
|
356
270
|
elsif loads
|
357
271
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
358
272
|
def load_#{arg_defn.keyword}(value)
|
359
|
-
|
273
|
+
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
274
|
+
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
275
|
+
load_application_object(argument, lookup_as_type, value)
|
360
276
|
end
|
361
277
|
RUBY
|
362
278
|
else
|
@@ -145,6 +145,14 @@ module GraphQL
|
|
145
145
|
@path.pop
|
146
146
|
end
|
147
147
|
|
148
|
+
def on_input_object(node, parent)
|
149
|
+
is_list = @argument_definitions.last&.type&.list? == true
|
150
|
+
|
151
|
+
@path.push(parent.children.index(node)) if is_list
|
152
|
+
super
|
153
|
+
@path.pop if is_list
|
154
|
+
end
|
155
|
+
|
148
156
|
# @return [GraphQL::BaseType] The current object type
|
149
157
|
def type_definition
|
150
158
|
@object_types.last
|
@@ -46,7 +46,7 @@ module GraphQL
|
|
46
46
|
# # Track the subscription here so we can remove it
|
47
47
|
# # on unsubscribe.
|
48
48
|
# if result.context[:subscription_id]
|
49
|
-
# @subscription_ids << context[:subscription_id]
|
49
|
+
# @subscription_ids << result.context[:subscription_id]
|
50
50
|
# end
|
51
51
|
#
|
52
52
|
# transmit(payload)
|
data/lib/graphql/tracing.rb
CHANGED
@@ -42,8 +42,8 @@ module GraphQL
|
|
42
42
|
# execute_multiplex | `{ multiplex: GraphQL::Execution::Multiplex }`
|
43
43
|
# execute_query | `{ query: GraphQL::Query }`
|
44
44
|
# execute_query_lazy | `{ query: GraphQL::Query?, multiplex: GraphQL::Execution::Multiplex? }`
|
45
|
-
# execute_field | `{ context: GraphQL::Query::Context::FieldResolutionContext?, field: GraphQL::Schema::Field?, path: Array<String, Integer>?}`
|
46
|
-
# execute_field_lazy | `{ context: GraphQL::Query::Context::FieldResolutionContext?, field: GraphQL::Schema::Field?, path: Array<String, Integer>?}`
|
45
|
+
# execute_field | `{ context: GraphQL::Query::Context::FieldResolutionContext?, owner: Class?, field: GraphQL::Schema::Field?, path: Array<String, Integer>?}`
|
46
|
+
# execute_field_lazy | `{ context: GraphQL::Query::Context::FieldResolutionContext?, owner: Class?, field: GraphQL::Schema::Field?, path: Array<String, Integer>?}`
|
47
47
|
#
|
48
48
|
# Note that `execute_field` and `execute_field_lazy` receive different data in different settings:
|
49
49
|
#
|
@@ -21,6 +21,11 @@ module GraphQL
|
|
21
21
|
if key == 'execute_multiplex'
|
22
22
|
operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
|
23
23
|
span.resource = operations unless operations.empty?
|
24
|
+
|
25
|
+
# For top span of query, set the analytics sample rate tag, if available.
|
26
|
+
if analytics_enabled?
|
27
|
+
Datadog::Contrib::Analytics.set_sample_rate(span, analytics_sample_rate)
|
28
|
+
end
|
24
29
|
end
|
25
30
|
|
26
31
|
if key == 'execute_query'
|
@@ -41,6 +46,20 @@ module GraphQL
|
|
41
46
|
options.fetch(:tracer, Datadog.tracer)
|
42
47
|
end
|
43
48
|
|
49
|
+
def analytics_available?
|
50
|
+
defined?(Datadog::Contrib::Analytics) \
|
51
|
+
&& Datadog::Contrib::Analytics.respond_to?(:enabled?) \
|
52
|
+
&& Datadog::Contrib::Analytics.respond_to?(:set_sample_rate)
|
53
|
+
end
|
54
|
+
|
55
|
+
def analytics_enabled?
|
56
|
+
analytics_available? && Datadog::Contrib::Analytics.enabled?(options.fetch(:analytics_enabled, false))
|
57
|
+
end
|
58
|
+
|
59
|
+
def analytics_sample_rate
|
60
|
+
options.fetch(:analytics_sample_rate, 1.0)
|
61
|
+
end
|
62
|
+
|
44
63
|
def platform_field_key(type, field)
|
45
64
|
"#{type.graphql_name}.#{field.graphql_name}"
|
46
65
|
end
|
@@ -32,8 +32,9 @@ module GraphQL
|
|
32
32
|
trace_field = true # implemented with instrumenter
|
33
33
|
else
|
34
34
|
field = data[:field]
|
35
|
+
owner = data[:owner]
|
35
36
|
# Lots of duplicated work here, can this be done ahead of time?
|
36
|
-
platform_key = platform_field_key(
|
37
|
+
platform_key = platform_field_key(owner, field)
|
37
38
|
return_type = field.type.unwrap
|
38
39
|
# Handle LateBoundTypes, which don't have `#kind`
|
39
40
|
trace_field = if return_type.respond_to?(:kind) && (return_type.kind.scalar? || return_type.kind.enum?)
|
data/lib/graphql/types.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Types
|
5
|
+
class BigInt < GraphQL::Schema::Scalar
|
6
|
+
description "Represents non-fractional signed whole numeric values. Since the value may exceed the size of a 32-bit integer, it's encoded as a string."
|
7
|
+
|
8
|
+
def self.coerce_input(value, _ctx)
|
9
|
+
Integer(value)
|
10
|
+
rescue ArgumentError
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.coerce_result(value, _ctx)
|
15
|
+
value.to_i.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/graphql/types/int.rb
CHANGED
data/lib/graphql/version.rb
CHANGED
@@ -912,4 +912,30 @@ describe GraphQL::Authorization do
|
|
912
912
|
assert_equal [{"message"=>"Unauthorized Query: nil"}], unauth_res["errors"]
|
913
913
|
end
|
914
914
|
end
|
915
|
+
|
916
|
+
describe "returning false" do
|
917
|
+
class FalseSchema < GraphQL::Schema
|
918
|
+
class Query < GraphQL::Schema::Object
|
919
|
+
def self.authorized?(obj, ctx)
|
920
|
+
false
|
921
|
+
end
|
922
|
+
|
923
|
+
field :int, Integer, null: false
|
924
|
+
|
925
|
+
def int
|
926
|
+
1
|
927
|
+
end
|
928
|
+
end
|
929
|
+
query(Query)
|
930
|
+
if TESTING_INTERPRETER
|
931
|
+
use GraphQL::Execution::Interpreter
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
it "works out-of-the-box" do
|
936
|
+
res = FalseSchema.execute("{ int }")
|
937
|
+
assert_nil res.fetch("data")
|
938
|
+
refute res.key?("errors")
|
939
|
+
end
|
940
|
+
end
|
915
941
|
end
|
@@ -161,8 +161,15 @@ describe GraphQL::Language::Visitor do
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def on_argument(node, parent)
|
164
|
+
# https://github.com/rmosolgo/graphql-ruby/issues/2148
|
165
|
+
# Parent could become a random value, double check that it's a node
|
166
|
+
# to actually fail the test
|
167
|
+
raise RuntimeError, "Parent isn't a Node!" unless parent.class < GraphQL::Language::Nodes::AbstractNode
|
168
|
+
|
164
169
|
if node.name == "deleteMe"
|
165
170
|
super(DELETE_NODE, parent)
|
171
|
+
elsif node.name.include?("nope")
|
172
|
+
[1]
|
166
173
|
else
|
167
174
|
super
|
168
175
|
end
|
@@ -351,6 +358,18 @@ GRAPHQL
|
|
351
358
|
assert_equal after_query, new_document.to_query_string
|
352
359
|
end
|
353
360
|
|
361
|
+
it "ignore non-Nodes::AbstractNode return values" do
|
362
|
+
query = <<-GRAPHQL.chop
|
363
|
+
query {
|
364
|
+
doesntDoAnything(stillNothing: {nope: 1, alsoNope: 2, stillNope: 3})
|
365
|
+
}
|
366
|
+
GRAPHQL
|
367
|
+
|
368
|
+
document, new_document = get_result(query)
|
369
|
+
assert_equal query, document.to_query_string
|
370
|
+
assert_equal query, new_document.to_query_string
|
371
|
+
end
|
372
|
+
|
354
373
|
it "can modify inline fragments" do
|
355
374
|
before_query = <<-GRAPHQL.chop
|
356
375
|
query {
|
@@ -4,7 +4,7 @@ require "spec_helper"
|
|
4
4
|
describe GraphQL::Schema::Argument do
|
5
5
|
module SchemaArgumentTest
|
6
6
|
class Query < GraphQL::Schema::Object
|
7
|
-
field :field, String, null:
|
7
|
+
field :field, String, null: true do
|
8
8
|
argument :arg, String, description: "test", required: false
|
9
9
|
|
10
10
|
argument :arg_with_block, String, required: false do
|
@@ -14,6 +14,11 @@ describe GraphQL::Schema::Argument do
|
|
14
14
|
argument :aliased_arg, String, required: false, as: :renamed
|
15
15
|
argument :prepared_arg, Int, required: false, prepare: :multiply
|
16
16
|
argument :prepared_by_proc_arg, Int, required: false, prepare: ->(val, context) { context[:multiply_by] * val }
|
17
|
+
argument :exploding_prepared_arg, Int, required: false, prepare: ->(val, context) do
|
18
|
+
raise GraphQL::ExecutionError.new('boom!')
|
19
|
+
end
|
20
|
+
|
21
|
+
argument :keys, [String], required: false
|
17
22
|
|
18
23
|
class Multiply
|
19
24
|
def call(val, context)
|
@@ -41,6 +46,13 @@ describe GraphQL::Schema::Argument do
|
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
49
|
+
describe "#keys" do
|
50
|
+
it "is not overwritten by the 'keys' argument" do
|
51
|
+
expected_keys = ["aliasedArg", "arg", "argWithBlock", "explodingPreparedArg", "keys", "preparedArg", "preparedByCallableArg", "preparedByProcArg"]
|
52
|
+
assert_equal expected_keys, SchemaArgumentTest::Query.fields["field"].arguments.keys.sort
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
44
56
|
describe "#path" do
|
45
57
|
it "includes type, field and argument names" do
|
46
58
|
assert_equal "Query.field.argWithBlock", SchemaArgumentTest::Query.fields["field"].arguments["argWithBlock"].path
|
@@ -105,6 +117,7 @@ describe GraphQL::Schema::Argument do
|
|
105
117
|
# Make sure it's getting the renamed symbol:
|
106
118
|
assert_equal '{:prepared_arg=>15}', res["data"]["field"]
|
107
119
|
end
|
120
|
+
|
108
121
|
it "calls the method on the provided Proc" do
|
109
122
|
query_str = <<-GRAPHQL
|
110
123
|
{ field(preparedByProcArg: 5) }
|
@@ -114,6 +127,7 @@ describe GraphQL::Schema::Argument do
|
|
114
127
|
# Make sure it's getting the renamed symbol:
|
115
128
|
assert_equal '{:prepared_by_proc_arg=>15}', res["data"]["field"]
|
116
129
|
end
|
130
|
+
|
117
131
|
it "calls the method on the provided callable object" do
|
118
132
|
query_str = <<-GRAPHQL
|
119
133
|
{ field(preparedByCallableArg: 5) }
|
@@ -123,5 +137,16 @@ describe GraphQL::Schema::Argument do
|
|
123
137
|
# Make sure it's getting the renamed symbol:
|
124
138
|
assert_equal '{:prepared_by_callable_arg=>15}', res["data"]["field"]
|
125
139
|
end
|
140
|
+
|
141
|
+
it "handles exceptions raised by prepare" do
|
142
|
+
query_str = <<-GRAPHQL
|
143
|
+
{ f1: field(arg: "echo"), f2: field(explodingPreparedArg: 5) }
|
144
|
+
GRAPHQL
|
145
|
+
|
146
|
+
res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
|
147
|
+
assert_equal({ 'f1' => '{:arg=>"echo"}', 'f2' => nil }, res['data'])
|
148
|
+
assert_equal(res['errors'][0]['message'], 'boom!')
|
149
|
+
assert_equal(res['errors'][0]['path'], ['f2'])
|
150
|
+
end
|
126
151
|
end
|
127
152
|
end
|
@@ -9,6 +9,12 @@ describe GraphQL::Schema::FieldExtension do
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
class PowerOfFilter < GraphQL::Schema::FieldExtension
|
13
|
+
def after_resolve(object:, value:, arguments:, context:, memo:)
|
14
|
+
value**options.fetch(:power, 2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
12
18
|
class MultiplyByOption < GraphQL::Schema::FieldExtension
|
13
19
|
def after_resolve(object:, value:, arguments:, context:, memo:)
|
14
20
|
value * options[:factor]
|
@@ -52,8 +58,13 @@ describe GraphQL::Schema::FieldExtension do
|
|
52
58
|
argument :input, Integer, required: true
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
input
|
61
|
+
field :square, Integer, null: false, resolver_method: :pass_thru, extensions: [PowerOfFilter] do
|
62
|
+
argument :input, Integer, required: true
|
63
|
+
end
|
64
|
+
|
65
|
+
field :cube, Integer, null: false, resolver_method: :pass_thru do
|
66
|
+
extension(PowerOfFilter, power: 3)
|
67
|
+
argument :input, Integer, required: true
|
57
68
|
end
|
58
69
|
|
59
70
|
field :tripled_by_option, Integer, null: false, resolver_method: :pass_thru do
|
@@ -68,6 +79,10 @@ describe GraphQL::Schema::FieldExtension do
|
|
68
79
|
field :multiply_input2, Integer, null: false, resolver_method: :pass_thru, extensions: [MultiplyByArgumentUsingResolve] do
|
69
80
|
argument :input, Integer, required: true
|
70
81
|
end
|
82
|
+
|
83
|
+
def pass_thru(input:, **args)
|
84
|
+
input # return it as-is, it will be modified by extensions
|
85
|
+
end
|
71
86
|
end
|
72
87
|
|
73
88
|
class Schema < GraphQL::Schema
|
@@ -107,6 +122,13 @@ describe GraphQL::Schema::FieldExtension do
|
|
107
122
|
assert_equal 12, res["data"]["tripledByOption"]
|
108
123
|
end
|
109
124
|
|
125
|
+
it "provides an empty hash as default options" do
|
126
|
+
res = exec_query("{ square(input: 4) }")
|
127
|
+
assert_equal 16, res["data"]["square"]
|
128
|
+
res = exec_query("{ cube(input: 4) }")
|
129
|
+
assert_equal 64, res["data"]["cube"]
|
130
|
+
end
|
131
|
+
|
110
132
|
it "can hide arguments from resolve methods" do
|
111
133
|
res = exec_query("{ multiplyInput(input: 3, factor: 5) }")
|
112
134
|
assert_equal 15, res["data"]["multiplyInput"]
|