graphql 1.8.0.pre7 → 1.8.0.pre8
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/argument.rb +24 -19
- data/lib/graphql/backtrace/tracer.rb +16 -22
- data/lib/graphql/base_type.rb +6 -1
- data/lib/graphql/execution/execute.rb +15 -13
- data/lib/graphql/execution/lazy/resolve.rb +1 -3
- data/lib/graphql/interface_type.rb +5 -3
- data/lib/graphql/language/document_from_schema_definition.rb +1 -1
- data/lib/graphql/language/lexer.rb +65 -51
- data/lib/graphql/language/lexer.rl +2 -0
- data/lib/graphql/language/nodes.rb +118 -71
- data/lib/graphql/language/parser.rb +699 -652
- data/lib/graphql/language/parser.y +11 -5
- data/lib/graphql/language/printer.rb +2 -2
- data/lib/graphql/object_type.rb +0 -5
- data/lib/graphql/relay/relation_connection.rb +1 -1
- data/lib/graphql/schema.rb +15 -3
- data/lib/graphql/schema/argument.rb +18 -1
- data/lib/graphql/schema/enum.rb +5 -2
- data/lib/graphql/schema/enum_value.rb +8 -1
- data/lib/graphql/schema/field.rb +10 -3
- data/lib/graphql/schema/input_object.rb +4 -2
- data/lib/graphql/schema/interface.rb +15 -0
- data/lib/graphql/schema/member.rb +2 -1
- data/lib/graphql/schema/member/accepts_definition.rb +118 -0
- data/lib/graphql/schema/member/build_type.rb +2 -2
- data/lib/graphql/schema/member/has_fields.rb +3 -2
- data/lib/graphql/schema/object.rb +4 -2
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/traversal.rb +3 -0
- data/lib/graphql/schema/union.rb +6 -11
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/argument_spec.rb +21 -0
- data/spec/graphql/base_type_spec.rb +22 -0
- data/spec/graphql/enum_type_spec.rb +18 -5
- data/spec/graphql/execution/execute_spec.rb +3 -3
- data/spec/graphql/input_object_type_spec.rb +13 -0
- data/spec/graphql/interface_type_spec.rb +12 -0
- data/spec/graphql/language/nodes_spec.rb +0 -12
- data/spec/graphql/language/parser_spec.rb +74 -0
- data/spec/graphql/language/printer_spec.rb +1 -1
- data/spec/graphql/object_type_spec.rb +21 -0
- data/spec/graphql/relay/range_add_spec.rb +5 -1
- data/spec/graphql/relay/relation_connection_spec.rb +7 -1
- data/spec/graphql/schema/argument_spec.rb +31 -0
- data/spec/graphql/schema/enum_spec.rb +5 -0
- data/spec/graphql/schema/field_spec.rb +11 -1
- data/spec/graphql/schema/input_object_spec.rb +5 -0
- data/spec/graphql/schema/interface_spec.rb +29 -0
- data/spec/graphql/schema/member/accepts_definition_spec.rb +62 -0
- data/spec/graphql/schema/printer_spec.rb +34 -0
- data/spec/graphql/schema/traversal_spec.rb +31 -0
- data/spec/graphql/schema/union_spec.rb +30 -0
- data/spec/graphql/schema_spec.rb +6 -0
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
- data/spec/graphql/tracing/active_support_notifications_tracing_spec.rb +1 -1
- data/spec/graphql/union_type_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/dummy/schema.rb +1 -4
- metadata +7 -2
@@ -109,8 +109,8 @@ module GraphQL
|
|
109
109
|
return string unless string.include?("_")
|
110
110
|
camelized = string.split('_').map(&:capitalize).join
|
111
111
|
camelized[0] = camelized[0].downcase
|
112
|
-
if string.
|
113
|
-
camelized = "
|
112
|
+
if (match_data = string.match(/\A(_+)/))
|
113
|
+
camelized = "#{match_data[0]}#{camelized}"
|
114
114
|
end
|
115
115
|
camelized
|
116
116
|
end
|
@@ -7,8 +7,9 @@ module GraphQL
|
|
7
7
|
# Add a field to this object or interface with the given definition
|
8
8
|
# @see {GraphQL::Schema::Field#initialize} for method signature
|
9
9
|
# @return [void]
|
10
|
-
def field(*args, &block)
|
11
|
-
|
10
|
+
def field(*args, **kwargs, &block)
|
11
|
+
kwargs[:owner] = self
|
12
|
+
field_defn = field_class.new(*args, **kwargs, &block)
|
12
13
|
add_field(field_defn)
|
13
14
|
nil
|
14
15
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module GraphQL
|
4
4
|
class Schema
|
5
5
|
class Object < GraphQL::Schema::Member
|
6
|
+
extend GraphQL::Schema::Member::AcceptsDefinition
|
7
|
+
|
6
8
|
# @return [Object] the application object this type is wrapping
|
7
9
|
attr_reader :object
|
8
10
|
|
@@ -22,8 +24,8 @@ module GraphQL
|
|
22
24
|
new_interfaces.each do |int|
|
23
25
|
if int.is_a?(Class) && int < GraphQL::Schema::Interface
|
24
26
|
# Add the graphql field defns
|
25
|
-
int.fields.each do |
|
26
|
-
|
27
|
+
int.fields.each do |name, field|
|
28
|
+
own_fields[name] = field
|
27
29
|
end
|
28
30
|
# And call the implemented hook
|
29
31
|
int.apply_implemented(self)
|
@@ -2,6 +2,8 @@
|
|
2
2
|
module GraphQL
|
3
3
|
class Schema
|
4
4
|
class Scalar < GraphQL::Schema::Member
|
5
|
+
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
|
+
|
5
7
|
class << self
|
6
8
|
def coerce_input(val, ctx)
|
7
9
|
raise NotImplementedError, "#{self.name}.coerce_input(val, ctx) must prepare GraphQL input (#{val.inspect}) for Ruby processing"
|
@@ -136,6 +136,9 @@ Some late-bound types couldn't be resolved:
|
|
136
136
|
visit_fields(schema, type_defn)
|
137
137
|
when GraphQL::InterfaceType
|
138
138
|
visit_fields(schema, type_defn)
|
139
|
+
type_defn.orphan_types.each do |t|
|
140
|
+
visit(schema, t, "Orphan type for #{type_defn.name}")
|
141
|
+
end
|
139
142
|
when GraphQL::UnionType
|
140
143
|
type_defn.possible_types.each do |t|
|
141
144
|
@union_memberships[t.name] << type_defn
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -2,30 +2,25 @@
|
|
2
2
|
module GraphQL
|
3
3
|
class Schema
|
4
4
|
class Union < GraphQL::Schema::Member
|
5
|
+
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
|
+
|
5
7
|
class << self
|
6
8
|
def possible_types(*types)
|
7
9
|
if types.any?
|
8
|
-
@
|
10
|
+
@possible_types = types
|
9
11
|
else
|
10
|
-
all_possible_types =
|
11
|
-
|
12
|
-
all_possible_types += inherited_possible_types
|
12
|
+
all_possible_types = @possible_types || []
|
13
|
+
all_possible_types += super if defined?(super)
|
13
14
|
all_possible_types.uniq
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
|
-
def own_possible_types
|
18
|
-
@own_possible_types ||= []
|
19
|
-
end
|
20
|
-
|
21
18
|
def to_graphql
|
22
19
|
type_defn = GraphQL::UnionType.new
|
23
20
|
type_defn.name = graphql_name
|
24
21
|
type_defn.description = description
|
25
22
|
type_defn.possible_types = possible_types
|
26
|
-
|
27
|
-
# resolve type hook, via the class method
|
28
|
-
if method_defined?(:resolve_type)
|
23
|
+
if respond_to?(:resolve_type)
|
29
24
|
type_defn.resolve_type = method(:resolve_type)
|
30
25
|
end
|
31
26
|
type_defn
|
data/lib/graphql/version.rb
CHANGED
@@ -32,6 +32,27 @@ describe GraphQL::Argument do
|
|
32
32
|
assert_equal GraphQL::STRING_TYPE, arg.type
|
33
33
|
end
|
34
34
|
|
35
|
+
it "accepts a definition block after defining kwargs" do
|
36
|
+
arg = GraphQL::Argument.from_dsl(:foo, GraphQL::STRING_TYPE) do
|
37
|
+
description "my type is #{target.type}"
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_equal "my type is String", arg.description
|
41
|
+
end
|
42
|
+
|
43
|
+
it "accepts a definition block with existing arg" do
|
44
|
+
existing = GraphQL::Argument.define do
|
45
|
+
name "bar"
|
46
|
+
type GraphQL::STRING_TYPE
|
47
|
+
end
|
48
|
+
|
49
|
+
arg = GraphQL::Argument.from_dsl(:foo, existing) do
|
50
|
+
description "Description for an existing field."
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_equal "Description for an existing field.", arg.description
|
54
|
+
end
|
55
|
+
|
35
56
|
it "creates an argument from dsl arguments" do
|
36
57
|
arg = GraphQL::Argument.from_dsl(
|
37
58
|
:foo,
|
@@ -28,6 +28,28 @@ describe GraphQL::BaseType do
|
|
28
28
|
assert_equal ["Cheese"], Dummy::CheeseType.metadata[:class_names]
|
29
29
|
end
|
30
30
|
|
31
|
+
describe "#name" do
|
32
|
+
describe "when containing spaces" do
|
33
|
+
BaseNameSpaceTest = GraphQL::BaseType.define do
|
34
|
+
name "Some Invalid Name"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "is invalid" do
|
38
|
+
assert_raises(GraphQL::InvalidNameError) { BaseNameSpaceTest.name }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "when containing colons" do
|
43
|
+
BaseNameColonsTest = GraphQL::BaseType.define do
|
44
|
+
name "Some::Invalid::Name"
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'is invalid' do
|
48
|
+
assert_raises(GraphQL::InvalidNameError) { BaseNameColonsTest.name }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
31
53
|
describe "name" do
|
32
54
|
it "fails with a helpful message" do
|
33
55
|
error = assert_raises RuntimeError do
|
@@ -37,21 +37,34 @@ describe GraphQL::EnumType do
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
describe "invalid
|
41
|
-
it "rejects names with a space" do
|
40
|
+
describe "invalid values" do
|
41
|
+
it "rejects value names with a space" do
|
42
42
|
assert_raises(GraphQL::InvalidNameError) {
|
43
|
-
|
44
|
-
name "
|
43
|
+
InvalidEnumValueTest = GraphQL::EnumType.define do
|
44
|
+
name "InvalidEnumValueTest"
|
45
45
|
|
46
46
|
value("SPACE IN VALUE", "Invalid enum because it contains spaces", value: 1)
|
47
47
|
end
|
48
48
|
|
49
49
|
# Force evaluation
|
50
|
-
|
50
|
+
InvalidEnumValueTest.name
|
51
51
|
}
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
describe "invalid name" do
|
56
|
+
it "reject names with invalid format" do
|
57
|
+
assert_raises(GraphQL::InvalidNameError) do
|
58
|
+
InvalidEnumNameTest = GraphQL::EnumType.define do
|
59
|
+
name "Some::Invalid::Name"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Force evaluation
|
63
|
+
InvalidEnumNameTest.name
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
55
68
|
describe "values that are Arrays" do
|
56
69
|
let(:schema) {
|
57
70
|
enum = GraphQL::EnumType.define do
|
@@ -163,10 +163,10 @@ describe GraphQL::Execution::Execute do
|
|
163
163
|
"execute_field",
|
164
164
|
"execute_query",
|
165
165
|
"lazy_loader",
|
166
|
-
"execute_field",
|
167
166
|
"execute_field_lazy",
|
168
167
|
"execute_field",
|
169
168
|
"execute_field_lazy",
|
169
|
+
"execute_field",
|
170
170
|
"execute_field_lazy",
|
171
171
|
"execute_field_lazy",
|
172
172
|
"execute_query_lazy",
|
@@ -177,8 +177,8 @@ describe GraphQL::Execution::Execute do
|
|
177
177
|
field_1_eager, field_2_eager,
|
178
178
|
query_eager, lazy_loader,
|
179
179
|
# field 3 is eager-resolved _during_ field 1's lazy resolve
|
180
|
-
|
181
|
-
|
180
|
+
field_1_lazy, field_3_eager,
|
181
|
+
field_2_lazy, field_4_eager,
|
182
182
|
# field 3 didn't finish above, it's resolved in the next round
|
183
183
|
field_3_lazy, field_4_lazy,
|
184
184
|
query_lazy, multiplex = exec_traces
|
@@ -218,6 +218,19 @@ describe GraphQL::InputObjectType do
|
|
218
218
|
assert_equal(expected, actual)
|
219
219
|
end
|
220
220
|
end
|
221
|
+
|
222
|
+
describe 'with invalid name' do
|
223
|
+
it 'raises the correct error' do
|
224
|
+
assert_raises(GraphQL::InvalidNameError) do
|
225
|
+
InvalidInputTest = GraphQL::InputObjectType.define do
|
226
|
+
name "Some::Invalid Name"
|
227
|
+
end
|
228
|
+
|
229
|
+
# Force evaluation
|
230
|
+
InvalidInputTest.name
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
221
234
|
end
|
222
235
|
end
|
223
236
|
|
@@ -92,6 +92,18 @@ describe GraphQL::InterfaceType do
|
|
92
92
|
assert_equal 3, interface.fields.size
|
93
93
|
assert_equal 4, interface_2.fields.size
|
94
94
|
end
|
95
|
+
|
96
|
+
it "copies orphan types without affecting the original" do
|
97
|
+
interface = GraphQL::InterfaceType.define do
|
98
|
+
name "AInterface"
|
99
|
+
orphan_types [Dummy::HoneyType]
|
100
|
+
end
|
101
|
+
|
102
|
+
interface_2 = interface.dup
|
103
|
+
interface_2.orphan_types << Dummy::CheeseType
|
104
|
+
assert_equal 1, interface.orphan_types.size
|
105
|
+
assert_equal 2, interface_2.orphan_types.size
|
106
|
+
end
|
95
107
|
end
|
96
108
|
|
97
109
|
describe "#resolve_type" do
|
@@ -2,18 +2,6 @@
|
|
2
2
|
require "spec_helper"
|
3
3
|
|
4
4
|
describe GraphQL::Language::Nodes::AbstractNode do
|
5
|
-
describe "child and scalar attributes" do
|
6
|
-
it "are inherited by node subclasses" do
|
7
|
-
subclassed_directive = Class.new(GraphQL::Language::Nodes::Directive)
|
8
|
-
|
9
|
-
assert_equal GraphQL::Language::Nodes::Directive.scalar_attributes,
|
10
|
-
subclassed_directive.scalar_attributes
|
11
|
-
|
12
|
-
assert_equal GraphQL::Language::Nodes::Directive.child_attributes,
|
13
|
-
subclassed_directive.child_attributes
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
5
|
describe "#filename" do
|
18
6
|
it "is set after .parse_file" do
|
19
7
|
filename = "spec/support/parser/filename_example.graphql"
|
@@ -44,6 +44,80 @@ describe GraphQL::Language::Parser do
|
|
44
44
|
assert_equal schema_string, document.to_query_string
|
45
45
|
end
|
46
46
|
|
47
|
+
describe "implements" do
|
48
|
+
it "parses when there are no interfaces" do
|
49
|
+
schema = "
|
50
|
+
type A {
|
51
|
+
a: String
|
52
|
+
}
|
53
|
+
"
|
54
|
+
|
55
|
+
document = subject.parse(schema)
|
56
|
+
|
57
|
+
assert_equal [], document.definitions[0].interfaces.map(&:name)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "parses with leading ampersand" do
|
61
|
+
schema = "
|
62
|
+
type A implements & B {
|
63
|
+
a: String
|
64
|
+
}
|
65
|
+
"
|
66
|
+
|
67
|
+
document = subject.parse(schema)
|
68
|
+
|
69
|
+
assert_equal ["B"], document.definitions[0].interfaces.map(&:name)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "parses with leading ampersand and multiple interfaces" do
|
73
|
+
schema = "
|
74
|
+
type A implements & B & C {
|
75
|
+
a: String
|
76
|
+
}
|
77
|
+
"
|
78
|
+
|
79
|
+
document = subject.parse(schema)
|
80
|
+
|
81
|
+
assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "parses without leading ampersand" do
|
85
|
+
schema = "
|
86
|
+
type A implements B {
|
87
|
+
a: String
|
88
|
+
}
|
89
|
+
"
|
90
|
+
|
91
|
+
document = subject.parse(schema)
|
92
|
+
|
93
|
+
assert_equal ["B"], document.definitions[0].interfaces.map(&:name)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "parses without leading ampersand and multiple interfaces" do
|
97
|
+
schema = "
|
98
|
+
type A implements B & C {
|
99
|
+
a: String
|
100
|
+
}
|
101
|
+
"
|
102
|
+
|
103
|
+
document = subject.parse(schema)
|
104
|
+
|
105
|
+
assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "supports the old way of parsing multiple interfaces for backwards compatibility" do
|
109
|
+
schema = "
|
110
|
+
type A implements B, C {
|
111
|
+
a: String
|
112
|
+
}
|
113
|
+
"
|
114
|
+
|
115
|
+
document = subject.parse(schema)
|
116
|
+
|
117
|
+
assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
47
121
|
describe ".parse_file" do
|
48
122
|
it "assigns filename to all nodes" do
|
49
123
|
example_filename = "spec/support/parser/filename_example.graphql"
|
@@ -115,7 +115,7 @@ describe GraphQL::Language::Printer do
|
|
115
115
|
# Union description
|
116
116
|
union AnnotatedUnion @onUnion = A | B
|
117
117
|
|
118
|
-
type Foo implements Bar {
|
118
|
+
type Foo implements Bar & AnnotatedInterface {
|
119
119
|
one: Type
|
120
120
|
two(argument: InputType!): Type
|
121
121
|
three(argument: InputType, other: String): Int
|
@@ -60,6 +60,27 @@ describe GraphQL::ObjectType do
|
|
60
60
|
|
61
61
|
assert_raises(ArgumentError) { type.name }
|
62
62
|
end
|
63
|
+
|
64
|
+
it "doesnt convolute field names that differ with underscore" do
|
65
|
+
interface = Class.new(GraphQL::Schema::Interface) do
|
66
|
+
graphql_name 'TestInterface'
|
67
|
+
description 'Requires an id'
|
68
|
+
|
69
|
+
field :id, GraphQL::ID_TYPE, null: false
|
70
|
+
end
|
71
|
+
|
72
|
+
object = Class.new(GraphQL::Schema::Object) do
|
73
|
+
graphql_name 'TestObject'
|
74
|
+
implements interface
|
75
|
+
global_id_field :id
|
76
|
+
|
77
|
+
# When the validation for `id` is run for `_id`, it will fail because
|
78
|
+
# GraphQL::STRING_TYPE cannot be transformed into a GraphQL::ID_TYPE
|
79
|
+
field :_id, String, description: 'database id', null: true
|
80
|
+
end
|
81
|
+
|
82
|
+
assert_equal nil, GraphQL::Schema::Validation.validate(object.to_graphql)
|
83
|
+
end
|
63
84
|
end
|
64
85
|
|
65
86
|
it "accepts fields definition" do
|
@@ -70,7 +70,11 @@ describe GraphQL::Relay::RangeAdd do
|
|
70
70
|
field :add_item, add_item.field
|
71
71
|
end
|
72
72
|
|
73
|
-
GraphQL::Schema
|
73
|
+
Class.new(GraphQL::Schema) do
|
74
|
+
self.query(query)
|
75
|
+
self.mutation(mutation)
|
76
|
+
self.cursor_encoder(PassThroughEncoder)
|
77
|
+
end
|
74
78
|
}
|
75
79
|
|
76
80
|
|
@@ -117,6 +117,13 @@ describe GraphQL::Relay::RelationConnection do
|
|
117
117
|
assert_equal true, get_page_info(result)["hasNextPage"]
|
118
118
|
assert_equal true, get_page_info(result)["hasPreviousPage"]
|
119
119
|
|
120
|
+
last_cursor = get_last_cursor(result)
|
121
|
+
result = with_bidirectional_pagination {
|
122
|
+
star_wars_query(query_string, "last" => 1, "before" => last_cursor)
|
123
|
+
}
|
124
|
+
assert_equal true, get_page_info(result)["hasNextPage"]
|
125
|
+
assert_equal false, get_page_info(result)["hasPreviousPage"]
|
126
|
+
|
120
127
|
result = star_wars_query(query_string, "first" => 100)
|
121
128
|
last_cursor = get_last_cursor(result)
|
122
129
|
|
@@ -129,7 +136,6 @@ describe GraphQL::Relay::RelationConnection do
|
|
129
136
|
}
|
130
137
|
assert_equal true, get_page_info(result)["hasNextPage"]
|
131
138
|
assert_equal true, get_page_info(result)["hasPreviousPage"]
|
132
|
-
|
133
139
|
end
|
134
140
|
|
135
141
|
it 'slices the result' do
|