graphql 1.8.0 → 1.8.1
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/compatibility/schema_parser_specification.rb +28 -0
- data/lib/graphql/enum_type.rb +3 -3
- data/lib/graphql/execution_error.rb +13 -1
- data/lib/graphql/introspection.rb +0 -3
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +1 -1
- data/lib/graphql/introspection/type_type.rb +1 -2
- data/lib/graphql/language/parser.rb +2 -2
- data/lib/graphql/language/parser.y +2 -2
- data/lib/graphql/railtie.rb +6 -6
- data/lib/graphql/schema/resolver.rb +0 -1
- data/lib/graphql/schema/warden.rb +17 -10
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
- data/lib/graphql/upgrader/member.rb +6 -6
- data/lib/graphql/version.rb +1 -1
- data/spec/fixtures/upgrader/blame_range.transformed.rb +2 -2
- data/spec/fixtures/upgrader/delete_project.transformed.rb +4 -4
- data/spec/fixtures/upgrader/increment_count.transformed.rb +1 -1
- data/spec/fixtures/upgrader/photo.transformed.rb +1 -1
- data/spec/fixtures/upgrader/starrable.transformed.rb +5 -5
- data/spec/fixtures/upgrader/subscribable.transformed.rb +7 -7
- data/spec/fixtures/upgrader/type_x.transformed.rb +6 -6
- data/spec/graphql/execution/multiplex_spec.rb +1 -1
- data/spec/graphql/execution_error_spec.rb +20 -1
- data/spec/graphql/introspection/schema_type_spec.rb +1 -0
- data/spec/graphql/relay/mongo_relation_connection_spec.rb +12 -1
- data/spec/graphql/schema/warden_spec.rb +129 -0
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +17 -9
- data/spec/graphql/upgrader/member_spec.rb +5 -5
- data/spec/spec_helper.rb +20 -1
- data/spec/support/dummy/schema.rb +7 -0
- metadata +2 -21
- data/lib/graphql/introspection/schema_field.rb +0 -16
- data/lib/graphql/introspection/type_by_name_field.rb +0 -22
- data/lib/graphql/introspection/typename_field.rb +0 -12
- data/spec/graphql/introspection/type_by_name_field_spec.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d3ae1b02473b2855990db63798b1c5d2fea46de
|
4
|
+
data.tar.gz: cfdc979146afea3eb44a969b66ac3a0763956db7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 762efe82d940d001685c0cf242e1b6fc0c7ee9a5b77de66c4917268b6d2ae0340346daf707d74d41c9b1dd77b71c2d2d1f887e74e2eae2ab9c7869a787a5a74b
|
7
|
+
data.tar.gz: 4bcd06d7c866bb65154310cb8d11965902372860747aaaa99ebf44a73ebac3f6b1743e459e47730ba44e7fab57588031352bd1ed63925a42ec89550d32bf7906
|
@@ -78,6 +78,34 @@ module GraphQL
|
|
78
78
|
assert_equal [], type.values[2].directives
|
79
79
|
end
|
80
80
|
|
81
|
+
def test_it_parses_union_types
|
82
|
+
document = parse(
|
83
|
+
"union BagOfThings = \n" \
|
84
|
+
"A |\n" \
|
85
|
+
"B |\n" \
|
86
|
+
"C"
|
87
|
+
)
|
88
|
+
|
89
|
+
union = document.definitions.first
|
90
|
+
|
91
|
+
assert_equal GraphQL::Language::Nodes::UnionTypeDefinition, union.class
|
92
|
+
assert_equal 'BagOfThings', union.name
|
93
|
+
assert_equal 3, union.types.length
|
94
|
+
assert_equal [1, 1], union.position
|
95
|
+
|
96
|
+
assert_equal GraphQL::Language::Nodes::TypeName, union.types[0].class
|
97
|
+
assert_equal 'A', union.types[0].name
|
98
|
+
assert_equal [2, 1], union.types[0].position
|
99
|
+
|
100
|
+
assert_equal GraphQL::Language::Nodes::TypeName, union.types[1].class
|
101
|
+
assert_equal 'B', union.types[1].name
|
102
|
+
assert_equal [3, 1], union.types[1].position
|
103
|
+
|
104
|
+
assert_equal GraphQL::Language::Nodes::TypeName, union.types[2].class
|
105
|
+
assert_equal 'C', union.types[2].name
|
106
|
+
assert_equal [4, 1], union.types[2].position
|
107
|
+
end
|
108
|
+
|
81
109
|
def test_it_parses_input_types
|
82
110
|
document = parse('
|
83
111
|
input EmptyMutationInput {
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -12,8 +12,8 @@ module GraphQL
|
|
12
12
|
#
|
13
13
|
# @example An enum of programming languages
|
14
14
|
# LanguageEnum = GraphQL::EnumType.define do
|
15
|
-
# name "
|
16
|
-
# description "Programming
|
15
|
+
# name "Language"
|
16
|
+
# description "Programming language for Web projects"
|
17
17
|
# value("PYTHON", "A dynamic, function-oriented language")
|
18
18
|
# value("RUBY", "A very dynamic language aimed at programmer happiness")
|
19
19
|
# value("JAVASCRIPT", "Accidental lingua franca of the web")
|
@@ -57,7 +57,7 @@ module GraphQL
|
|
57
57
|
# }
|
58
58
|
#
|
59
59
|
# @example Enum whose values are different in ActiveRecord-land
|
60
|
-
# class Language < ActiveRecord::
|
60
|
+
# class Language < ActiveRecord::Base
|
61
61
|
# enum language: {
|
62
62
|
# rb: 0
|
63
63
|
# }
|
@@ -12,11 +12,19 @@ module GraphQL
|
|
12
12
|
attr_accessor :path
|
13
13
|
|
14
14
|
# @return [Hash] Optional data for error objects
|
15
|
+
# @deprecated Use `extensions` instead of `options`. The GraphQL spec
|
16
|
+
# recommends that any custom entries in an error be under the
|
17
|
+
# `extensions` key.
|
15
18
|
attr_accessor :options
|
16
19
|
|
17
|
-
|
20
|
+
# @return [Hash] Optional custom data for error objects which will be added
|
21
|
+
# under the `extensions` key.
|
22
|
+
attr_accessor :extensions
|
23
|
+
|
24
|
+
def initialize(message, ast_node: nil, options: nil, extensions: nil)
|
18
25
|
@ast_node = ast_node
|
19
26
|
@options = options
|
27
|
+
@extensions = extensions
|
20
28
|
super(message)
|
21
29
|
end
|
22
30
|
|
@@ -39,6 +47,10 @@ module GraphQL
|
|
39
47
|
if options
|
40
48
|
hash.merge!(options)
|
41
49
|
end
|
50
|
+
if extensions
|
51
|
+
hash["extensions"] ||= {}
|
52
|
+
hash["extensions"].merge!(extensions)
|
53
|
+
end
|
42
54
|
hash
|
43
55
|
end
|
44
56
|
end
|
@@ -5,7 +5,6 @@ module GraphQL
|
|
5
5
|
end
|
6
6
|
|
7
7
|
require "graphql/introspection/base_object"
|
8
|
-
require "graphql/introspection/typename_field"
|
9
8
|
require "graphql/introspection/input_value_type"
|
10
9
|
require "graphql/introspection/enum_value_type"
|
11
10
|
require "graphql/introspection/type_kind_enum"
|
@@ -14,8 +13,6 @@ require "graphql/introspection/field_type"
|
|
14
13
|
require "graphql/introspection/directive_location_enum"
|
15
14
|
require "graphql/introspection/directive_type"
|
16
15
|
require "graphql/introspection/schema_type"
|
17
|
-
require "graphql/introspection/schema_field"
|
18
|
-
require "graphql/introspection/type_by_name_field"
|
19
16
|
require "graphql/introspection/introspection_query"
|
20
17
|
require "graphql/introspection/dynamic_fields"
|
21
18
|
require "graphql/introspection/entry_points"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
|
-
class DynamicFields <
|
4
|
+
class DynamicFields < Introspection::BaseObject
|
5
5
|
field :__typename, String, "The name of this type", null: false, extras: [:irep_node]
|
6
6
|
def __typename(irep_node:)
|
7
7
|
irep_node.owner_type.name
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
|
-
class EntryPoints <
|
4
|
+
class EntryPoints < Introspection::BaseObject
|
5
5
|
field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false
|
6
6
|
field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", null: true do
|
7
7
|
argument :name, String, required: true
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
|
-
class TypeType <
|
4
|
+
class TypeType < Introspection::BaseObject
|
5
5
|
graphql_name "__Type"
|
6
6
|
description "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in "\
|
7
7
|
"GraphQL as represented by the `__TypeKind` enum.\n\n"\
|
@@ -24,7 +24,6 @@ module GraphQL
|
|
24
24
|
end
|
25
25
|
field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: true
|
26
26
|
field :of_type, GraphQL::Schema::LateBoundType.new("__Type"), null: true
|
27
|
-
introspection true
|
28
27
|
|
29
28
|
def kind
|
30
29
|
@object.kind.name
|
@@ -1651,14 +1651,14 @@ module_eval(<<'.,.,', 'parser.y', 341)
|
|
1651
1651
|
|
1652
1652
|
module_eval(<<'.,.,', 'parser.y', 345)
|
1653
1653
|
def _reduce_141(val, _values, result)
|
1654
|
-
return [make_node(:TypeName, name: val[0])]
|
1654
|
+
return [make_node(:TypeName, name: val[0], position_source: val[0])]
|
1655
1655
|
result
|
1656
1656
|
end
|
1657
1657
|
.,.,
|
1658
1658
|
|
1659
1659
|
module_eval(<<'.,.,', 'parser.y', 346)
|
1660
1660
|
def _reduce_142(val, _values, result)
|
1661
|
-
val[0] << make_node(:TypeName, name: val[2])
|
1661
|
+
val[0] << make_node(:TypeName, name: val[2], position_source: val[2])
|
1662
1662
|
result
|
1663
1663
|
end
|
1664
1664
|
.,.,
|
@@ -343,8 +343,8 @@ rule
|
|
343
343
|
}
|
344
344
|
|
345
345
|
union_members:
|
346
|
-
name { return [make_node(:TypeName, name: val[0])]}
|
347
|
-
| union_members PIPE name { val[0] << make_node(:TypeName, name: val[2]) }
|
346
|
+
name { return [make_node(:TypeName, name: val[0], position_source: val[0])]}
|
347
|
+
| union_members PIPE name { val[0] << make_node(:TypeName, name: val[2], position_source: val[2]) }
|
348
348
|
|
349
349
|
union_type_definition:
|
350
350
|
UNION name directives_list_opt EQUALS union_members {
|
data/lib/graphql/railtie.rb
CHANGED
@@ -40,7 +40,7 @@ module GraphQL
|
|
40
40
|
unless File.exists?(destination_file)
|
41
41
|
FileUtils.mkdir_p(File.dirname(destination_file))
|
42
42
|
File.open(destination_file, 'w') do |f|
|
43
|
-
f.
|
43
|
+
f.puts "class Types::BaseScalar < GraphQL::Schema::Scalar\nend"
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
48
48
|
unless File.exists?(destination_file)
|
49
49
|
FileUtils.mkdir_p(File.dirname(destination_file))
|
50
50
|
File.open(destination_file, 'w') do |f|
|
51
|
-
f.
|
51
|
+
f.puts "class Types::BaseInputObject < GraphQL::Schema::InputObject\nend"
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -56,7 +56,7 @@ module GraphQL
|
|
56
56
|
unless File.exists?(destination_file)
|
57
57
|
FileUtils.mkdir_p(File.dirname(destination_file))
|
58
58
|
File.open(destination_file, 'w') do |f|
|
59
|
-
f.
|
59
|
+
f.puts "class Types::BaseEnum < GraphQL::Schema::Enum\nend"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -64,7 +64,7 @@ module GraphQL
|
|
64
64
|
unless File.exists?(destination_file)
|
65
65
|
FileUtils.mkdir_p(File.dirname(destination_file))
|
66
66
|
File.open(destination_file, 'w') do |f|
|
67
|
-
f.
|
67
|
+
f.puts "class Types::BaseUnion < GraphQL::Schema::Union\nend"
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -72,14 +72,14 @@ module GraphQL
|
|
72
72
|
unless File.exists?(destination_file)
|
73
73
|
FileUtils.mkdir_p(File.dirname(destination_file))
|
74
74
|
File.open(destination_file, 'w') do |f|
|
75
|
-
f.
|
75
|
+
f.puts "module Types::BaseInterface\n include GraphQL::Schema::Interface\nend"
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
79
|
destination_file = File.join(base_dir, "types", "base_object.rb")
|
80
80
|
unless File.exists?(destination_file)
|
81
81
|
File.open(destination_file, 'w') do |f|
|
82
|
-
f.
|
82
|
+
f.puts "class Types::BaseObject < GraphQL::Schema::Object\nend"
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
@@ -136,13 +136,17 @@ module GraphQL
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def visible_type?(type_defn)
|
139
|
-
visible?(type_defn)
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
139
|
+
return false unless visible?(type_defn)
|
140
|
+
return true if root_type?(type_defn)
|
141
|
+
return true if type_defn.introspection?
|
142
|
+
|
143
|
+
if type_defn.kind.union?
|
144
|
+
visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
|
145
|
+
elsif type_defn.kind.interface?
|
146
|
+
visible_possible_types?(type_defn)
|
147
|
+
else
|
148
|
+
referenced?(type_defn) || visible_abstract_type?(type_defn)
|
149
|
+
end
|
146
150
|
end
|
147
151
|
|
148
152
|
def root_type?(type_defn)
|
@@ -154,6 +158,10 @@ module GraphQL
|
|
154
158
|
members.any? { |m| visible?(m) }
|
155
159
|
end
|
156
160
|
|
161
|
+
def orphan_type?(type_defn)
|
162
|
+
@schema.orphan_types.include?(type_defn)
|
163
|
+
end
|
164
|
+
|
157
165
|
def visible_abstract_type?(type_defn)
|
158
166
|
type_defn.kind.object? && (
|
159
167
|
interfaces(type_defn).any? ||
|
@@ -161,9 +169,8 @@ module GraphQL
|
|
161
169
|
)
|
162
170
|
end
|
163
171
|
|
164
|
-
def
|
165
|
-
(type_defn.
|
166
|
-
@schema.possible_types(type_defn).any? { |t| visible_type?(t) }
|
172
|
+
def visible_possible_types?(type_defn)
|
173
|
+
@schema.possible_types(type_defn).any? { |t| visible_type?(t) }
|
167
174
|
end
|
168
175
|
|
169
176
|
def visible?(member)
|
@@ -19,7 +19,7 @@ module GraphQL
|
|
19
19
|
field = context.warden.get_field(parent_type, ast_field.name)
|
20
20
|
|
21
21
|
if field.nil?
|
22
|
-
if
|
22
|
+
if parent_type.kind.union?
|
23
23
|
context.errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent, context: context)
|
24
24
|
else
|
25
25
|
context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", ast_field, context: context)
|
@@ -29,8 +29,8 @@ module GraphQL
|
|
29
29
|
else
|
30
30
|
"Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
|
31
31
|
end
|
32
|
-
elsif resolved_type.kind.
|
33
|
-
"
|
32
|
+
elsif resolved_type.kind.fields? && ast_node.selections.none?
|
33
|
+
"Field must have selections (%{node_name} returns #{resolved_type.name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)"
|
34
34
|
else
|
35
35
|
nil
|
36
36
|
end
|
@@ -356,10 +356,10 @@ module GraphQL
|
|
356
356
|
# This is not good, it will hit false positives
|
357
357
|
# Should use AST to make this substitution
|
358
358
|
if obj_arg_name != "_"
|
359
|
-
proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\
|
359
|
+
proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
|
360
360
|
end
|
361
361
|
if ctx_arg_name != "_"
|
362
|
-
proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\
|
362
|
+
proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
|
363
363
|
end
|
364
364
|
|
365
365
|
method_defn = "def #{@proc_name}(**#{args_arg_name})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
|
@@ -491,8 +491,8 @@ module GraphQL
|
|
491
491
|
# - Get the three argument names (obj, arg, ctx)
|
492
492
|
# - Get the proc body
|
493
493
|
# - Find and replace:
|
494
|
-
# - The ctx argument becomes
|
495
|
-
# - The obj argument becomes
|
494
|
+
# - The ctx argument becomes `context`
|
495
|
+
# - The obj argument becomes `object`
|
496
496
|
# - Args is trickier:
|
497
497
|
# - If it's not used, remove it
|
498
498
|
# - If it's used, abandon ship and make it `**args`
|
@@ -513,10 +513,10 @@ module GraphQL
|
|
513
513
|
# This is not good, it will hit false positives
|
514
514
|
# Should use AST to make this substitution
|
515
515
|
if obj_arg_name != "_"
|
516
|
-
proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\
|
516
|
+
proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
|
517
517
|
end
|
518
518
|
if ctx_arg_name != "_"
|
519
|
-
proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\
|
519
|
+
proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
|
520
520
|
end
|
521
521
|
|
522
522
|
method_def_indent = " " * (processor.resolve_indent - 2)
|
data/lib/graphql/version.rb
CHANGED
@@ -13,13 +13,13 @@ module Platform
|
|
13
13
|
field :starting_line, Integer, description: "The starting line for the range", null: false
|
14
14
|
|
15
15
|
def starting_line
|
16
|
-
|
16
|
+
object.lines.first[:lineno]
|
17
17
|
end
|
18
18
|
|
19
19
|
field :ending_line, Integer, description: "The ending line for the range", null: false
|
20
20
|
|
21
21
|
def ending_line
|
22
|
-
|
22
|
+
object.lines.first[:lineno] + (object.lines.length - 1)
|
23
23
|
end
|
24
24
|
|
25
25
|
field :commit, Objects::Commit, description: "Identifies the line author", null: false
|
@@ -12,13 +12,13 @@ module Platform
|
|
12
12
|
|
13
13
|
def resolve(**inputs)
|
14
14
|
project = Platform::Helpers::NodeIdentification.typed_object_from_id(
|
15
|
-
[Objects::Project], inputs[:project_id],
|
15
|
+
[Objects::Project], inputs[:project_id], context
|
16
16
|
)
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
context[:permission].can_modify?("DeleteProject", project).sync
|
19
|
+
context[:abilities].authorize_content(:project, :destroy, owner: project.owner)
|
20
20
|
|
21
|
-
project.enqueue_delete(actor:
|
21
|
+
project.enqueue_delete(actor: context[:viewer])
|
22
22
|
|
23
23
|
{ owner: project.owner }
|
24
24
|
end
|
@@ -20,7 +20,7 @@ module Platform
|
|
20
20
|
{ abcDef: 1 }
|
21
21
|
some_method do { xyzAbc: 1 } end
|
22
22
|
|
23
|
-
thing = Platform::Helpers::NodeIdentification.typed_object_from_id(Objects::Thing, inputs[:thing_id],
|
23
|
+
thing = Platform::Helpers::NodeIdentification.typed_object_from_id(Objects::Thing, inputs[:thing_id], context)
|
24
24
|
raise Errors::Validation.new("Thing not found.") unless thing
|
25
25
|
|
26
26
|
ThingActivity.track(thing.id, Time.now.change(min: 0, sec: 0))
|
@@ -13,9 +13,9 @@ module Platform
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def viewer_has_starred(**arguments)
|
16
|
-
if
|
16
|
+
if context[:viewer]
|
17
17
|
->(test_inner_proc) do
|
18
|
-
|
18
|
+
context[:viewer].starred?(object)
|
19
19
|
end
|
20
20
|
else
|
21
21
|
false
|
@@ -27,11 +27,11 @@ module Platform
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def stargazers(**arguments)
|
30
|
-
scope = case
|
30
|
+
scope = case object
|
31
31
|
when Repository
|
32
|
-
|
32
|
+
object.stars
|
33
33
|
when Gist
|
34
|
-
GistStar.where(gist_id:
|
34
|
+
GistStar.where(gist_id: object.id)
|
35
35
|
end
|
36
36
|
|
37
37
|
table = scope.table_name
|
@@ -11,17 +11,17 @@ module Platform
|
|
11
11
|
field :viewer_subscription, Enums::SubscriptionState, description: "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.", null: false
|
12
12
|
|
13
13
|
def viewer_subscription
|
14
|
-
if
|
14
|
+
if context[:viewer].nil?
|
15
15
|
return "unsubscribed"
|
16
16
|
end
|
17
17
|
|
18
|
-
subscription_status_response =
|
18
|
+
subscription_status_response = object.async_subscription_status(context[:viewer]).sync
|
19
19
|
|
20
20
|
if subscription_status_response.failed?
|
21
21
|
error = Platform::Errors::ServiceUnavailable.new("Subscriptions are currently unavailable. Please try again later.")
|
22
|
-
error.ast_node =
|
23
|
-
error.path =
|
24
|
-
|
22
|
+
error.ast_node = context.irep_node.ast_node
|
23
|
+
error.path = context.path
|
24
|
+
context.errors << error
|
25
25
|
return "unavailable"
|
26
26
|
end
|
27
27
|
|
@@ -38,9 +38,9 @@ module Platform
|
|
38
38
|
field :viewer_can_subscribe, Boolean, description: "Check if the viewer is able to change their subscription status for the repository.", null: false
|
39
39
|
|
40
40
|
def viewer_can_subscribe
|
41
|
-
return false if
|
41
|
+
return false if context[:viewer].nil?
|
42
42
|
|
43
|
-
|
43
|
+
object.async_subscription_status(context[:viewer]).then(&:success?)
|
44
44
|
end
|
45
45
|
|
46
46
|
field :issues, function: Platform::Functions::Issues.new, description: "A list of issues associated with the milestone.", connection: true
|
@@ -23,12 +23,12 @@ module Platform
|
|
23
23
|
def f4(**arguments)
|
24
24
|
Class1.new(
|
25
25
|
a: Class2.new(
|
26
|
-
b:
|
27
|
-
c:
|
26
|
+
b: object.b_1,
|
27
|
+
c: object.c_1
|
28
28
|
),
|
29
29
|
d: Class3.new(
|
30
|
-
b:
|
31
|
-
c:
|
30
|
+
b: object.b_2,
|
31
|
+
c: object.c_3,
|
32
32
|
)
|
33
33
|
)
|
34
34
|
end
|
@@ -46,9 +46,9 @@ module Platform
|
|
46
46
|
field :f10, String, null: true
|
47
47
|
|
48
48
|
def f10
|
49
|
-
|
49
|
+
object.something do |_|
|
50
50
|
xyz_obj.obj
|
51
|
-
|
51
|
+
object.f10
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -92,7 +92,7 @@ describe GraphQL::Execution::Multiplex do
|
|
92
92
|
},
|
93
93
|
{
|
94
94
|
"errors" => [{
|
95
|
-
"message"=>"
|
95
|
+
"message"=>"Field must have selections (field 'nullableNestedSum' returns LazySum but has no selections. Did you mean 'nullableNestedSum { ... }'?)",
|
96
96
|
"locations"=>[{"line"=>1, "column"=>4}],
|
97
97
|
"fields"=>["query", "validationError"]
|
98
98
|
}]
|
@@ -271,7 +271,26 @@ describe GraphQL::ExecutionError do
|
|
271
271
|
assert_equal(expected_result, result)
|
272
272
|
end
|
273
273
|
end
|
274
|
-
|
274
|
+
|
275
|
+
describe "extensions in ExecutionError" do
|
276
|
+
let(:query_string) {%|
|
277
|
+
{
|
278
|
+
executionErrorWithExtensions
|
279
|
+
}
|
280
|
+
|}
|
281
|
+
it "the error is inserted into the errors key with custom data set in `extensions`" do
|
282
|
+
expected_result = {
|
283
|
+
"data"=>{"executionErrorWithExtensions"=>nil},
|
284
|
+
"errors"=>
|
285
|
+
[{"message"=>"Permission Denied!",
|
286
|
+
"locations"=>[{"line"=>3, "column"=>7}],
|
287
|
+
"path"=>["executionErrorWithExtensions"],
|
288
|
+
"extensions"=>{"code"=>"permission_denied"}}]
|
289
|
+
}
|
290
|
+
assert_equal(expected_result, result)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
275
294
|
describe "more than one ExecutionError" do
|
276
295
|
let(:query_string) { %|{ multipleErrorsOnNonNullableField} |}
|
277
296
|
it "the errors are inserted into the errors key and the data is nil even for a NonNullable field " do
|
@@ -30,6 +30,7 @@ describe GraphQL::Introspection::SchemaType do
|
|
30
30
|
{"name"=>"deepNonNull"},
|
31
31
|
{"name"=>"error"},
|
32
32
|
{"name"=>"executionError"},
|
33
|
+
{"name"=>"executionErrorWithExtensions"},
|
33
34
|
{"name"=>"executionErrorWithOptions"},
|
34
35
|
{"name"=>"favoriteEdible"},
|
35
36
|
{"name"=>"fromSource"},
|
@@ -1,7 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
|
4
|
+
if MONGO_DETECTED
|
5
|
+
require "support/star_trek/data"
|
6
|
+
require "support/star_trek/schema"
|
7
|
+
end
|
8
|
+
|
9
|
+
describe GraphQL::Relay::MongoRelationConnection do
|
10
|
+
before do
|
11
|
+
if !MONGO_DETECTED
|
12
|
+
skip("Mongo not detected")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
def get_names(result)
|
6
17
|
ships = result["data"]["federation"]["bases"]["edges"]
|
7
18
|
ships.map { |e| e["node"]["name"] }
|
@@ -358,6 +358,135 @@ describe GraphQL::Schema::Warden do
|
|
358
358
|
assert_equal false, possible_type_names(res["data"]["LanguageMember"]).include?("Phoneme")
|
359
359
|
end
|
360
360
|
|
361
|
+
it "hides interfaces if all possible types are hidden" do
|
362
|
+
sdl = %|
|
363
|
+
type Query {
|
364
|
+
a: String
|
365
|
+
repository: Repository
|
366
|
+
}
|
367
|
+
|
368
|
+
type Repository implements Node {
|
369
|
+
id: ID!
|
370
|
+
}
|
371
|
+
|
372
|
+
interface Node {
|
373
|
+
id: ID!
|
374
|
+
}
|
375
|
+
|
|
376
|
+
|
377
|
+
schema = GraphQL::Schema.from_definition(sdl)
|
378
|
+
|
379
|
+
query_string = %|
|
380
|
+
{
|
381
|
+
Node: __type(name: "Node") { name }
|
382
|
+
}
|
383
|
+
|
|
384
|
+
|
385
|
+
res = schema.execute(query_string)
|
386
|
+
assert res["data"]["Node"]
|
387
|
+
|
388
|
+
res = schema.execute(query_string, except: ->(m, _) { m.name == "Repository" })
|
389
|
+
assert_nil res["data"]["Node"]
|
390
|
+
end
|
391
|
+
|
392
|
+
it "hides unions if all possible types are hidden or its references are hidden" do
|
393
|
+
sdl = "
|
394
|
+
type Query {
|
395
|
+
bag: BagOfThings
|
396
|
+
}
|
397
|
+
|
398
|
+
type A {
|
399
|
+
id: ID!
|
400
|
+
}
|
401
|
+
|
402
|
+
type B {
|
403
|
+
id: ID!
|
404
|
+
}
|
405
|
+
|
406
|
+
type C {
|
407
|
+
id: ID!
|
408
|
+
}
|
409
|
+
|
410
|
+
union BagOfThings = A | B | C
|
411
|
+
"
|
412
|
+
|
413
|
+
schema = GraphQL::Schema.from_definition(sdl)
|
414
|
+
schema.orphan_types = []
|
415
|
+
|
416
|
+
query_string = %|
|
417
|
+
{
|
418
|
+
BagOfThings: __type(name: "BagOfThings") { name }
|
419
|
+
Query: __type(name: "Query") { fields { name } }
|
420
|
+
}
|
421
|
+
|
|
422
|
+
|
423
|
+
res = schema.execute(query_string)
|
424
|
+
assert res["data"]["BagOfThings"]
|
425
|
+
assert_equal ["bag"], res["data"]["Query"]["fields"].map { |f| f["name"] }
|
426
|
+
|
427
|
+
# Hide the union when all its possible types are gone. This will cause the field to be hidden too.
|
428
|
+
res = schema.execute(query_string, except: ->(m, _) { ["A", "B", "C"].include?(m.name) })
|
429
|
+
assert_nil res["data"]["BagOfThings"]
|
430
|
+
assert_equal [], res["data"]["Query"]["fields"]
|
431
|
+
|
432
|
+
res = schema.execute(query_string, except: ->(m, _) { m.name == "bag" })
|
433
|
+
assert_nil res["data"]["BagOfThings"]
|
434
|
+
assert_equal [], res["data"]["Query"]["fields"]
|
435
|
+
|
436
|
+
# Unreferenced but still visible because orphan type
|
437
|
+
schema.orphan_types = [schema.find("BagOfThings")]
|
438
|
+
res = schema.execute(query_string, except: ->(m, _) { m.name == "bag" })
|
439
|
+
assert res["data"]["BagOfThings"]
|
440
|
+
end
|
441
|
+
|
442
|
+
it "hides interfaces if all possible types are hidden or its references are hidden" do
|
443
|
+
sdl = "
|
444
|
+
type Query {
|
445
|
+
node: Node
|
446
|
+
}
|
447
|
+
|
448
|
+
type A implements Node {
|
449
|
+
id: ID!
|
450
|
+
}
|
451
|
+
|
452
|
+
type B implements Node {
|
453
|
+
id: ID!
|
454
|
+
}
|
455
|
+
|
456
|
+
type C implements Node {
|
457
|
+
id: ID!
|
458
|
+
}
|
459
|
+
|
460
|
+
interface Node {
|
461
|
+
id: ID!
|
462
|
+
}
|
463
|
+
"
|
464
|
+
|
465
|
+
schema = GraphQL::Schema.from_definition(sdl)
|
466
|
+
|
467
|
+
query_string = %|
|
468
|
+
{
|
469
|
+
Node: __type(name: "Node") { name }
|
470
|
+
Query: __type(name: "Query") { fields { name } }
|
471
|
+
}
|
472
|
+
|
|
473
|
+
|
474
|
+
res = schema.execute(query_string)
|
475
|
+
assert res["data"]["Node"]
|
476
|
+
assert_equal ["node"], res["data"]["Query"]["fields"].map { |f| f["name"] }
|
477
|
+
|
478
|
+
# When the possible types are all hidden, hide the interface and fields pointing to it
|
479
|
+
res = schema.execute(query_string, except: ->(m, _) { ["A", "B", "C"].include?(m.name) })
|
480
|
+
assert_nil res["data"]["Node"]
|
481
|
+
assert_equal [], res["data"]["Query"]["fields"]
|
482
|
+
|
483
|
+
# Even when it's not the return value of a field,
|
484
|
+
# still show the interface since it allows code reuse
|
485
|
+
res = schema.execute(query_string, except: ->(m, _) { m.name == "node" })
|
486
|
+
assert_equal "Node", res["data"]["Node"]["name"]
|
487
|
+
assert_equal [], res["data"]["Query"]["fields"]
|
488
|
+
end
|
489
|
+
|
361
490
|
it "can't be a fragment condition" do
|
362
491
|
query_string = %|
|
363
492
|
{
|
@@ -6,32 +6,40 @@ describe GraphQL::StaticValidation::FieldsHaveAppropriateSelections do
|
|
6
6
|
let(:query_string) {"
|
7
7
|
query getCheese {
|
8
8
|
okCheese: cheese(id: 1) { fatContent, similarCheese(source: YAK) { source } }
|
9
|
-
|
9
|
+
missingFieldsObject: cheese(id: 1)
|
10
|
+
missingFieldsInterface: cheese(id: 1) { selfAsEdible }
|
10
11
|
illegalSelectionCheese: cheese(id: 1) { id { something, ... someFields } }
|
11
12
|
incorrectFragmentSpread: cheese(id: 1) { flavor { ... on String { __typename } } }
|
12
13
|
}
|
13
14
|
"}
|
14
15
|
|
15
16
|
it "adds errors for selections on scalars" do
|
16
|
-
assert_equal(
|
17
|
+
assert_equal(4, errors.length)
|
17
18
|
|
18
19
|
illegal_selection_error = {
|
19
20
|
"message"=>"Selections can't be made on scalars (field 'id' returns Int but has selections [something, someFields])",
|
20
|
-
"locations"=>[{"line"=>
|
21
|
+
"locations"=>[{"line"=>6, "column"=>47}],
|
21
22
|
"fields"=>["query getCheese", "illegalSelectionCheese", "id"],
|
22
23
|
}
|
23
24
|
assert_includes(errors, illegal_selection_error, "finds illegal selections on scalars")
|
24
25
|
|
25
|
-
|
26
|
-
"message"=>"
|
26
|
+
objects_selection_required_error = {
|
27
|
+
"message"=>"Field must have selections (field 'cheese' returns Cheese but has no selections. Did you mean 'cheese { ... }'?)",
|
27
28
|
"locations"=>[{"line"=>4, "column"=>7}],
|
28
|
-
"fields"=>["query getCheese", "
|
29
|
+
"fields"=>["query getCheese", "missingFieldsObject"],
|
30
|
+
}
|
31
|
+
assert_includes(errors, objects_selection_required_error, "finds objects without selections")
|
32
|
+
|
33
|
+
interfaces_selection_required_error = {
|
34
|
+
"message"=>"Field must have selections (field 'selfAsEdible' returns Edible but has no selections. Did you mean 'selfAsEdible { ... }'?)",
|
35
|
+
"locations"=>[{"line"=>5, "column"=>47}],
|
36
|
+
"fields"=>["query getCheese", "missingFieldsInterface", "selfAsEdible"],
|
29
37
|
}
|
30
|
-
assert_includes(errors,
|
38
|
+
assert_includes(errors, interfaces_selection_required_error, "finds interfaces without selections")
|
31
39
|
|
32
40
|
incorrect_fragment_error = {
|
33
41
|
"message"=>"Selections can't be made on scalars (field 'flavor' returns String but has inline fragments [String])",
|
34
|
-
"locations"=>[{"line"=>
|
42
|
+
"locations"=>[{"line"=>7, "column"=>48}],
|
35
43
|
"fields"=>["query getCheese", "incorrectFragmentSpread", "flavor"],
|
36
44
|
}
|
37
45
|
assert_includes(errors, incorrect_fragment_error, "finds scalar fields with selections")
|
@@ -43,7 +51,7 @@ describe GraphQL::StaticValidation::FieldsHaveAppropriateSelections do
|
|
43
51
|
assert_equal(1, errors.length)
|
44
52
|
|
45
53
|
selections_required_error = {
|
46
|
-
"message"=> "
|
54
|
+
"message"=> "Field must have selections (anonymous query returns Query but has no selections. Did you mean ' { ... }'?)",
|
47
55
|
"locations"=>[{"line"=>1, "column"=>1}],
|
48
56
|
"fields"=>["query"]
|
49
57
|
}
|
@@ -195,7 +195,7 @@ RUBY
|
|
195
195
|
end
|
196
196
|
|
197
197
|
describe "resolve proc to method" do
|
198
|
-
it "converts
|
198
|
+
it "converts object and context" do
|
199
199
|
old = %{
|
200
200
|
field :firstName, !types.String do
|
201
201
|
resolve ->(obj, arg, ctx) {
|
@@ -211,11 +211,11 @@ RUBY
|
|
211
211
|
field :first_name, String, null: false
|
212
212
|
|
213
213
|
def first_name
|
214
|
-
|
214
|
+
context.something
|
215
215
|
other_ctx # test combined identifiers
|
216
216
|
|
217
|
-
|
218
|
-
|
217
|
+
object[context] + object
|
218
|
+
object.given_name
|
219
219
|
end
|
220
220
|
}
|
221
221
|
assert_equal new, upgrade(old)
|
@@ -233,7 +233,7 @@ RUBY
|
|
233
233
|
field :first_name, String, null: false
|
234
234
|
|
235
235
|
def first_name
|
236
|
-
|
236
|
+
object.given_name
|
237
237
|
end
|
238
238
|
}
|
239
239
|
assert_equal new, upgrade(old)
|
data/spec/spec_helper.rb
CHANGED
@@ -27,6 +27,22 @@ require "pry"
|
|
27
27
|
require "minitest/autorun"
|
28
28
|
require "minitest/focus"
|
29
29
|
require "minitest/reporters"
|
30
|
+
|
31
|
+
MONGO_DETECTED = begin
|
32
|
+
require "mongo"
|
33
|
+
Mongo::Client.new('mongodb://127.0.0.1:27017/graphql_ruby_test',
|
34
|
+
connect_timeout: 1,
|
35
|
+
socket_timeout: 1,
|
36
|
+
server_selection_timeout: 1,
|
37
|
+
logger: Logger.new(nil)
|
38
|
+
)
|
39
|
+
.database
|
40
|
+
.collections
|
41
|
+
rescue StandardError, LoadError => err # rubocop:disable Lint/UselessAssignment
|
42
|
+
# puts err.message, err.backtrace
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
30
46
|
Minitest::Reporters.use! Minitest::Reporters::DefaultReporter.new(color: true)
|
31
47
|
|
32
48
|
Minitest::Spec.make_my_diffs_pretty!
|
@@ -62,9 +78,12 @@ NO_OP_RESOLVE_TYPE = ->(type, obj, ctx) {
|
|
62
78
|
|
63
79
|
# Load support files
|
64
80
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |f|
|
81
|
+
# These require mongodb in order to run,
|
82
|
+
# so only load them in the specific tests that require them.
|
83
|
+
next if f.include?("star_trek")
|
84
|
+
|
65
85
|
unless rails_should_be_installed?
|
66
86
|
next if f.end_with?('star_wars/data.rb')
|
67
|
-
next if f.end_with?('star_trek/data.rb')
|
68
87
|
next if f.end_with?('base_generator_test.rb')
|
69
88
|
end
|
70
89
|
require f
|
@@ -381,6 +381,13 @@ module Dummy
|
|
381
381
|
}
|
382
382
|
end
|
383
383
|
|
384
|
+
field :executionErrorWithExtensions do
|
385
|
+
type GraphQL::INT_TYPE
|
386
|
+
resolve ->(t, a, c) {
|
387
|
+
GraphQL::ExecutionError.new("Permission Denied!", extensions: { "code" => "permission_denied" })
|
388
|
+
}
|
389
|
+
end
|
390
|
+
|
384
391
|
# To test possibly-null fields
|
385
392
|
field :maybeNull, MaybeNullType do
|
386
393
|
resolve ->(t, a, c) { OpenStruct.new(cheese: nil) }
|
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.8.
|
4
|
+
version: 1.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -304,20 +304,6 @@ dependencies:
|
|
304
304
|
- - ">="
|
305
305
|
- !ruby/object:Gem::Version
|
306
306
|
version: '0'
|
307
|
-
- !ruby/object:Gem::Dependency
|
308
|
-
name: algoliasearch-jekyll
|
309
|
-
requirement: !ruby/object:Gem::Requirement
|
310
|
-
requirements:
|
311
|
-
- - ">="
|
312
|
-
- !ruby/object:Gem::Version
|
313
|
-
version: '0'
|
314
|
-
type: :development
|
315
|
-
prerelease: false
|
316
|
-
version_requirements: !ruby/object:Gem::Requirement
|
317
|
-
requirements:
|
318
|
-
- - ">="
|
319
|
-
- !ruby/object:Gem::Version
|
320
|
-
version: '0'
|
321
307
|
description: A plain-Ruby implementation of GraphQL.
|
322
308
|
email:
|
323
309
|
- rdmosolgo@gmail.com
|
@@ -433,12 +419,9 @@ files:
|
|
433
419
|
- lib/graphql/introspection/field_type.rb
|
434
420
|
- lib/graphql/introspection/input_value_type.rb
|
435
421
|
- lib/graphql/introspection/introspection_query.rb
|
436
|
-
- lib/graphql/introspection/schema_field.rb
|
437
422
|
- lib/graphql/introspection/schema_type.rb
|
438
|
-
- lib/graphql/introspection/type_by_name_field.rb
|
439
423
|
- lib/graphql/introspection/type_kind_enum.rb
|
440
424
|
- lib/graphql/introspection/type_type.rb
|
441
|
-
- lib/graphql/introspection/typename_field.rb
|
442
425
|
- lib/graphql/invalid_name_error.rb
|
443
426
|
- lib/graphql/invalid_null_error.rb
|
444
427
|
- lib/graphql/language.rb
|
@@ -1005,7 +988,6 @@ files:
|
|
1005
988
|
- spec/graphql/introspection/input_value_type_spec.rb
|
1006
989
|
- spec/graphql/introspection/introspection_query_spec.rb
|
1007
990
|
- spec/graphql/introspection/schema_type_spec.rb
|
1008
|
-
- spec/graphql/introspection/type_by_name_field_spec.rb
|
1009
991
|
- spec/graphql/introspection/type_type_spec.rb
|
1010
992
|
- spec/graphql/language/block_string_spec.rb
|
1011
993
|
- spec/graphql/language/definition_slice_spec.rb
|
@@ -1553,7 +1535,6 @@ test_files:
|
|
1553
1535
|
- spec/graphql/introspection/input_value_type_spec.rb
|
1554
1536
|
- spec/graphql/introspection/introspection_query_spec.rb
|
1555
1537
|
- spec/graphql/introspection/schema_type_spec.rb
|
1556
|
-
- spec/graphql/introspection/type_by_name_field_spec.rb
|
1557
1538
|
- spec/graphql/introspection/type_type_spec.rb
|
1558
1539
|
- spec/graphql/language/block_string_spec.rb
|
1559
1540
|
- spec/graphql/language/definition_slice_spec.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Introspection
|
4
|
-
SchemaField = GraphQL::Field.define do
|
5
|
-
name("__schema")
|
6
|
-
description("This GraphQL schema")
|
7
|
-
type(GraphQL::Schema::LateBoundType.new("__Schema").to_non_null_type)
|
8
|
-
resolve ->(o, a, ctx) {
|
9
|
-
# Apply wrapping manually since this field isn't wrapped by instrumentation
|
10
|
-
schema = ctx.query.schema
|
11
|
-
schema_type = schema.introspection_system.schema_type
|
12
|
-
schema_type.metadata[:type_class].new(schema, ctx.query.context)
|
13
|
-
}
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Introspection
|
4
|
-
TypeByNameField = GraphQL::Field.define do
|
5
|
-
name("__type")
|
6
|
-
description("A type in the GraphQL system")
|
7
|
-
introspection true
|
8
|
-
type(GraphQL::Schema::LateBoundType.new("__Type"))
|
9
|
-
argument :name, !types.String
|
10
|
-
resolve ->(o, args, ctx) {
|
11
|
-
type = ctx.warden.get_type(args["name"])
|
12
|
-
if type
|
13
|
-
# Apply wrapping manually since this field isn't wrapped by instrumentation
|
14
|
-
type_type = ctx.schema.introspection_system.type_type
|
15
|
-
type_type.metadata[:type_class].new(type, ctx)
|
16
|
-
else
|
17
|
-
nil
|
18
|
-
end
|
19
|
-
}
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Introspection
|
4
|
-
TypenameField = GraphQL::Field.define do
|
5
|
-
name "__typename"
|
6
|
-
description "The name of this type"
|
7
|
-
type -> { !GraphQL::STRING_TYPE }
|
8
|
-
introspection true
|
9
|
-
resolve ->(obj, a, ctx) { ctx.irep_node.owner_type }
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "spec_helper"
|
3
|
-
|
4
|
-
describe GraphQL::Introspection::TypeByNameField do
|
5
|
-
describe "after instrumentation" do
|
6
|
-
# Just make sure it returns a new object, not the original field
|
7
|
-
class DupInstrumenter
|
8
|
-
def self.instrument(t, f)
|
9
|
-
f.redefine {
|
10
|
-
resolve ->(o, a, c) { :no_op }
|
11
|
-
}
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class ArgAnalyzer
|
16
|
-
def call(_, _, node)
|
17
|
-
if node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
|
18
|
-
node.arguments
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:instrumented_schema) {
|
24
|
-
# This was probably assigned earlier in the test suite, but to simulate an application, clear it.
|
25
|
-
GraphQL::Introspection::TypeByNameField.arguments_class = nil
|
26
|
-
|
27
|
-
Dummy::Schema.redefine {
|
28
|
-
instrument(:field, DupInstrumenter)
|
29
|
-
query_analyzer(ArgAnalyzer.new)
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
it "still works with __type" do
|
34
|
-
res = instrumented_schema.execute("{ __type(name: \"X\") { name } }")
|
35
|
-
assert_equal({"data"=>{"__type"=>nil}}, res)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|