This commit is contained in:
Joshua Peek 2016-10-31 15:28:09 -07:00
Родитель 4b2ef6ce00
Коммит 190c93dc30
3 изменённых файлов: 105 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,63 @@
require "active_support/inflector"
require "graphql"
require "graphql/client/erubis"
require "rubocop"
module RuboCop
module Cop
module GraphQL
# Public: Rubocop for catching overfetched fields in ERB templates.
class Overfetch < Cop
def_node_search :send_methods, "(send ...)"
def investigate(processed_source)
erb = File.read(processed_source.buffer.name)
query, = ::GraphQL::Client::Erubis.extract_graphql_section(erb)
return unless query
aliases = {}
fields = {}
ranges = {}
# TODO: Use GraphQL client parser
document = ::GraphQL.parse(query.gsub(/::/, "__"))
visitor = ::GraphQL::Language::Visitor.new(document)
visitor[::GraphQL::Language::Nodes::Field] << ->(node, _parent) do
name = node.alias || node.name
fields[name] ||= 0
field_aliases(name).each { |n| (aliases[n] ||= []) << name }
ranges[name] ||= source_range(processed_source.buffer, node.line, 0)
end
visitor.visit
send_methods(processed_source.ast).each do |node|
_receiver, method_name, *_args = *node
aliases.fetch(method_name.to_s, []).each do |field_name|
fields[field_name] += 1
end
end
fields.each do |field, count|
next if count > 0
add_offense(nil, ranges[field], "GraphQL field '#{field}' query but was not used in template.")
end
end
def field_aliases(name)
names = Set.new
names << name
names << "#{name}?"
names << underscore_name = ActiveSupport::Inflector.underscore(name)
names << "#{underscore_name}?"
names << "each_node" if name == "edges" || name == "node"
names
end
end
end
end
end

Просмотреть файл

@ -0,0 +1,35 @@
require "graphql/client/erubis"
require "rubocop/cop/graphql/overfetch"
require "minitest/autorun"
class TestRubocopOverfetch < MiniTest::Test
Root = File.expand_path("..", __FILE__)
def setup
config = RuboCop::Config.new
@cop = RuboCop::Cop::GraphQL::Overfetch.new(config)
end
def test_all_fields_used
investigate(@cop, "#{Root}/views/users/show.html.erb")
assert_empty @cop.offenses.map(&:message)
end
def test_field_unused
investigate(@cop, "#{Root}/views/users/overfetch.html.erb")
assert_equal 1, @cop.offenses.count
assert_equal "GraphQL field 'birthday' query but was not used in template.", @cop.offenses.first.message
end
private
def investigate(cop, path)
engine = GraphQL::Client::Erubis.new(File.read(path))
processed_source = RuboCop::ProcessedSource.new(engine.src, RUBY_VERSION.to_f, path)
commissioner = RuboCop::Cop::Commissioner.new([cop], [], raise_error: true)
commissioner.investigate(processed_source)
commissioner
end
end

Просмотреть файл

@ -0,0 +1,7 @@
<%graphql
fragment User on User {
login
birthday
}
%>
<%= user.login %>