Skip to content

Commit 5c0a2d5

Browse files
authored
Merge pull request #5480 from Shopify/ravangen.invalid-empty-selection-on-union-type
Pass `resolved_type` into `legacy_invalid_empty_selections_on_union`
2 parents dee9294 + d779607 commit 5c0a2d5

3 files changed

Lines changed: 66 additions & 5 deletions

File tree

lib/graphql/schema.rb

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,7 +1708,7 @@ def did_you_mean(new_dym = NOT_CONFIGURED)
17081708
# If you need to support previous, non-spec behavior which allowed selecting union fields
17091709
# but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
17101710
#
1711-
# If this is `true`, then {.legacy_invalid_empty_selections_on_union} will be called with {Query} objects
1711+
# If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
17121712
# with that kind of selections. You must implement that method
17131713
# @param new_value [Boolean]
17141714
# @return [true, false, nil]
@@ -1724,18 +1724,35 @@ def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED)
17241724
end
17251725
end
17261726

1727+
# This method is called during validation when a previously-allowed, but non-spec
1728+
# query is encountered where a union field has no child selections on it.
1729+
#
1730+
# If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
1731+
#
1732+
# You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
1733+
# to log the violation so that you can contact clients and notify them about changing their queries.
1734+
# Then return a suitable value to tell GraphQL-Ruby how to continue.
1735+
# @param query [GraphQL::Query]
1736+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
1737+
# @return [String] A validation error to return for this query
1738+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
1739+
def legacy_invalid_empty_selections_on_union(query)
1740+
raise "Implement `def self.legacy_invalid_empty_selections_on_union_with_type(query, type)` or `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario"
1741+
end
1742+
17271743
# This method is called during validation when a previously-allowed, but non-spec
17281744
# query is encountered where a union field has no child selections on it.
17291745
#
17301746
# You should implement this method to log the violation so that you can contact clients
17311747
# and notify them about changing their queries. Then return a suitable value to
17321748
# tell GraphQL-Ruby how to continue.
17331749
# @param query [GraphQL::Query]
1750+
# @param type1 [Module] A GraphQL type definition
17341751
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
17351752
# @return [String] A validation error to return for this query
17361753
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
1737-
def legacy_invalid_empty_selections_on_union(query)
1738-
raise "Implement `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario"
1754+
def legacy_invalid_empty_selections_on_union_with_type(query, type)
1755+
legacy_invalid_empty_selections_on_union(query)
17391756
end
17401757

17411758
# This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types

lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def validate_field_selections(ast_node, resolved_type)
4949
if !resolved_type.kind.fields?
5050
case @schema.allow_legacy_invalid_empty_selections_on_union
5151
when true
52-
legacy_invalid_empty_selection_result = @schema.legacy_invalid_empty_selections_on_union(@context.query)
52+
legacy_invalid_empty_selection_result = @schema.legacy_invalid_empty_selections_on_union_with_type(@context.query, resolved_type)
5353
case legacy_invalid_empty_selection_result
5454
when :return_validation_error
5555
# keep `return_validation_error = true`
@@ -61,7 +61,7 @@ def validate_field_selections(ast_node, resolved_type)
6161
return_validation_error = false
6262
legacy_invalid_empty_selection_result = nil
6363
else
64-
raise GraphQL::InvariantError, "Unexpected return value from legacy_invalid_empty_selections_on_union, must be `:return_validation_error`, String, or nil (got: #{legacy_invalid_empty_selection_result.inspect})"
64+
raise GraphQL::InvariantError, "Unexpected return value from legacy_invalid_empty_selections_on_union_with_type, must be `:return_validation_error`, String, or nil (got: #{legacy_invalid_empty_selection_result.inspect})"
6565
end
6666
when false
6767
# pass -- error below

spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@
9090

9191
describe "selections on unions" do
9292
let(:query_string) { "{ searchDairy }"}
93+
describe "When the schema has custom handling with type to return the message" do
94+
let(:schema) { Class.new(Dummy::Schema) {
95+
allow_legacy_invalid_empty_selections_on_union(true)
96+
def self.legacy_invalid_empty_selections_on_union_with_type(query, type)
97+
:return_validation_error
98+
end
99+
}
100+
}
101+
102+
it "returns the default message" do
103+
expected_err = "Field must have selections (field 'searchDairy' returns DairyProduct but has no selections. Did you mean 'searchDairy { ... }'?)"
104+
assert_includes(errors.map { |e| e["message"] }, expected_err)
105+
end
106+
end
107+
93108
describe "When the schema has custom handling to return the message" do
94109
let(:schema) { Class.new(Dummy::Schema) {
95110
allow_legacy_invalid_empty_selections_on_union(true)
@@ -105,6 +120,21 @@ def self.legacy_invalid_empty_selections_on_union(query)
105120
end
106121
end
107122

123+
describe "When the schema has custom handling with type to return a custom message" do
124+
let(:schema) { Class.new(Dummy::Schema) {
125+
allow_legacy_invalid_empty_selections_on_union(true)
126+
def self.legacy_invalid_empty_selections_on_union_with_type(query, type)
127+
"Boo, hiss!"
128+
end
129+
}
130+
}
131+
132+
it "returns the custom message" do
133+
expected_err = "Boo, hiss!"
134+
assert_includes(errors.map { |e| e["message"] }, expected_err)
135+
end
136+
end
137+
108138
describe "When the schema has custom handling to return a custom message" do
109139
let(:schema) { Class.new(Dummy::Schema) {
110140
allow_legacy_invalid_empty_selections_on_union(true)
@@ -120,6 +150,20 @@ def self.legacy_invalid_empty_selections_on_union(query)
120150
end
121151
end
122152

153+
describe "When the schema has custom handling with type to allow the query" do
154+
let(:schema) { Class.new(Dummy::Schema) {
155+
allow_legacy_invalid_empty_selections_on_union(true)
156+
def self.legacy_invalid_empty_selections_on_union_with_type(query, type)
157+
nil
158+
end
159+
}
160+
}
161+
162+
it "returns no errors" do
163+
assert_equal [], errors
164+
end
165+
end
166+
123167
describe "When the schema has custom handling to allow the query" do
124168
let(:schema) { Class.new(Dummy::Schema) {
125169
allow_legacy_invalid_empty_selections_on_union(true)

0 commit comments

Comments
 (0)