graphql 1.8.1 → 1.8.2
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/generators/graphql/core.rb +1 -1
- data/lib/generators/graphql/enum_generator.rb +3 -3
- data/lib/generators/graphql/install_generator.rb +12 -1
- data/lib/generators/graphql/mutation_generator.rb +4 -4
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +3 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +1 -2
- data/lib/generators/graphql/templates/interface.erb +2 -3
- data/lib/generators/graphql/templates/mutation.erb +4 -5
- data/lib/generators/graphql/templates/mutation_type.erb +6 -9
- data/lib/generators/graphql/templates/object.erb +2 -3
- data/lib/generators/graphql/templates/query_type.erb +6 -8
- data/lib/generators/graphql/templates/schema.erb +7 -7
- data/lib/generators/graphql/templates/union.erb +1 -2
- data/lib/generators/graphql/type_generator.rb +33 -18
- data/lib/generators/graphql/union_generator.rb +1 -1
- data/lib/graphql/execution/execute.rb +3 -0
- data/lib/graphql/schema.rb +1 -0
- data/lib/graphql/schema/input_object.rb +22 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -2
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
- data/lib/graphql/subscriptions/instrumentation.rb +3 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/enum_generator_spec.rb +1 -2
- data/spec/generators/graphql/install_generator_spec.rb +23 -24
- data/spec/generators/graphql/interface_generator_spec.rb +5 -6
- data/spec/generators/graphql/mutation_generator_spec.rb +10 -27
- data/spec/generators/graphql/object_generator_spec.rb +7 -10
- data/spec/generators/graphql/union_generator_spec.rb +3 -6
- data/spec/graphql/execution/execute_spec.rb +97 -0
- data/spec/graphql/schema/input_object_spec.rb +32 -0
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +16 -0
- data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +44 -0
- data/spec/graphql/subscriptions_spec.rb +21 -1
- data/spec/support/jazz.rb +15 -2
- metadata +10 -6
- data/lib/generators/graphql/function_generator.rb +0 -18
- data/lib/generators/graphql/templates/function.erb +0 -17
- data/spec/generators/graphql/function_generator_spec.rb +0 -59
@@ -44,7 +44,10 @@ module GraphQL
|
|
44
44
|
|
45
45
|
# Wrap the proc with subscription registration logic
|
46
46
|
def call(obj, args, ctx)
|
47
|
+
@inner_proc.call(obj, args, ctx) if @inner_proc && !@inner_proc.is_a?(GraphQL::Field::Resolve::BuiltInResolve)
|
48
|
+
|
47
49
|
events = ctx.namespace(:subscriptions)[:events]
|
50
|
+
|
48
51
|
if events
|
49
52
|
# This is the first execution, so gather an Event
|
50
53
|
# for the backend to register:
|
data/lib/graphql/version.rb
CHANGED
@@ -7,8 +7,7 @@ class GraphQLGeneratorsEnumGeneratorTest < BaseGeneratorTest
|
|
7
7
|
|
8
8
|
test "it generate enums with values" do
|
9
9
|
expected_content = <<-RUBY
|
10
|
-
Types::FamilyType
|
11
|
-
name "Family"
|
10
|
+
class Types::FamilyType < Types::BaseEnum
|
12
11
|
value "NIGHTSHADE"
|
13
12
|
value "BRASSICA", value: Family::COLE
|
14
13
|
value "UMBELLIFER", value: :umbellifer
|
@@ -19,6 +19,9 @@ class GraphQLGeneratorsInstallGeneratorTest < Rails::Generators::TestCase
|
|
19
19
|
|
20
20
|
assert_file "app/graphql/types/.keep"
|
21
21
|
assert_file "app/graphql/mutations/.keep"
|
22
|
+
["base_object", "base_input_object", "base_enum", "base_union", "base_interface"].each do |base_type|
|
23
|
+
assert_file "app/graphql/types/#{base_type}.rb"
|
24
|
+
end
|
22
25
|
expected_query_route = %|post "/graphql", to: "graphql#execute"|
|
23
26
|
expected_graphiql_route = %|
|
24
27
|
if Rails.env.development?
|
@@ -36,7 +39,7 @@ class GraphQLGeneratorsInstallGeneratorTest < Rails::Generators::TestCase
|
|
36
39
|
end
|
37
40
|
|
38
41
|
expected_schema = <<-RUBY
|
39
|
-
DummySchema
|
42
|
+
class DummySchema < GraphQL::Schema
|
40
43
|
mutation(Types::MutationType)
|
41
44
|
query(Types::QueryType)
|
42
45
|
end
|
@@ -45,17 +48,15 @@ RUBY
|
|
45
48
|
|
46
49
|
|
47
50
|
expected_query_type = <<-RUBY
|
48
|
-
Types::QueryType
|
49
|
-
name "Query"
|
51
|
+
class Types::QueryType < Types::BaseObject
|
50
52
|
# Add root-level fields here.
|
51
53
|
# They will be entry points for queries on your schema.
|
52
54
|
|
53
55
|
# TODO: remove me
|
54
|
-
field :
|
55
|
-
description \"An example field added by the generator\"
|
56
|
-
|
57
|
-
|
58
|
-
}
|
56
|
+
field :test_field, String, null: false,
|
57
|
+
description: \"An example field added by the generator\"
|
58
|
+
def test_field
|
59
|
+
\"Hello World!\"
|
59
60
|
end
|
60
61
|
end
|
61
62
|
RUBY
|
@@ -79,20 +80,18 @@ RUBY
|
|
79
80
|
end
|
80
81
|
|
81
82
|
expected_query_type = <<-RUBY
|
82
|
-
Types::QueryType
|
83
|
-
name "Query"
|
83
|
+
class Types::QueryType < Types::BaseObject
|
84
84
|
# Add root-level fields here.
|
85
85
|
# They will be entry points for queries on your schema.
|
86
86
|
|
87
87
|
# TODO: remove me
|
88
|
-
field :
|
89
|
-
description \"An example field added by the generator\"
|
90
|
-
|
91
|
-
|
92
|
-
}
|
88
|
+
field :test_field, String, null: false,
|
89
|
+
description: \"An example field added by the generator\"
|
90
|
+
def test_field
|
91
|
+
\"Hello World!\"
|
93
92
|
end
|
94
93
|
|
95
|
-
field :node, GraphQL::Relay::Node.field
|
94
|
+
field :node, field: GraphQL::Relay::Node.field
|
96
95
|
end
|
97
96
|
RUBY
|
98
97
|
|
@@ -126,7 +125,7 @@ RUBY
|
|
126
125
|
refute_includes contents, "GraphiQL::Rails"
|
127
126
|
end
|
128
127
|
|
129
|
-
assert_file "app/graphql/custom_schema.rb", /CustomSchema
|
128
|
+
assert_file "app/graphql/custom_schema.rb", /class CustomSchema < GraphQL::Schema/
|
130
129
|
assert_file "app/controllers/graphql_controller.rb", /CustomSchema\.execute/
|
131
130
|
end
|
132
131
|
|
@@ -167,36 +166,36 @@ end
|
|
167
166
|
RUBY
|
168
167
|
|
169
168
|
EXPECTED_RELAY_BATCH_SCHEMA = <<-RUBY
|
170
|
-
DummySchema
|
169
|
+
class DummySchema < GraphQL::Schema
|
171
170
|
|
172
171
|
mutation(Types::MutationType)
|
173
172
|
query(Types::QueryType)
|
174
173
|
# Relay Object Identification:
|
175
174
|
|
176
175
|
# Return a string UUID for `object`
|
177
|
-
id_from_object
|
176
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
178
177
|
# Here's a simple implementation which:
|
179
178
|
# - joins the type name & object.id
|
180
179
|
# - encodes it with base64:
|
181
180
|
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
182
|
-
|
181
|
+
end
|
183
182
|
|
184
183
|
# Given a string UUID, find the object
|
185
|
-
object_from_id
|
184
|
+
def self.object_from_id(id, query_ctx)
|
186
185
|
# For example, to decode the UUIDs generated above:
|
187
186
|
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
188
187
|
#
|
189
188
|
# Then, based on `type_name` and `id`
|
190
189
|
# find an object in your application
|
191
190
|
# ...
|
192
|
-
|
191
|
+
end
|
193
192
|
|
194
193
|
# Object Resolution
|
195
|
-
resolve_type
|
194
|
+
def self.resolve_type(type, obj, ctx)
|
196
195
|
# TODO: Implement this function
|
197
196
|
# to return the correct type for `obj`
|
198
197
|
raise(NotImplementedError)
|
199
|
-
|
198
|
+
end
|
200
199
|
|
201
200
|
# GraphQL::Batch setup:
|
202
201
|
use GraphQL::Batch
|
@@ -10,16 +10,15 @@ class GraphQLGeneratorsInterfaceGeneratorTest < BaseGeneratorTest
|
|
10
10
|
# GraphQL-style:
|
11
11
|
["Bird", "wingspan:Int!", "foliage:[Color]"],
|
12
12
|
# Ruby-style:
|
13
|
-
["BirdType", "wingspan
|
13
|
+
["BirdType", "wingspan:Integer!", "foliage:[Types::ColorType]"],
|
14
14
|
# Mixed
|
15
|
-
["BirdType", "wingspan:!Int", "foliage:
|
15
|
+
["BirdType", "wingspan:!Int", "foliage:[Color]"],
|
16
16
|
]
|
17
17
|
|
18
18
|
expected_content = <<-RUBY
|
19
|
-
Types::BirdType
|
20
|
-
|
21
|
-
field :
|
22
|
-
field :foliage, types[Types::ColorType]
|
19
|
+
class Types::BirdType < Types::BaseInterface
|
20
|
+
field :wingspan, Integer, null: false
|
21
|
+
field :foliage, [Types::ColorType], null: true
|
23
22
|
end
|
24
23
|
RUBY
|
25
24
|
|
@@ -19,47 +19,30 @@ class GraphQLGeneratorsMutationGeneratorTest < BaseGeneratorTest
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
run_generator(["UpdateName"])
|
25
|
-
|
26
|
-
expected_content = <<-RUBY
|
27
|
-
Mutations::UpdateName = GraphQL::Relay::Mutation.define do
|
28
|
-
name "UpdateName"
|
22
|
+
UPDATE_NAME_MUTATION = <<-RUBY
|
23
|
+
class Mutations::UpdateName < GraphQL::Schema::RelayClassicMutation
|
29
24
|
# TODO: define return fields
|
30
25
|
# return_field :post, Types::PostType
|
31
26
|
|
32
27
|
# TODO: define arguments
|
33
28
|
# input_field :name, !types.String
|
34
29
|
|
35
|
-
resolve
|
36
|
-
# TODO: define resolve
|
37
|
-
|
30
|
+
def resolve(**inputs)
|
31
|
+
# TODO: define resolve method
|
32
|
+
end
|
38
33
|
end
|
39
34
|
RUBY
|
40
35
|
|
41
|
-
|
36
|
+
test "it generates an empty resolver by name" do
|
37
|
+
setup
|
38
|
+
run_generator(["UpdateName"])
|
39
|
+
assert_file "app/graphql/mutations/update_name.rb", UPDATE_NAME_MUTATION
|
42
40
|
end
|
43
41
|
|
44
42
|
test "it allows for user-specified directory" do
|
45
43
|
setup "app/mydirectory"
|
46
44
|
run_generator(["UpdateName", "--directory", "app/mydirectory"])
|
47
45
|
|
48
|
-
|
49
|
-
Mutations::UpdateName = GraphQL::Relay::Mutation.define do
|
50
|
-
name "UpdateName"
|
51
|
-
# TODO: define return fields
|
52
|
-
# return_field :post, Types::PostType
|
53
|
-
|
54
|
-
# TODO: define arguments
|
55
|
-
# input_field :name, !types.String
|
56
|
-
|
57
|
-
resolve ->(obj, args, ctx) {
|
58
|
-
# TODO: define resolve function
|
59
|
-
}
|
60
|
-
end
|
61
|
-
RUBY
|
62
|
-
|
63
|
-
assert_file "app/mydirectory/mutations/update_name.rb", expected_content
|
46
|
+
assert_file "app/mydirectory/mutations/update_name.rb", UPDATE_NAME_MUTATION
|
64
47
|
end
|
65
48
|
end
|
@@ -10,16 +10,15 @@ class GraphQLGeneratorsObjectGeneratorTest < BaseGeneratorTest
|
|
10
10
|
# GraphQL-style:
|
11
11
|
["Bird", "wingspan:Int!", "foliage:[Color]"],
|
12
12
|
# Ruby-style:
|
13
|
-
["BirdType", "wingspan:!
|
13
|
+
["BirdType", "wingspan:!Integer", "foliage:[Types::ColorType]"],
|
14
14
|
# Mixed
|
15
|
-
["BirdType", "wingspan:!Int", "foliage:
|
15
|
+
["BirdType", "wingspan:!Int", "foliage:[Color]"],
|
16
16
|
]
|
17
17
|
|
18
18
|
expected_content = <<-RUBY
|
19
|
-
Types::BirdType
|
20
|
-
|
21
|
-
field :
|
22
|
-
field :foliage, types[Types::ColorType]
|
19
|
+
class Types::BirdType < Types::BaseObject
|
20
|
+
field :wingspan, Integer, null: false
|
21
|
+
field :foliage, [Types::ColorType], null: true
|
23
22
|
end
|
24
23
|
RUBY
|
25
24
|
|
@@ -33,8 +32,7 @@ RUBY
|
|
33
32
|
test "it generates classifed file" do
|
34
33
|
run_generator(["page"])
|
35
34
|
assert_file "app/graphql/types/page_type.rb", <<-RUBY
|
36
|
-
Types::PageType
|
37
|
-
name "Page"
|
35
|
+
class Types::PageType < Types::BaseObject
|
38
36
|
end
|
39
37
|
RUBY
|
40
38
|
end
|
@@ -42,8 +40,7 @@ RUBY
|
|
42
40
|
test "it makes Relay nodes" do
|
43
41
|
run_generator(["Page", "--node"])
|
44
42
|
assert_file "app/graphql/types/page_type.rb", <<-RUBY
|
45
|
-
Types::PageType
|
46
|
-
name "Page"
|
43
|
+
class Types::PageType < Types::BaseObject
|
47
44
|
implements GraphQL::Relay::Node.interface
|
48
45
|
end
|
49
46
|
RUBY
|
@@ -14,8 +14,7 @@ class GraphQLGeneratorsUnionGeneratorTest < BaseGeneratorTest
|
|
14
14
|
]
|
15
15
|
|
16
16
|
expected_content = <<-RUBY
|
17
|
-
Types::WingedCreatureType
|
18
|
-
name "WingedCreature"
|
17
|
+
class Types::WingedCreatureType < Types::BaseUnion
|
19
18
|
possible_types [Types::InsectType, Types::BirdType]
|
20
19
|
end
|
21
20
|
RUBY
|
@@ -36,8 +35,7 @@ RUBY
|
|
36
35
|
]
|
37
36
|
|
38
37
|
expected_content = <<-RUBY
|
39
|
-
Types::WingedCreatureType
|
40
|
-
name "WingedCreature"
|
38
|
+
class Types::WingedCreatureType < Types::BaseUnion
|
41
39
|
end
|
42
40
|
RUBY
|
43
41
|
|
@@ -52,8 +50,7 @@ RUBY
|
|
52
50
|
command = ["WingedCreature", "--directory", "app/mydirectory"]
|
53
51
|
|
54
52
|
expected_content = <<-RUBY
|
55
|
-
Types::WingedCreatureType
|
56
|
-
name "WingedCreature"
|
53
|
+
class Types::WingedCreatureType < Types::BaseUnion
|
57
54
|
end
|
58
55
|
RUBY
|
59
56
|
|
@@ -77,6 +77,103 @@ describe GraphQL::Execution::Execute do
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
describe "when a member of a list of non-null type returns nil" do
|
81
|
+
let(:schema) {
|
82
|
+
node_type = GraphQL::ObjectType.define do
|
83
|
+
name "Node"
|
84
|
+
|
85
|
+
field :id, types.ID, "" do
|
86
|
+
resolve ->(obj, args, ctx) {
|
87
|
+
obj[:id]
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
query_type = GraphQL::ObjectType.define do
|
93
|
+
name "Query"
|
94
|
+
|
95
|
+
field :nonNullListWithNullStrings, types[!types.String], "" do
|
96
|
+
resolve ->(obj, args, ctx) {
|
97
|
+
[nil]
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
field :nonNullListWithNullStringsLazy, types[!types.String], "" do
|
102
|
+
resolve ->(obj, args, ctx) {
|
103
|
+
LazyHelpers::Wrapper.new([nil])
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
field :nonNullListWithNullTypes, types[!node_type], "" do
|
108
|
+
resolve ->(obj, args, ctx) {
|
109
|
+
[{ id: 1 }, nil, { id: 2 }]
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
field :listWithNullStrings, types[types.String], "" do
|
114
|
+
resolve ->(obj, args, ctx) {
|
115
|
+
[nil, "hello"]
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
field :listWithNullTypes, types[node_type], "" do
|
120
|
+
resolve ->(obj, args, ctx) {
|
121
|
+
[nil, { id: 1 }, nil]
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
field :nonNullList, !types[!types.String], "" do
|
126
|
+
resolve ->(obj, args, ctx) {
|
127
|
+
[nil]
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
GraphQL::Schema.define do
|
133
|
+
query query_type
|
134
|
+
lazy_resolve(LazyHelpers::Wrapper, :item)
|
135
|
+
end
|
136
|
+
}
|
137
|
+
|
138
|
+
it "propagates null for non-lazy resolvers" do
|
139
|
+
query = <<-GRAPHQL
|
140
|
+
{
|
141
|
+
nonNullListWithNullStrings
|
142
|
+
nonNullListWithNullTypes {
|
143
|
+
id
|
144
|
+
}
|
145
|
+
listWithNullStrings
|
146
|
+
listWithNullTypes {
|
147
|
+
id
|
148
|
+
}
|
149
|
+
}
|
150
|
+
GRAPHQL
|
151
|
+
|
152
|
+
result = schema.execute(query).to_h
|
153
|
+
|
154
|
+
assert_equal ["nonNullListWithNullStrings", "nonNullListWithNullTypes", "listWithNullStrings", "listWithNullTypes"], result["data"].keys
|
155
|
+
|
156
|
+
assert_equal nil, result["data"]["nonNullListWithNullStrings"]
|
157
|
+
assert_equal nil, result["data"]["nonNullListWithNullTypes"]
|
158
|
+
assert_equal [nil, "hello"], result["data"]["listWithNullStrings"]
|
159
|
+
assert_equal [nil, { "id" => "1" }, nil], result["data"]["listWithNullTypes"]
|
160
|
+
end
|
161
|
+
|
162
|
+
it "propagates null for lazy resolvers" do
|
163
|
+
result = schema.execute("{ nonNullListWithNullStringsLazy }").to_h
|
164
|
+
|
165
|
+
assert_equal ["nonNullListWithNullStringsLazy"], result["data"].keys
|
166
|
+
|
167
|
+
assert_equal nil, result["data"]["nonNullListWithNullStringsLazy"]
|
168
|
+
end
|
169
|
+
|
170
|
+
it "propagates null for non-null lists of non-null types" do
|
171
|
+
result = schema.execute("{ nonNullList }").to_h
|
172
|
+
|
173
|
+
assert_equal nil, result["data"]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
80
177
|
describe "when a list member raises an error" do
|
81
178
|
let(:schema) {
|
82
179
|
thing_type = GraphQL::ObjectType.define do
|
@@ -108,4 +108,36 @@ describe GraphQL::Schema::InputObject do
|
|
108
108
|
assert_equal expected_info, res["data"]["inspectInput"]
|
109
109
|
end
|
110
110
|
end
|
111
|
+
|
112
|
+
describe "#to_h" do
|
113
|
+
module InputObjectToHTest
|
114
|
+
class TestInput1 < GraphQL::Schema::InputObject
|
115
|
+
graphql_name "TestInput1"
|
116
|
+
argument :d, Int, required: true
|
117
|
+
argument :e, Int, required: true
|
118
|
+
end
|
119
|
+
|
120
|
+
class TestInput2 < GraphQL::Schema::InputObject
|
121
|
+
graphql_name "TestInput2"
|
122
|
+
argument :a, Int, required: true
|
123
|
+
argument :b, Int, required: true
|
124
|
+
argument :c, TestInput1, as: :inputObject, required: true
|
125
|
+
end
|
126
|
+
|
127
|
+
TestInput1.to_graphql
|
128
|
+
TestInput2.to_graphql
|
129
|
+
end
|
130
|
+
|
131
|
+
it "returns a symbolized, aliased, ruby keyword style hash" do
|
132
|
+
arg_values = {a: 1, b: 2, c: { d: 3, e: 4 }}
|
133
|
+
|
134
|
+
input_object = InputObjectToHTest::TestInput2.new(
|
135
|
+
arg_values,
|
136
|
+
context: nil,
|
137
|
+
defaults_used: Set.new
|
138
|
+
)
|
139
|
+
|
140
|
+
assert_equal({ a: 1, b: 2, input_object: { d: 3, e: 4 } }, input_object.to_h)
|
141
|
+
end
|
142
|
+
end
|
111
143
|
end
|
@@ -35,4 +35,20 @@ describe GraphQL::Schema::RelayClassicMutation do
|
|
35
35
|
assert mutation.null
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
describe "execution" do
|
40
|
+
it "works with no arguments" do
|
41
|
+
res = Jazz::Schema.execute <<-GRAPHQL
|
42
|
+
mutation {
|
43
|
+
addSitar(input: {}) {
|
44
|
+
instrument {
|
45
|
+
name
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
GRAPHQL
|
50
|
+
|
51
|
+
assert_equal "Sitar", res["data"]["addSitar"]["instrument"]["name"]
|
52
|
+
end
|
53
|
+
end
|
38
54
|
end
|