graphql 1.8.4 → 1.8.5
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/templates/graphql_controller.erb +10 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +398 -0
- data/lib/graphql/execution/execute.rb +24 -18
- data/lib/graphql/execution/lazy.rb +14 -1
- data/lib/graphql/introspection/type_type.rb +1 -1
- data/lib/graphql/language/lexer.rb +68 -42
- data/lib/graphql/language/lexer.rl +2 -0
- data/lib/graphql/language/nodes.rb +98 -0
- data/lib/graphql/language/parser.rb +1050 -770
- data/lib/graphql/language/parser.y +50 -2
- data/lib/graphql/object_type.rb +4 -0
- data/lib/graphql/query.rb +3 -1
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/schema.rb +10 -2
- data/lib/graphql/schema/input_object.rb +1 -1
- data/lib/graphql/schema/interface.rb +6 -0
- data/lib/graphql/schema/member/has_arguments.rb +1 -0
- data/lib/graphql/schema/member/instrumentation.rb +21 -16
- data/lib/graphql/schema/mutation.rb +1 -1
- data/lib/graphql/schema/object.rb +7 -2
- data/lib/graphql/schema/possible_types.rb +1 -1
- data/lib/graphql/schema/resolver.rb +210 -1
- data/lib/graphql/schema/validation.rb +1 -1
- data/lib/graphql/static_validation/message.rb +5 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +9 -0
- data/lib/graphql/type_kinds.rb +9 -6
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/types/iso_8601_date_time.rb +1 -5
- data/lib/graphql/unauthorized_error.rb +7 -2
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/install_generator_spec.rb +12 -2
- data/spec/graphql/authorization_spec.rb +80 -1
- data/spec/graphql/directive_spec.rb +42 -0
- data/spec/graphql/execution_error_spec.rb +1 -1
- data/spec/graphql/query_spec.rb +14 -0
- data/spec/graphql/schema/interface_spec.rb +14 -0
- data/spec/graphql/schema/object_spec.rb +41 -1
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +47 -0
- data/spec/graphql/schema/resolver_spec.rb +182 -8
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -5
- data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +34 -0
- data/spec/graphql/static_validation/validator_spec.rb +15 -0
- data/spec/support/jazz.rb +36 -1
- metadata +2 -2
@@ -51,4 +51,51 @@ describe GraphQL::Schema::RelayClassicMutation do
|
|
51
51
|
assert_equal "Sitar", res["data"]["addSitar"]["instrument"]["name"]
|
52
52
|
end
|
53
53
|
end
|
54
|
+
|
55
|
+
describe "loading application objects" do
|
56
|
+
let(:query_str) {
|
57
|
+
<<-GRAPHQL
|
58
|
+
mutation($id: ID!, $newName: String!) {
|
59
|
+
renameEnsemble(input: {ensembleId: $id, newName: $newName}) {
|
60
|
+
ensemble {
|
61
|
+
name
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
GRAPHQL
|
66
|
+
}
|
67
|
+
|
68
|
+
it "loads arguments as objects of the given type" do
|
69
|
+
res = Jazz::Schema.execute(query_str, variables: { id: "Ensemble/Robert Glasper Experiment", newName: "August Greene"})
|
70
|
+
assert_equal "August Greene", res["data"]["renameEnsemble"]["ensemble"]["name"]
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns an error instead when the ID resolves to nil" do
|
74
|
+
res = Jazz::Schema.execute(query_str, variables: {
|
75
|
+
id: "Ensemble/Nonexistant Name",
|
76
|
+
newName: "August Greene"
|
77
|
+
})
|
78
|
+
assert_nil res["data"].fetch("renameEnsemble")
|
79
|
+
assert_equal ['No object found for `ensembleId: "Ensemble/Nonexistant Name"`'], res["errors"].map { |e| e["message"] }
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns an error instead when the ID resolves to an object of the wrong type" do
|
83
|
+
res = Jazz::Schema.execute(query_str, variables: {
|
84
|
+
id: "Instrument/Organ",
|
85
|
+
newName: "August Greene"
|
86
|
+
})
|
87
|
+
assert_nil res["data"].fetch("renameEnsemble")
|
88
|
+
assert_equal ["No object found for `ensembleId: \"Instrument/Organ\"`"], res["errors"].map { |e| e["message"] }
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises an authorization error when the type's auth fails" do
|
92
|
+
res = Jazz::Schema.execute(query_str, variables: {
|
93
|
+
id: "Ensemble/Spinal Tap",
|
94
|
+
newName: "August Greene"
|
95
|
+
})
|
96
|
+
assert_nil res["data"].fetch("renameEnsemble")
|
97
|
+
# Failed silently
|
98
|
+
refute res.key?("errors")
|
99
|
+
end
|
100
|
+
end
|
54
101
|
end
|
@@ -3,6 +3,16 @@ require "spec_helper"
|
|
3
3
|
|
4
4
|
describe GraphQL::Schema::Resolver do
|
5
5
|
module ResolverTest
|
6
|
+
class LazyBlock
|
7
|
+
def initialize
|
8
|
+
@get_value = Proc.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def value
|
12
|
+
@get_value.call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
class BaseResolver < GraphQL::Schema::Resolver
|
7
17
|
end
|
8
18
|
|
@@ -64,6 +74,90 @@ describe GraphQL::Schema::Resolver do
|
|
64
74
|
class Resolver8 < Resolver7
|
65
75
|
end
|
66
76
|
|
77
|
+
class PrepResolver1 < BaseResolver
|
78
|
+
argument :int, Integer, required: true
|
79
|
+
|
80
|
+
def load_int(i)
|
81
|
+
i * 10
|
82
|
+
end
|
83
|
+
|
84
|
+
type Integer, null: false
|
85
|
+
|
86
|
+
def resolve(int:)
|
87
|
+
int
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def check_for_magic_number(int)
|
93
|
+
if int == 13
|
94
|
+
raise GraphQL::ExecutionError, "13 is unlucky!"
|
95
|
+
elsif int > 99
|
96
|
+
raise GraphQL::UnauthorizedError, "Top secret big number: #{int}"
|
97
|
+
else
|
98
|
+
int
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class PrepResolver2 < PrepResolver1
|
104
|
+
def load_int(i)
|
105
|
+
LazyBlock.new {
|
106
|
+
super - 35
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class PrepResolver3 < PrepResolver1
|
112
|
+
type Integer, null: true
|
113
|
+
|
114
|
+
def load_int(i)
|
115
|
+
check_for_magic_number(i)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class PrepResolver4 < PrepResolver3
|
120
|
+
def load_int(i)
|
121
|
+
LazyBlock.new {
|
122
|
+
super
|
123
|
+
}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class PrepResolver5 < PrepResolver1
|
128
|
+
type Integer, null: true
|
129
|
+
|
130
|
+
def before_prepare(int:)
|
131
|
+
check_for_magic_number(int)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class PrepResolver6 < PrepResolver5
|
136
|
+
def before_prepare(**args)
|
137
|
+
LazyBlock.new {
|
138
|
+
super
|
139
|
+
}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class PrepResolver7 < PrepResolver1
|
144
|
+
type Integer, null: true
|
145
|
+
|
146
|
+
def load_int(int)
|
147
|
+
int
|
148
|
+
end
|
149
|
+
|
150
|
+
def validate_int(int)
|
151
|
+
check_for_magic_number(int)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class PrepResolver8 < PrepResolver7
|
156
|
+
def validate_int(int)
|
157
|
+
LazyBlock.new { super }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
67
161
|
class Query < GraphQL::Schema::Object
|
68
162
|
class CustomField < GraphQL::Schema::Field
|
69
163
|
def resolve_field(*args)
|
@@ -86,47 +180,61 @@ describe GraphQL::Schema::Resolver do
|
|
86
180
|
field :resolver_6, resolver: Resolver6
|
87
181
|
field :resolver_7, resolver: Resolver7
|
88
182
|
field :resolver_8, resolver: Resolver8
|
183
|
+
|
184
|
+
field :prep_resolver_1, resolver: PrepResolver1
|
185
|
+
field :prep_resolver_2, resolver: PrepResolver2
|
186
|
+
field :prep_resolver_3, resolver: PrepResolver3
|
187
|
+
field :prep_resolver_4, resolver: PrepResolver4
|
188
|
+
field :prep_resolver_5, resolver: PrepResolver5
|
189
|
+
field :prep_resolver_6, resolver: PrepResolver6
|
190
|
+
field :prep_resolver_7, resolver: PrepResolver7
|
191
|
+
field :prep_resolver_8, resolver: PrepResolver8
|
89
192
|
end
|
90
193
|
|
91
194
|
class Schema < GraphQL::Schema
|
92
195
|
query(Query)
|
196
|
+
lazy_resolve LazyBlock, :value
|
93
197
|
end
|
94
198
|
end
|
95
199
|
|
200
|
+
def exec_query(*args)
|
201
|
+
ResolverTest::Schema.execute(*args)
|
202
|
+
end
|
203
|
+
|
96
204
|
it "gets initialized for each resolution" do
|
97
205
|
# State isn't shared between calls:
|
98
|
-
res =
|
206
|
+
res = exec_query " { r1: resolver1(value: 1) r2: resolver1 }"
|
99
207
|
assert_equal [100, 1], res["data"]["r1"]
|
100
208
|
assert_equal [100, nil], res["data"]["r2"]
|
101
209
|
end
|
102
210
|
|
103
211
|
it "inherits type and arguments" do
|
104
|
-
res =
|
212
|
+
res = exec_query " { r1: resolver2(value: 1, extraValue: 2) r2: resolver2(extraValue: 3) }"
|
105
213
|
assert_equal [100, 1, 2], res["data"]["r1"]
|
106
214
|
assert_equal [100, nil, 3], res["data"]["r2"]
|
107
215
|
end
|
108
216
|
|
109
217
|
it "uses the object's field_class" do
|
110
|
-
res =
|
218
|
+
res = exec_query " { r1: resolver3(value: 1) r2: resolver3 }"
|
111
219
|
assert_equal [100, 1, -1], res["data"]["r1"]
|
112
220
|
assert_equal [100, nil, -1], res["data"]["r2"]
|
113
221
|
end
|
114
222
|
|
115
223
|
describe "resolve method" do
|
116
224
|
it "has access to the application object" do
|
117
|
-
res =
|
225
|
+
res = exec_query " { resolver4 } ", root_value: OpenStruct.new(value: 4)
|
118
226
|
assert_equal 13, res["data"]["resolver4"]
|
119
227
|
end
|
120
228
|
|
121
229
|
it "gets extras" do
|
122
|
-
res =
|
230
|
+
res = exec_query " { resolver4 } ", root_value: OpenStruct.new(value: 0)
|
123
231
|
assert_equal 9, res["data"]["resolver4"]
|
124
232
|
end
|
125
233
|
end
|
126
234
|
|
127
235
|
describe "extras" do
|
128
236
|
it "is inherited" do
|
129
|
-
res =
|
237
|
+
res = exec_query " { resolver4 resolver5 } ", root_value: OpenStruct.new(value: 0)
|
130
238
|
assert_equal 9, res["data"]["resolver4"]
|
131
239
|
assert_equal 9, res["data"]["resolver5"]
|
132
240
|
end
|
@@ -134,12 +242,12 @@ describe GraphQL::Schema::Resolver do
|
|
134
242
|
|
135
243
|
describe "complexity" do
|
136
244
|
it "has default values" do
|
137
|
-
res =
|
245
|
+
res = exec_query " { resolver6 } ", root_value: OpenStruct.new(value: 0)
|
138
246
|
assert_equal 1, res["data"]["resolver6"]
|
139
247
|
end
|
140
248
|
|
141
249
|
it "is inherited" do
|
142
|
-
res =
|
250
|
+
res = exec_query " { resolver7 resolver8 } ", root_value: OpenStruct.new(value: 0)
|
143
251
|
assert_equal 2, res["data"]["resolver7"]
|
144
252
|
assert_equal 2, res["data"]["resolver8"]
|
145
253
|
end
|
@@ -159,4 +267,70 @@ describe GraphQL::Schema::Resolver do
|
|
159
267
|
assert ResolverTest::Schema.find("Query.resolver3Again")
|
160
268
|
end
|
161
269
|
end
|
270
|
+
|
271
|
+
describe "preparing inputs" do
|
272
|
+
# Add assertions for a given field, assuming the behavior of `check_for_magic_number`
|
273
|
+
def add_error_assertions(field_name, description)
|
274
|
+
res = exec_query("{ int: #{field_name}(int: 13) }")
|
275
|
+
assert_nil res["data"].fetch("int"), "#{description}: no result for execution error"
|
276
|
+
assert_equal ["13 is unlucky!"], res["errors"].map { |e| e["message"] }, "#{description}: top-level error is added"
|
277
|
+
|
278
|
+
res = exec_query("{ int: #{field_name}(int: 200) }")
|
279
|
+
assert_nil res["data"].fetch("int"), "#{description}: No result for authorization error"
|
280
|
+
refute res.key?("errors"), "#{description}: silent auth failure (no top-level error)"
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "before_prepare" do
|
284
|
+
it "can raise errors" do
|
285
|
+
res = exec_query("{ int: prepResolver5(int: 5) }")
|
286
|
+
assert_equal 50, res["data"]["int"]
|
287
|
+
add_error_assertions("prepResolver5", "before_prepare")
|
288
|
+
end
|
289
|
+
|
290
|
+
it "can raise errors in lazy sync" do
|
291
|
+
res = exec_query("{ int: prepResolver6(int: 5) }")
|
292
|
+
assert_equal 50, res["data"]["int"]
|
293
|
+
add_error_assertions("prepResolver6", "lazy before_prepare")
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "loading arguments" do
|
298
|
+
it "calls load methods and injects the return value" do
|
299
|
+
res = exec_query("{ prepResolver1(int: 5) }")
|
300
|
+
assert_equal 50, res["data"]["prepResolver1"], "The load multiplier was called"
|
301
|
+
end
|
302
|
+
|
303
|
+
it "supports lazy values" do
|
304
|
+
res = exec_query("{ prepResolver2(int: 5) }")
|
305
|
+
assert_equal 15, res["data"]["prepResolver2"], "The load multiplier was called"
|
306
|
+
end
|
307
|
+
|
308
|
+
it "supports raising GraphQL::UnauthorizedError and GraphQL::ExecutionError" do
|
309
|
+
res = exec_query("{ prepResolver3(int: 5) }")
|
310
|
+
assert_equal 5, res["data"]["prepResolver3"]
|
311
|
+
add_error_assertions("prepResolver3", "load_ hook")
|
312
|
+
end
|
313
|
+
|
314
|
+
it "supports raising errors from promises" do
|
315
|
+
res = exec_query("{ prepResolver4(int: 5) }")
|
316
|
+
assert_equal 5, res["data"]["prepResolver4"]
|
317
|
+
add_error_assertions("prepResolver4", "lazy load_ hook")
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "validating arguments" do
|
322
|
+
test_cases = {
|
323
|
+
"eager" => "prepResolver7",
|
324
|
+
"lazy" => "prepResolver8",
|
325
|
+
}
|
326
|
+
|
327
|
+
test_cases.each do |mode, field_name|
|
328
|
+
it "supports raising #{mode} errors" do
|
329
|
+
res = exec_query("{ validatedInt: #{field_name}(int: 5) }")
|
330
|
+
assert_equal 5, res["data"]["validatedInt"]
|
331
|
+
add_error_assertions(field_name, "#{mode} validation")
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
162
336
|
end
|
@@ -222,7 +222,8 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
222
222
|
|
223
223
|
describe "custom error messages" do
|
224
224
|
let(:schema) {
|
225
|
-
|
225
|
+
|
226
|
+
CoerceTestTimeType = GraphQL::ScalarType.define do
|
226
227
|
name "Time"
|
227
228
|
description "Time since epoch in seconds"
|
228
229
|
|
@@ -237,19 +238,19 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
237
238
|
coerce_result ->(value, ctx) { value.to_f }
|
238
239
|
end
|
239
240
|
|
240
|
-
|
241
|
+
CoerceTestQueryType = GraphQL::ObjectType.define do
|
241
242
|
name "Query"
|
242
243
|
description "The query root of this schema"
|
243
244
|
|
244
245
|
field :time do
|
245
|
-
type
|
246
|
-
argument :value, !
|
246
|
+
type CoerceTestTimeType
|
247
|
+
argument :value, !CoerceTestTimeType
|
247
248
|
resolve ->(obj, args, ctx) { args[:value] }
|
248
249
|
end
|
249
250
|
end
|
250
251
|
|
251
252
|
GraphQL::Schema.define do
|
252
|
-
query
|
253
|
+
query CoerceTestQueryType
|
253
254
|
end
|
254
255
|
}
|
255
256
|
|
@@ -25,4 +25,38 @@ describe GraphQL::StaticValidation::NoDefinitionsArePresent do
|
|
25
25
|
assert_equal [{"line"=>5, "column"=>7}, {"line"=>9, "column"=>7}], err["locations"]
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
29
|
+
describe "when schema extensions are present in the query" do
|
30
|
+
let(:query_string) {
|
31
|
+
<<-GRAPHQL
|
32
|
+
{
|
33
|
+
cheese(id: 1) { flavor }
|
34
|
+
}
|
35
|
+
|
36
|
+
extend schema {
|
37
|
+
subscription: Query
|
38
|
+
}
|
39
|
+
|
40
|
+
extend scalar TracingScalar @deprecated
|
41
|
+
extend type Dairy @deprecated
|
42
|
+
extend interface Edible @deprecated
|
43
|
+
extend union Beverage @deprecated
|
44
|
+
extend enum DairyAnimal @deprecated
|
45
|
+
extend input ResourceOrderType @deprecated
|
46
|
+
GRAPHQL
|
47
|
+
}
|
48
|
+
|
49
|
+
it "adds an error" do
|
50
|
+
assert_equal 1, errors.length
|
51
|
+
err = errors[0]
|
52
|
+
assert_equal "Query cannot contain schema definitions", err["message"]
|
53
|
+
assert_equal [{"line"=>5, "column"=>7},
|
54
|
+
{"line"=>9, "column"=>7},
|
55
|
+
{"line"=>10, "column"=>7},
|
56
|
+
{"line"=>11, "column"=>7},
|
57
|
+
{"line"=>12, "column"=>7},
|
58
|
+
{"line"=>13, "column"=>7},
|
59
|
+
{"line"=>14, "column"=>7}], err["locations"]
|
60
|
+
end
|
61
|
+
end
|
28
62
|
end
|
@@ -25,6 +25,21 @@ describe GraphQL::StaticValidation::Validator do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
describe "error format" do
|
29
|
+
let(:query_string) { "{ cheese(id: $undefinedVar) { source } }" }
|
30
|
+
let(:document) { GraphQL.parse_with_racc(query_string, filename: "not_a_real.graphql") }
|
31
|
+
let(:query) { GraphQL::Query.new(Dummy::Schema, nil, document: document) }
|
32
|
+
|
33
|
+
it "includes message, locations, and fields keys" do
|
34
|
+
expected_errors = [{
|
35
|
+
"message" => "Variable $undefinedVar is used by but not declared",
|
36
|
+
"locations" => [{"line" => 1, "column" => 14, "filename" => "not_a_real.graphql"}],
|
37
|
+
"fields" => ["query", "cheese", "id"]
|
38
|
+
}]
|
39
|
+
assert_equal expected_errors, errors
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
28
43
|
describe "validation order" do
|
29
44
|
let(:document) { GraphQL.parse(query_string)}
|
30
45
|
|
data/spec/support/jazz.rb
CHANGED
@@ -32,6 +32,7 @@ module Jazz
|
|
32
32
|
"Ensemble" => [
|
33
33
|
Models::Ensemble.new("Bela Fleck and the Flecktones"),
|
34
34
|
Models::Ensemble.new("Robert Glasper Experiment"),
|
35
|
+
Models::Ensemble.new("Spinal Tap"),
|
35
36
|
],
|
36
37
|
"Musician" => [
|
37
38
|
Models::Musician.new("Herbie Hancock", Models::Key.from_notation("B♭")),
|
@@ -190,6 +191,12 @@ module Jazz
|
|
190
191
|
def overridden_name
|
191
192
|
@object.name.sub("Robert Glasper", "ROBERT GLASPER")
|
192
193
|
end
|
194
|
+
|
195
|
+
def self.authorized?(object, context)
|
196
|
+
# Spinal Tap is top-secret, don't show it to anyone.
|
197
|
+
obj_name = object.is_a?(Hash) ? object[:name] : object.name
|
198
|
+
obj_name != "Spinal Tap"
|
199
|
+
end
|
193
200
|
end
|
194
201
|
|
195
202
|
class Family < BaseEnum
|
@@ -344,7 +351,8 @@ module Jazz
|
|
344
351
|
end
|
345
352
|
|
346
353
|
def ensembles
|
347
|
-
|
354
|
+
# Filter out the unauthorized one to avoid an error later
|
355
|
+
Models.data["Ensemble"].select { |e| e.name != "Spinal Tap" }
|
348
356
|
end
|
349
357
|
|
350
358
|
def find(id:)
|
@@ -419,6 +427,12 @@ module Jazz
|
|
419
427
|
def hash_by_sym
|
420
428
|
{ falsey: false }
|
421
429
|
end
|
430
|
+
|
431
|
+
field :named_entities, [NamedEntity, null: true], null: false
|
432
|
+
|
433
|
+
def named_entities
|
434
|
+
[Models.data["Ensemble"].first, nil]
|
435
|
+
end
|
422
436
|
end
|
423
437
|
|
424
438
|
class EnsembleInput < GraphQL::Schema::InputObject
|
@@ -458,6 +472,22 @@ module Jazz
|
|
458
472
|
end
|
459
473
|
end
|
460
474
|
|
475
|
+
class RenameEnsemble < GraphQL::Schema::RelayClassicMutation
|
476
|
+
argument :ensemble_id, ID, required: true, loads: Ensemble
|
477
|
+
argument :new_name, String, required: true
|
478
|
+
|
479
|
+
field :ensemble, Ensemble, null: false
|
480
|
+
|
481
|
+
def resolve(ensemble:, new_name:)
|
482
|
+
# doesn't actually update the "database"
|
483
|
+
dup_ensemble = ensemble.dup
|
484
|
+
dup_ensemble.name = new_name
|
485
|
+
{
|
486
|
+
ensemble: dup_ensemble
|
487
|
+
}
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
461
491
|
class Mutation < BaseObject
|
462
492
|
field :add_ensemble, Ensemble, null: false do
|
463
493
|
argument :input, EnsembleInput, required: true
|
@@ -465,6 +495,7 @@ module Jazz
|
|
465
495
|
|
466
496
|
field :add_instrument, mutation: AddInstrument
|
467
497
|
field :add_sitar, mutation: AddSitar
|
498
|
+
field :rename_ensemble, mutation: RenameEnsemble
|
468
499
|
|
469
500
|
def add_ensemble(input:)
|
470
501
|
ens = Models::Ensemble.new(input.name)
|
@@ -553,5 +584,9 @@ module Jazz
|
|
553
584
|
class_name = obj.class.name.split("::").last
|
554
585
|
ctx.schema.types[class_name] || raise("No type for #{obj.inspect}")
|
555
586
|
end
|
587
|
+
|
588
|
+
def self.object_from_id(id, ctx)
|
589
|
+
GloballyIdentifiableType.find(id)
|
590
|
+
end
|
556
591
|
end
|
557
592
|
end
|