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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dfc72228ed2ce65697195792c7e58ff2c061435d
4
- data.tar.gz: 2ab1fa5308bb89c21319558620a705f5e58fbbdb
3
+ metadata.gz: fe0eb7f5a621028df479067eb72143c397f34ecd
4
+ data.tar.gz: db4afa75f08ba2a8a409837c962442b824779e05
5
5
  SHA512:
6
- metadata.gz: 650ef5ae621e294c2a5242206a0158d58970d40dfe5bace0f863a15e59a533324e2b2c623b7df0b089c2ffa8ebcc0b445b43fe5e3b6c0bb018a9a5a732c74fa2
7
- data.tar.gz: 66d3f0113edbcf6562f9a430c59ae569bff7a9f444ced9c5e6fa2eb11a9095f192d57016126c0a69334cf6c58c8f23bffa2a5bafd83214453c205876d8a8e9f9
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
- ret_type = @field.type.unwrap
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
@@ -57,7 +57,7 @@ module GraphQL
57
57
  # `:no_response` to return nothing.
58
58
  #
59
59
  # The default is `:no_response`.
60
- def subscribe(args)
60
+ def subscribe(args = {})
61
61
  :no_response
62
62
  end
63
63
 
@@ -12,7 +12,20 @@ module GraphQL
12
12
  private
13
13
 
14
14
  def get_parent_type(context, parent)
15
- defn = context.field_definition
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("Subscription", event_name)
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("Subscription", normalized_event_name)
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
- trace_field = if return_type.kind.scalar? || return_type.kind.enum?
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
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.9.2"
3
+ VERSION = "1.9.3"
4
4
  end
@@ -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
@@ -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.2
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-15 00:00:00.000000000 Z
11
+ date: 2019-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips