graphql 1.9.2 → 1.9.3
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 +4 -4
- data/lib/graphql/schema/field/scope_extension.rb +8 -4
- data/lib/graphql/schema/subscription.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +14 -1
- data/lib/graphql/subscriptions.rb +2 -2
- data/lib/graphql/tracing/platform_tracing.rb +2 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/authorization_spec.rb +18 -4
- data/spec/graphql/schema/member/scoped_spec.rb +21 -1
- data/spec/graphql/schema/subscription_spec.rb +24 -0
- data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +14 -0
- data/spec/graphql/tracing/platform_tracing_spec.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe0eb7f5a621028df479067eb72143c397f34ecd
|
4
|
+
data.tar.gz: db4afa75f08ba2a8a409837c962442b824779e05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bccb51d411ce789817fa821216eae65c0e88faee075f3859c130ffb10a6c6c8fca7fd1a2455c8aff2d9ba7f7a378804528e4d3f5ea9a579167de10f77234e0a
|
7
|
+
data.tar.gz: dba82fb8a24a3b309701278db3f6d95818090f22f128d3a7ed57703fcef9826949a1eec7449afddc5892b24e08cc1172d3b98c88f80f092dbaf0a06a9674323e
|
@@ -5,11 +5,15 @@ module GraphQL
|
|
5
5
|
class Field
|
6
6
|
class ScopeExtension < GraphQL::Schema::FieldExtension
|
7
7
|
def after_resolve(value:, context:, **rest)
|
8
|
-
|
9
|
-
if ret_type.respond_to?(:scope_items)
|
10
|
-
ret_type.scope_items(value, context)
|
11
|
-
else
|
8
|
+
if value.nil?
|
12
9
|
value
|
10
|
+
else
|
11
|
+
ret_type = @field.type.unwrap
|
12
|
+
if ret_type.respond_to?(:scope_items)
|
13
|
+
ret_type.scope_items(value, context)
|
14
|
+
else
|
15
|
+
value
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
15
19
|
end
|
@@ -12,7 +12,20 @@ module GraphQL
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def get_parent_type(context, parent)
|
15
|
-
|
15
|
+
# If argument_definition is defined we're at nested object
|
16
|
+
# and need to refer to the containing input object type rather
|
17
|
+
# than the field_definition.
|
18
|
+
# h/t @rmosolgo
|
19
|
+
arg_defn = context.argument_definition
|
20
|
+
|
21
|
+
# Double checking that arg_defn is an input object as nested
|
22
|
+
# scalars, namely JSON, can make it to this branch
|
23
|
+
defn = if arg_defn && arg_defn.type.unwrap.kind.input_object?
|
24
|
+
arg_defn.type.unwrap
|
25
|
+
else
|
26
|
+
context.field_definition
|
27
|
+
end
|
28
|
+
|
16
29
|
parent_type = context.warden.arguments(defn)
|
17
30
|
.find{|f| f.name == parent_name(parent, defn) }
|
18
31
|
parent_type ? parent_type.type.unwrap : nil
|
@@ -43,12 +43,12 @@ module GraphQL
|
|
43
43
|
event_name = event_name.to_s
|
44
44
|
|
45
45
|
# Try with the verbatim input first:
|
46
|
-
field = @schema.get_field(
|
46
|
+
field = @schema.get_field(@schema.subscription, event_name)
|
47
47
|
|
48
48
|
if field.nil?
|
49
49
|
# And if it wasn't found, normalize it:
|
50
50
|
normalized_event_name = normalize_name(event_name)
|
51
|
-
field = @schema.get_field(
|
51
|
+
field = @schema.get_field(@schema.subscription, normalized_event_name)
|
52
52
|
if field.nil?
|
53
53
|
raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
|
54
54
|
end
|
@@ -35,7 +35,8 @@ module GraphQL
|
|
35
35
|
# Lots of duplicated work here, can this be done ahead of time?
|
36
36
|
platform_key = platform_field_key(field.owner, field)
|
37
37
|
return_type = field.type.unwrap
|
38
|
-
|
38
|
+
# Handle LateBoundTypes, which don't have `#kind`
|
39
|
+
trace_field = if return_type.respond_to?(:kind) && (return_type.kind.scalar? || return_type.kind.enum?)
|
39
40
|
(field.trace.nil? && @trace_scalars) || field.trace
|
40
41
|
else
|
41
42
|
true
|
@@ -15,7 +15,7 @@ module GraphQL
|
|
15
15
|
attr_reader :context
|
16
16
|
|
17
17
|
def initialize(message = nil, object: nil, type: nil, context: nil)
|
18
|
-
if message.nil? && object.nil?
|
18
|
+
if message.nil? && object.nil? && type.nil?
|
19
19
|
raise ArgumentError, "#{self.class.name} requires either a message or keywords"
|
20
20
|
end
|
21
21
|
|
data/lib/graphql/version.rb
CHANGED
@@ -236,6 +236,10 @@ describe GraphQL::Authorization do
|
|
236
236
|
end
|
237
237
|
|
238
238
|
class Query < BaseObject
|
239
|
+
def self.authorized?(obj, ctx)
|
240
|
+
!ctx[:query_unauthorized]
|
241
|
+
end
|
242
|
+
|
239
243
|
field :hidden, Integer, null: false
|
240
244
|
field :unauthorized, Integer, null: true, method: :itself
|
241
245
|
field :int2, Integer, null: true do
|
@@ -386,7 +390,7 @@ describe GraphQL::Authorization do
|
|
386
390
|
elsif err.object == :replace
|
387
391
|
33
|
388
392
|
else
|
389
|
-
raise GraphQL::ExecutionError, "Unauthorized #{err.type.graphql_name}: #{err.object}"
|
393
|
+
raise GraphQL::ExecutionError, "Unauthorized #{err.type.graphql_name}: #{err.object.inspect}"
|
390
394
|
end
|
391
395
|
end
|
392
396
|
|
@@ -679,7 +683,7 @@ describe GraphQL::Authorization do
|
|
679
683
|
it "adds the error to the errors key" do
|
680
684
|
query = "{ unauthorized }"
|
681
685
|
response = AuthTest::Schema.execute(query, root_value: :hide)
|
682
|
-
assert_equal ["Unauthorized Query: hide"], response["errors"].map { |e| e["message"] }
|
686
|
+
assert_equal ["Unauthorized Query: :hide"], response["errors"].map { |e| e["message"] }
|
683
687
|
end
|
684
688
|
end
|
685
689
|
end
|
@@ -820,7 +824,7 @@ describe GraphQL::Authorization do
|
|
820
824
|
assert_nil unauthorized_res["data"].fetch("a")
|
821
825
|
assert_equal "b", unauthorized_res["data"]["b"]["value"]
|
822
826
|
# Also, the custom handler was called:
|
823
|
-
assert_equal ["Unauthorized UnauthorizedCheckBox: a"], unauthorized_res["errors"].map { |e| e["message"] }
|
827
|
+
assert_equal ["Unauthorized UnauthorizedCheckBox: \"a\""], unauthorized_res["errors"].map { |e| e["message"] }
|
824
828
|
end
|
825
829
|
|
826
830
|
it "Works for lazy connections" do
|
@@ -885,7 +889,7 @@ describe GraphQL::Authorization do
|
|
885
889
|
|
886
890
|
res = auth_execute(query)
|
887
891
|
# An error from two, values from the others
|
888
|
-
assert_equal ["Unauthorized UnauthorizedCheckBox: a", "Unauthorized UnauthorizedCheckBox: a"], res["errors"].map { |e| e["message"] }
|
892
|
+
assert_equal ["Unauthorized UnauthorizedCheckBox: \"a\"", "Unauthorized UnauthorizedCheckBox: \"a\""], res["errors"].map { |e| e["message"] }
|
889
893
|
assert_equal [{"value" => "z"}, {"value" => "z2"}, nil, nil], res["data"]["unauthorizedLazyListInterface"]
|
890
894
|
end
|
891
895
|
|
@@ -897,5 +901,15 @@ describe GraphQL::Authorization do
|
|
897
901
|
res = auth_execute(query, context: { replace_me: false })
|
898
902
|
assert_equal false, res["data"]["replacedObject"]["replaced"]
|
899
903
|
end
|
904
|
+
|
905
|
+
it "works when the query hook returns false and there's no root object" do
|
906
|
+
query = "{ __typename }"
|
907
|
+
res = auth_execute(query)
|
908
|
+
assert_equal "Query", res["data"]["__typename"]
|
909
|
+
|
910
|
+
unauth_res = auth_execute(query, context: { query_unauthorized: true })
|
911
|
+
assert_nil unauth_res["data"]
|
912
|
+
assert_equal [{"message"=>"Unauthorized Query: nil"}], unauth_res["errors"]
|
913
|
+
end
|
900
914
|
end
|
901
915
|
end
|
@@ -13,7 +13,8 @@ describe GraphQL::Schema::Member::Scoped do
|
|
13
13
|
elsif context[:english]
|
14
14
|
items.select { |i| i.name == "Paperclip" }
|
15
15
|
else
|
16
|
-
|
16
|
+
# boot everything
|
17
|
+
items.reject { true }
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -55,6 +56,12 @@ describe GraphQL::Schema::Member::Scoped do
|
|
55
56
|
field :unscoped_items, [Item], null: false,
|
56
57
|
scope: false,
|
57
58
|
resolver_method: :items
|
59
|
+
|
60
|
+
field :nil_items, [Item], null: true
|
61
|
+
def nil_items
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
58
65
|
field :french_items, [FrenchItem], null: false,
|
59
66
|
resolver_method: :items
|
60
67
|
if TESTING_INTERPRETER
|
@@ -122,6 +129,19 @@ describe GraphQL::Schema::Member::Scoped do
|
|
122
129
|
assert_equal ["Trombone", "Paperclip"], get_item_names_with_context({}, field_name: "unscopedItems")
|
123
130
|
end
|
124
131
|
|
132
|
+
it "returns null when the value is nil" do
|
133
|
+
query_str = "
|
134
|
+
{
|
135
|
+
nilItems {
|
136
|
+
name
|
137
|
+
}
|
138
|
+
}
|
139
|
+
"
|
140
|
+
res = ScopeSchema.execute(query_str)
|
141
|
+
refute res.key?("errors")
|
142
|
+
assert_nil res.fetch("data").fetch("nilItems")
|
143
|
+
end
|
144
|
+
|
125
145
|
it "is inherited" do
|
126
146
|
assert_equal ["Trombone"], get_item_names_with_context({}, field_name: "frenchItems")
|
127
147
|
end
|
@@ -87,10 +87,18 @@ describe GraphQL::Schema::Subscription do
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
# Like above, but doesn't override #subscription,
|
91
|
+
# to make sure it works without arguments
|
92
|
+
class NewUsersJoined < BaseSubscription
|
93
|
+
field :users, [User], null: true,
|
94
|
+
description: "Includes newly-created users, or all users on the initial load"
|
95
|
+
end
|
96
|
+
|
90
97
|
class Subscription < GraphQL::Schema::Object
|
91
98
|
extend GraphQL::Subscriptions::SubscriptionRoot
|
92
99
|
field :toot_was_tooted, subscription: TootWasTooted
|
93
100
|
field :users_joined, subscription: UsersJoined
|
101
|
+
field :new_users_joined, subscription: NewUsersJoined
|
94
102
|
end
|
95
103
|
|
96
104
|
class Mutation < GraphQL::Schema::Object
|
@@ -294,6 +302,22 @@ describe GraphQL::Schema::Subscription do
|
|
294
302
|
assert_equal({"data" => {}}, res)
|
295
303
|
assert_equal 1, in_memory_subscription_count
|
296
304
|
end
|
305
|
+
|
306
|
+
it "works when there are no arguments" do
|
307
|
+
assert_equal 0, in_memory_subscription_count
|
308
|
+
|
309
|
+
res = exec_query <<-GRAPHQL
|
310
|
+
subscription {
|
311
|
+
newUsersJoined {
|
312
|
+
users {
|
313
|
+
handle
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
GRAPHQL
|
318
|
+
assert_equal({"data" => {}}, res)
|
319
|
+
assert_equal 1, in_memory_subscription_count
|
320
|
+
end
|
297
321
|
end
|
298
322
|
|
299
323
|
describe "updates" do
|
data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb
CHANGED
@@ -12,6 +12,7 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
|
|
12
12
|
yakSource: searchDairy(product: [{source: COW, fatContent: 1.1}]) { __typename }
|
13
13
|
badSource: searchDairy(product: [{source: 1.1}]) { __typename }
|
14
14
|
missingSource: searchDairy(product: [{fatContent: 1.1}]) { __typename }
|
15
|
+
missingNestedRequiredInputObjectAttribute: searchDairy(product: [{fatContent: 1.2, order_by: {}}]) { __typename }
|
15
16
|
listCoerce: cheese(id: 1) { similarCheese(source: YAK) { __typename } }
|
16
17
|
missingInputField: searchDairy(product: [{source: YAK, wacky: 1}]) { __typename }
|
17
18
|
}
|
@@ -42,9 +43,21 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
|
|
42
43
|
"inputObjectType"=>"DairyProductInput"
|
43
44
|
}
|
44
45
|
}
|
46
|
+
missing_order_by_direction_error = {
|
47
|
+
"message"=>"Argument 'direction' on InputObject 'ResourceOrderType' is required. Expected type String!",
|
48
|
+
"locations"=>[{"line"=>8, "column"=>100}],
|
49
|
+
"path"=>["query getCheese", "missingNestedRequiredInputObjectAttribute", "product", "order_by", "direction"],
|
50
|
+
"extensions"=>{
|
51
|
+
"code"=>"missingRequiredInputObjectAttribute",
|
52
|
+
"argumentName"=>"direction",
|
53
|
+
"argumentType"=>"String!",
|
54
|
+
"inputObjectType"=>"ResourceOrderType"
|
55
|
+
}
|
56
|
+
}
|
45
57
|
it "finds undefined or missing-required arguments to fields and directives" do
|
46
58
|
without_error_bubbling(schema) do
|
47
59
|
assert_includes(errors, missing_source_error)
|
60
|
+
assert_includes(errors, missing_order_by_direction_error)
|
48
61
|
refute_includes(errors, missing_required_field_error)
|
49
62
|
end
|
50
63
|
end
|
@@ -52,6 +65,7 @@ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
|
|
52
65
|
with_error_bubbling(schema) do
|
53
66
|
assert_includes(errors, missing_required_field_error)
|
54
67
|
assert_includes(errors, missing_source_error)
|
68
|
+
assert_includes(errors, missing_order_by_direction_error)
|
55
69
|
end
|
56
70
|
end
|
57
71
|
end
|
@@ -37,6 +37,10 @@ describe GraphQL::Tracing::PlatformTracing do
|
|
37
37
|
CustomPlatformTracer::TRACE.clear
|
38
38
|
end
|
39
39
|
|
40
|
+
it "runs the introspection query (handles late-bound types)" do
|
41
|
+
assert schema.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
|
42
|
+
end
|
43
|
+
|
40
44
|
it "calls the platform's own method with its own keys" do
|
41
45
|
schema.execute(" { cheese(id: 1) { flavor } }")
|
42
46
|
# This is different because schema/member/instrumentation
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|