Skip to content

Commit a6ed30f

Browse files
author
Robert Mosolgo
authored
Merge pull request rmosolgo#1797 from cjoudrey/loads-array
Add support for array of IDs to `loads:` argument
2 parents 0e9f93b + 4c5ff78 commit a6ed30f

File tree

4 files changed

+153
-3
lines changed

4 files changed

+153
-3
lines changed

lib/graphql/schema/resolver.rb

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,12 +324,32 @@ def type_expr
324324
# @see {GraphQL::Schema::Argument#initialize} for the signature
325325
def argument(name, type, *rest, loads: nil, **kwargs, &block)
326326
if loads
327-
arg_keyword = kwargs[:as] ||= name.to_s.sub(/_id$/, "").to_sym
328-
own_arguments_loads_as_type[arg_keyword] = loads
327+
name_as_string = name.to_s
328+
329+
inferred_arg_name = case name_as_string
330+
when /_id$/
331+
name_as_string.sub(/_id$/, "").to_sym
332+
when /_ids$/
333+
name_as_string.sub(/_ids$/, "")
334+
.sub(/([^s])$/, "\\1s")
335+
.to_sym
336+
else
337+
name
338+
end
339+
340+
kwargs[:as] ||= inferred_arg_name
341+
own_arguments_loads_as_type[kwargs[:as]] = loads
329342
end
343+
330344
arg_defn = super(name, type, *rest, **kwargs, &block)
331345

332-
if loads
346+
if loads && arg_defn.type.list?
347+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
348+
def load_#{arg_defn.keyword}(values)
349+
GraphQL::Execution::Lazy.all(values.map { |value| load_application_object(:#{arg_defn.keyword}, value) })
350+
end
351+
RUBY
352+
elsif loads
333353
class_eval <<-RUBY, __FILE__, __LINE__ + 1
334354
def load_#{arg_defn.keyword}(value)
335355
load_application_object(:#{arg_defn.keyword}, value)

spec/graphql/schema/relay_classic_mutation_spec.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,71 @@
5252
end
5353
end
5454

55+
describe "loading multiple application objects" do
56+
let(:query_str) {
57+
<<-GRAPHQL
58+
mutation($ids: [ID!]!) {
59+
upvoteEnsembles(input: {ensembleIds: $ids}) {
60+
ensembles {
61+
id
62+
}
63+
}
64+
}
65+
GRAPHQL
66+
}
67+
68+
it "loads arguments as objects of the given type and strips `_ids` suffix off argument name and appends `s`" do
69+
res = Jazz::Schema.execute(query_str, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
70+
assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["upvoteEnsembles"]["ensembles"].map { |e| e["id"] }
71+
end
72+
73+
it "uses the `as:` name when loading" do
74+
as_bands_query_str = query_str.sub("upvoteEnsembles", "upvoteEnsemblesAsBands")
75+
res = Jazz::Schema.execute(as_bands_query_str, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
76+
assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["upvoteEnsemblesAsBands"]["ensembles"].map { |e| e["id"] }
77+
end
78+
79+
it "doesn't append `s` to argument names that already end in `s`" do
80+
query = <<-GRAPHQL
81+
mutation($ids: [ID!]!) {
82+
upvoteEnsemblesIds(input: {ensemblesIds: $ids}) {
83+
ensembles {
84+
id
85+
}
86+
}
87+
}
88+
GRAPHQL
89+
90+
res = Jazz::Schema.execute(query, variables: { ids: ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"]})
91+
assert_equal ["Ensemble/Robert Glasper Experiment", "Ensemble/Bela Fleck and the Flecktones"], res["data"]["upvoteEnsemblesIds"]["ensembles"].map { |e| e["id"] }
92+
end
93+
94+
it "returns an error instead when the ID resolves to nil" do
95+
res = Jazz::Schema.execute(query_str, variables: {
96+
ids: ["Ensemble/Nonexistant Name"],
97+
})
98+
assert_nil res["data"].fetch("upvoteEnsembles")
99+
assert_equal ['No object found for `ensembleIds: "Ensemble/Nonexistant Name"`'], res["errors"].map { |e| e["message"] }
100+
end
101+
102+
it "returns an error instead when the ID resolves to an object of the wrong type" do
103+
res = Jazz::Schema.execute(query_str, variables: {
104+
ids: ["Instrument/Organ"],
105+
})
106+
assert_nil res["data"].fetch("upvoteEnsembles")
107+
assert_equal ["No object found for `ensembleIds: \"Instrument/Organ\"`"], res["errors"].map { |e| e["message"] }
108+
end
109+
110+
it "raises an authorization error when the type's auth fails" do
111+
res = Jazz::Schema.execute(query_str, variables: {
112+
ids: ["Ensemble/Spinal Tap"],
113+
})
114+
assert_nil res["data"].fetch("upvoteEnsembles")
115+
# Failed silently
116+
refute res.key?("errors")
117+
end
118+
end
119+
55120
describe "loading application objects" do
56121
let(:query_str) {
57122
<<-GRAPHQL

spec/graphql/schema/resolver_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,23 @@ def resolve(int:)
192192
end
193193
end
194194

195+
class PrepResolver9Array < BaseResolver
196+
argument :int_ids, [ID], required: true, loads: HasValue, as: :ints
197+
# Make sure the lazy object is resolved properly:
198+
type [HasValue], null: false
199+
def object_from_id(type, id, ctx)
200+
# Make sure a lazy object is handled appropriately
201+
LazyBlock.new {
202+
# Make sure that the right type ends up here
203+
id.to_i + type.graphql_name.length
204+
}
205+
end
206+
207+
def resolve(ints:)
208+
ints.map { |int| int * 3}
209+
end
210+
end
211+
195212
class PrepResolver10 < BaseResolver
196213
argument :int1, Integer, required: true
197214
argument :int2, Integer, required: true, as: :integer_2
@@ -285,6 +302,7 @@ def resolve_field(*args)
285302
field :prep_resolver_6, resolver: PrepResolver6
286303
field :prep_resolver_7, resolver: PrepResolver7
287304
field :prep_resolver_9, resolver: PrepResolver9
305+
field :prep_resolver_9_array, resolver: PrepResolver9Array
288306
field :prep_resolver_10, resolver: PrepResolver10
289307
field :prep_resolver_11, resolver: PrepResolver11
290308
field :prep_resolver_12, resolver: PrepResolver12
@@ -509,6 +527,14 @@ def add_error_assertions(field_name, description)
509527
# (5 + 8) * 3
510528
assert_equal 39, res["data"]["prepResolver9"]["value"]
511529
end
530+
531+
it "supports loading array of ids" do
532+
res = exec_query('{ prepResolver9Array(intIds: ["1", "10", "100"]) { value } }')
533+
# (1 + 8) * 3
534+
# (10 + 8) * 3
535+
# (100 + 8) * 3
536+
assert_equal [27, 54, 324], res["data"]["prepResolver9Array"].map { |v| v["value"] }
537+
end
512538
end
513539
end
514540
end

spec/support/jazz.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,42 @@ def resolve(ensemble:, new_name:)
488488
end
489489
end
490490

491+
class UpvoteEnsembles < GraphQL::Schema::RelayClassicMutation
492+
argument :ensemble_ids, [ID], required: true, loads: Ensemble
493+
494+
field :ensembles, [Ensemble], null: false
495+
496+
def resolve(ensembles:)
497+
{
498+
ensembles: ensembles
499+
}
500+
end
501+
end
502+
503+
class UpvoteEnsemblesAsBands < GraphQL::Schema::RelayClassicMutation
504+
argument :ensemble_ids, [ID], required: true, loads: Ensemble, as: :bands
505+
506+
field :ensembles, [Ensemble], null: false
507+
508+
def resolve(bands:)
509+
{
510+
ensembles: bands
511+
}
512+
end
513+
end
514+
515+
class UpvoteEnsemblesIds < GraphQL::Schema::RelayClassicMutation
516+
argument :ensembles_ids, [ID], required: true, loads: Ensemble
517+
518+
field :ensembles, [Ensemble], null: false
519+
520+
def resolve(ensembles:)
521+
{
522+
ensembles: ensembles
523+
}
524+
end
525+
end
526+
491527
class RenameEnsembleAsBand < RenameEnsemble
492528
argument :ensemble_id, ID, required: true, loads: Ensemble, as: :band
493529

@@ -504,6 +540,9 @@ class Mutation < BaseObject
504540
field :add_instrument, mutation: AddInstrument
505541
field :add_sitar, mutation: AddSitar
506542
field :rename_ensemble, mutation: RenameEnsemble
543+
field :upvote_ensembles, mutation: UpvoteEnsembles
544+
field :upvote_ensembles_as_bands, mutation: UpvoteEnsemblesAsBands
545+
field :upvote_ensembles_ids, mutation: UpvoteEnsemblesIds
507546
field :rename_ensemble_as_band, mutation: RenameEnsembleAsBand
508547

509548
def add_ensemble(input:)

0 commit comments

Comments
 (0)