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