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