Skip to content

Commit

Permalink
Implement OrderedArguments cop #16 (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
kiskoza authored Mar 17, 2021
1 parent 14f768b commit ff330cc
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## master

- [PR#34](https://github.com/DmitryTsepelev/rubocop-graphql/pull/34) Add OrderedArguments cop ([@kiskoza][])
- [PR#33](https://github.com/DmitryTsepelev/rubocop-graphql/pull/33) Add autocorrect for OrderedFields cop ([@kiskoza][])

## 0.6.2 (2021-03-03)
Expand Down
120 changes: 120 additions & 0 deletions lib/rubocop/cop/graphql/ordered_arguments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# frozen_string_literal: true

module RuboCop
module Cop
module GraphQL
# Arguments should be alphabetically sorted within groups.
#
# @example
# # good
#
# class UpdateProfile < BaseMutation
# argument :email, String, required: false
# argument :name, String, required: false
# end
#
# # good
#
# class UpdateProfile < BaseMutation
# argument :uuid, ID, required: true
#
# argument :email, String, required: false
# argument :name, String, required: false
# end
#
# # good
#
# class UserType < BaseType
# field :posts, PostType do
# argument :created_after, ISO8601DateTime, required: false
# argument :created_before, ISO8601DateTime, required: false
# end
# end
#
# # bad
#
# class UpdateProfile < BaseMutation
# argument :uuid, ID, required: true
# argument :name, String, required: false
# argument :email, String, required: false
# end
#
# # bad
#
# class UserType < BaseType
# field :posts, PostType do
# argument :created_before, ISO8601DateTime, required: false
# argument :created_after, ISO8601DateTime, required: false
# end
# end
#
class OrderedArguments < Cop
MSG = "Arguments should be sorted in an alphabetical order within their section. " \
"Field `%<current>s` should appear before `%<previous>s`."

def investigate(processed_source)
return if processed_source.blank?

argument_declarations(processed_source.ast)
.each_cons(2) do |previous, current|
next unless consecutive_lines(previous, current)
next if argument_name(current) > argument_name(previous)

register_offense(previous, current)
end
end

def autocorrect(node)
declarations = argument_declarations(processed_source.ast)
node_index = declarations.map(&:location).find_index(node.location)
previous_declaration = declarations.to_a[node_index - 1]

current_range = declaration(node)
previous_range = declaration(previous_declaration)

lambda do |corrector|
swap_range(corrector, current_range, previous_range)
end
end

private

def declaration(node)
buffer = processed_source.buffer
begin_pos = node.source_range.begin_pos
end_line = buffer.line_for_position(node.loc.expression.end_pos)
end_pos = buffer.line_range(end_line).end_pos
Parser::Source::Range.new(buffer, begin_pos, end_pos)
end

def swap_range(corrector, range1, range2)
src1 = range1.source
src2 = range2.source
corrector.replace(range1, src2)
corrector.replace(range2, src1)
end

def register_offense(previous, current)
message = format(
self.class::MSG,
previous: argument_name(previous),
current: argument_name(current)
)
add_offense(current, message: message)
end

def argument_name(node)
node.first_argument.value.to_s
end

def consecutive_lines(previous, current)
previous.source_range.last_line == current.source_range.first_line - 1
end

def_node_search :argument_declarations, <<~PATTERN
(send nil? :argument (:sym _) ...)
PATTERN
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/graphql_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
require_relative "graphql/field_name"
require_relative "graphql/resolver_method_length"
require_relative "graphql/object_description"
require_relative "graphql/ordered_arguments"
require_relative "graphql/ordered_fields"
104 changes: 104 additions & 0 deletions spec/rubocop/cop/graphql/ordered_arguments_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::GraphQL::OrderedArguments do
subject(:cop) { described_class.new(config) }

let(:config) { RuboCop::Config.new }

context "when arguments are alphabetically sorted" do
it "not registers an offense" do
expect_no_offenses(<<~RUBY)
class UpdateProfile < BaseMutation
argument :email, String, required: false
argument :name, String, required: false
end
RUBY
end
end

context "when each individual groups are alphabetically sorted" do
it "not registers an offense" do
expect_no_offenses(<<~RUBY)
class UpdateProfile < BaseMutation
argument :uuid, ID, required: true
argument :email, String, required: false
argument :name, String, required: false
end
RUBY
end
end

context "when arguments are alphabetically sorted inside a field declaration" do
it "not registers an offense" do
expect_no_offenses(<<~RUBY)
class UserType < BaseType
field :posts, PostType do
argument :created_after, ISO8601DateTime, required: false
argument :created_before, ISO8601DateTime, required: false
end
end
RUBY
end
end

context "when arguments are not alphabetically sorted" do
it "registers an offense" do
expect_offense(<<~RUBY)
class UpdateProfile < BaseMutation
argument :uuid, ID, required: true
argument :email, String, required: false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Arguments should be sorted in an alphabetical order within their section. Field `email` should appear before `uuid`.
argument :name, String, required: false
end
RUBY

expect_correction(<<~RUBY)
class UpdateProfile < BaseMutation
argument :email, String, required: false
argument :name, String, required: false
argument :uuid, ID, required: true
end
RUBY
end
end

context "when arguments are not alphabetically sorted inside a field declaration" do
it "not registers an offense" do
expect_offense(<<~RUBY)
class UserType < BaseType
field :posts, PostType do
argument :created_before, ISO8601DateTime, required: false
argument :created_after, ISO8601DateTime, required: false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Arguments should be sorted in an alphabetical order within their section. Field `created_after` should appear before `created_before`.
end
end
RUBY
end
end

context "when an unordered argument declaration takes several lines" do
it "registers an offense" do
expect_offense(<<~RUBY)
class UpdateProfile < BaseMutation
argument :uuid,
ID,
required: true
argument :email, String, required: false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Arguments should be sorted in an alphabetical order within their section. Field `email` should appear before `uuid`.
argument :name, String, required: false
end
RUBY

expect_correction(<<~RUBY)
class UpdateProfile < BaseMutation
argument :email, String, required: false
argument :name, String, required: false
argument :uuid,
ID,
required: true
end
RUBY
end
end
end

0 comments on commit ff330cc

Please sign in to comment.