Skip to content

Commit

Permalink
Merge pull request #4804 from lukes/ld-FieldUsage-detect-deprecated-e…
Browse files Browse the repository at this point in the history
…num-values

Allow GraphQL::Analysis::AST::FieldUsage to detect use of deprecated enum value as argument
  • Loading branch information
rmosolgo authored Jan 25, 2024
2 parents 3adaba5 + 9c1deaa commit c8069d7
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 8 deletions.
39 changes: 32 additions & 7 deletions lib/graphql/analysis/ast/field_usage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ def initialize(query)
@used_fields = Set.new
@used_deprecated_fields = Set.new
@used_deprecated_arguments = Set.new
@used_deprecated_enum_values = Set.new
end

def on_leave_field(node, parent, visitor)
field_defn = visitor.field_definition
field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
@used_fields << field
@used_deprecated_fields << field if field_defn.deprecation_reason
arguments = visitor.query.arguments_for(node, visitor.field_definition)
arguments = visitor.query.arguments_for(node, field_defn)
# If there was an error when preparing this argument object,
# then this might be an error or something:
if arguments.respond_to?(:argument_values)
Expand All @@ -28,6 +29,7 @@ def result
used_fields: @used_fields.to_a,
used_deprecated_fields: @used_deprecated_fields.to_a,
used_deprecated_arguments: @used_deprecated_arguments.to_a,
used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
}
end

Expand All @@ -41,16 +43,39 @@ def extract_deprecated_arguments(argument_values)

next if argument.value.nil?

if argument.definition.type.kind.input_object?
argument_type = argument.definition.type
if argument_type.non_null?
argument_type = argument_type.of_type
end

if argument_type.kind.input_object?
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
elsif argument.definition.type.list?
argument
.value
.select { |value| value.respond_to?(:arguments) }
.each { |value| extract_deprecated_arguments(value.arguments.argument_values) } # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
elsif argument_type.kind.enum?
extract_deprecated_enum_value(argument_type, argument.value)
elsif argument_type.list?
inner_type = argument_type.unwrap
case inner_type.kind
when TypeKinds::INPUT_OBJECT
argument.value.each do |value|
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
end
when TypeKinds::ENUM
argument.value.each do |value|
extract_deprecated_enum_value(inner_type, value)
end
else
# Not a kind of input that we track
end
end
end
end

def extract_deprecated_enum_value(enum_type, value)
enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
if enum_value&.deprecation_reason
@used_deprecated_enum_values << enum_value.path
end
end
end
end
end
Expand Down
46 changes: 45 additions & 1 deletion spec/graphql/analysis/ast/field_usage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,36 @@
end
end

describe "query with deprecated enum argument" do
let(:query_string) {%|
query {
fromSource(source: YAK) {
id
}
}
|}

it "keeps track of deprecated arguments" do
assert_equal ['DairyAnimal.YAK'], result[:used_deprecated_enum_values]
end

describe "tracks non-null/list enums" do
let(:query_string) {%|
query {
cheese(id: 1) {
similarCheese(source: [YAK]) {
id
}
}
}
|}

it "keeps track of deprecated arguments" do
assert_equal ['DairyAnimal.YAK'], result[:used_deprecated_enum_values]
end
end
end

describe "query with an array argument sent as null" do
let(:query_string) {%|
query {
Expand Down Expand Up @@ -211,6 +241,20 @@
end
end


describe "mutation with deprecated argument" do
let(:query_string) {%|
mutation {
pushValue(deprecatedTestInput: { oldSource: "deprecated" })
}
|}

it "keeps track of nested deprecated arguments" do
assert_equal ['DairyProductInput.oldSource'], result[:used_deprecated_arguments]
end
end


describe "when an argument prepare raises a GraphQL::ExecutionError" do
class ArgumentErrorFieldUsageSchema < GraphQL::Schema
class FieldUsage < GraphQL::Analysis::AST::FieldUsage
Expand All @@ -234,7 +278,7 @@ class Query < GraphQL::Schema::Object
it "skips analysis of those arguments" do
res = ArgumentErrorFieldUsageSchema.execute("{ f(i: 1) }")
assert_equal ["boom!"], res["errors"].map { |e| e["message"] }
assert_equal({used_fields: ["Query.f"], used_deprecated_arguments: [], used_deprecated_fields: []}, res.context[:field_usage])
assert_equal({used_fields: ["Query.f"], used_deprecated_arguments: [], used_deprecated_fields: [], used_deprecated_enum_values: []}, res.context[:field_usage])
end
end
end
1 change: 1 addition & 0 deletions spec/support/dummy/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ class DairyAppMutation < BaseObject
description "The root for mutations in this schema"
field :push_value, [Integer], null: false, description: "Push a value onto a global array :D" do
argument :value, Integer, as: :val
argument :deprecated_test_input, DairyProductInput, required: false
end
def push_value(val:)
GLOBAL_VALUES << val
Expand Down

0 comments on commit c8069d7

Please sign in to comment.