Skip to content

Commit

Permalink
Update field definitions cop to handle multiple definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
toneymathews committed Sep 26, 2022
1 parent 85db5df commit 81e047f
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 8 deletions.
29 changes: 22 additions & 7 deletions lib/rubocop/cop/graphql/field_definitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ class FieldDefinitions < Base # rubocop:disable Metrics/ClassLength
PATTERN

def on_send(node)
return if !field_definition?(node) || style != :define_resolver_after_definition
return if !field?(node) || style != :define_resolver_after_definition

node = node.parent if field_definition_with_body?(node.parent)
field = RuboCop::GraphQL::Field.new(node)
check_resolver_is_defined_after_definition(field)
end
Expand Down Expand Up @@ -114,7 +115,10 @@ def group_field_declarations(corrector, node)
"sharing resolver method."

def check_resolver_is_defined_after_definition(field)
return if field.kwargs.resolver || field.kwargs.method || field.kwargs.hash_key
multiple_definitions = multiple_definitions(field)
return if field.node != multiple_definitions.last

return if field_has_no_resolver_method(field)

method_definition = field.schema_member.find_method_definition(field.resolver_method_name)
return unless method_definition
Expand All @@ -125,11 +129,25 @@ def check_resolver_is_defined_after_definition(field)
return if resolver_defined_after_definition?(field, method_definition)

add_offense(field.node,
message: offense_message(fields_with_same_resolver.one?)) do |corrector|
message: offense_message(
fields_with_same_resolver.one? && multiple_definitions.size == 1
)) do |corrector|
place_resolver_after_field_definition(corrector, field.node)
end
end

def field_has_no_resolver_method(field)
field.kwargs.resolver || field.kwargs.method || field.kwargs.hash_key
end

def multiple_definitions(field)
field.schema_member.body.select { |node| field?(node) && field_name(node) == field.name }
end

def field_name(node)
RuboCop::GraphQL::Field.new(node).name
end

def offense_message(single_field_using_resolver)
single_field_using_resolver ? RESOLVER_AFTER_FIELD_MSG : RESOLVER_AFTER_LAST_FIELD_MSG
end
Expand Down Expand Up @@ -157,10 +175,7 @@ def resolver_defined_after_definition?(field, method_definition)
def fields_with_same_resolver(field, resolver)
fields = field.schema_member.body
.select { |node| field?(node) }
.map do |node|
field = field_definition_with_body?(node) ? node.children.first : node
RuboCop::GraphQL::Field.new(field)
end
.map { |node| RuboCop::GraphQL::Field.new(node) }

[].tap do |fields_with_same_resolver|
fields.each do |field|
Expand Down
123 changes: 122 additions & 1 deletion spec/rubocop/cop/graphql/field_definitions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ class UserType < BaseType
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Define resolver method after field definition.
field :image_url, String, null: false do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Define resolver method after field definition.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Define resolver method after field definition.
argument :width, Integer, required: false
argument :height, Integer, required: false
end
Expand Down Expand Up @@ -790,5 +790,126 @@ def name
end
end
end

context "when there are multiple field definitions" do
it "not register an offense when resolver is defined after the last field definitions" do
expect_no_offenses(<<~RUBY)
class UserType < BaseType
field :first_name, Name, null: true
field :first_name, String, null: true
def first_name
object.contact_data.first_name
end
field :last_name, String, null: true
def last_name
object.contact_data.last_name
end
end
RUBY
end

it "registers an offense when resolver is defined before the last field definition" do
expect_offense(<<~RUBY)
class UserType < BaseType
field :first_name, Name, null: true
def first_name
object.contact_data.first_name
end
field :first_name, String, null: true
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Define resolver method after last field definition sharing resolver method.
field :last_name, String, null: true
def last_name
object.contact_data.last_name
end
end
RUBY

expect_correction(<<~RUBY)
class UserType < BaseType
field :first_name, Name, null: true
field :first_name, String, null: true
def first_name
object.contact_data.first_name
end
field :last_name, String, null: true
def last_name
object.contact_data.last_name
end
end
RUBY
end

context "when the field nodes are same but the field definitions are not" do
it "not register an offense when resolver is defined after the last field definition" do
expect_no_offenses(<<~RUBY)
class UserType < BaseType
field :image_url, String, null: false do
argument :width, Integer, required: false
end
field :image_url, String, null: false do
argument :width, Integer, required: false
argument :height, Integer, required: false
end
def image_url
object.image_url
end
field :first_name, String, null: false
end
RUBY
end

it "register an offense when when resolver method is before the last field definition" do
expect_offense(<<~RUBY)
class UserType < BaseType
field :image_url, String, null: false do
argument :width, Integer, required: false
end
def image_url
object.image_url
end
field :image_url, String, null: false do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Define resolver method after last field definition sharing resolver method.
argument :width, Integer, required: false
argument :height, Integer, required: false
end
end
RUBY

expect_correction(<<~RUBY)
class UserType < BaseType
field :image_url, String, null: false do
argument :width, Integer, required: false
end
field :image_url, String, null: false do
argument :width, Integer, required: false
argument :height, Integer, required: false
end
def image_url
object.image_url
end
end
RUBY
end
end
end
end
end

0 comments on commit 81e047f

Please sign in to comment.