rubocop-graphql 0.11.2 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa0ffd49d0fa9883050aa121e7a344afff34060a89b5d995023dcb76437e8923
4
- data.tar.gz: f34f5c5ddfedd3fb597b0216c88d56cf554bb124e14633a8632f8045c6e935be
3
+ metadata.gz: 1d488e2bcf0fac1f61555afb433e26834bc653b88598ba9fd23d58be29db44cc
4
+ data.tar.gz: 31afb3a28f7cd44f165dcd4bd93aecebf270ccc2af05f50816f171f19c88680d
5
5
  SHA512:
6
- metadata.gz: 00efa45967096346f711c41c712d7e1c09cc301e81c713f9dc7359120854d5273312e471de989d80fbaa5a8f3fa1736dd3b3de4e18688c1f06ed3b86b68dcede
7
- data.tar.gz: a35bfa43b6a7acf8d2820f7e91f442c0ab04ab9867db0e43d427c90daff711ccf47080b66b62f122fbca416e6c604f69e591f4124c24fdf54304f78f5e1d1dd8
6
+ metadata.gz: a5d35e978439e99de72eb4d90088b6a726bfbf78a6303ce422bfb09a05b4d965e7a28acb06ada12538dea8148f691384a48682ad651cc39d631e779d41098cfb
7
+ data.tar.gz: 44ca6c9891b4184b6ab40b1a4be8d9398fb94276502a603f09639bf5993c553aee01f845e61a119470f1a7eb5f51659db7114bf47a472c41394649b91fc20c28
data/config/default.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  GraphQL:
2
+ Enabled: true
2
3
  Include:
3
4
  - "**/graphql/**/*"
4
5
  Exclude:
@@ -104,4 +105,8 @@ GraphQL/OrderedFields:
104
105
  VersionAdded: '0.80'
105
106
  Description: 'Fields should be alphabetically sorted within groups'
106
107
 
108
+ GraphQL/UnusedArgument:
109
+ Enabled: true
110
+ Description: 'Arguments should either be listed explicitly or **rest should be in the resolve signature'
111
+
107
112
 
@@ -20,23 +20,31 @@ module RuboCop
20
20
  # end
21
21
  #
22
22
  class ArgumentUniqueness < Base
23
+ include RuboCop::GraphQL::NodeUniqueness
24
+
23
25
  MSG = "Argument names should only be defined once per block. "\
24
26
  "Argument `%<current>s` is duplicated%<field_name>s."
25
27
 
26
28
  def on_class(node)
27
- global_argument_names = Set.new
28
- argument_names_by_field = {}
29
+ return if ::RuboCop::GraphQL::Class.new(node).nested?
30
+
31
+ # { "MyClassName" => { "test_field" => <Set: {"field_arg_name"}> } }
32
+ argument_names_by_field_by_class = Hash.new do |h, k|
33
+ h[k] = Hash.new do |h1, k1|
34
+ h1[k1] = Set.new
35
+ end
36
+ end
29
37
 
30
38
  argument_declarations(node).each do |current|
31
39
  current_field_name = field_name(current)
32
40
  current_argument_name = argument_name(current)
41
+ class_name = current_class_full_name(current)
33
42
 
34
- if current_field_name
35
- argument_names_by_field[current_field_name] ||= Set.new
36
- argument_names = argument_names_by_field[current_field_name]
37
- else
38
- argument_names = global_argument_names
39
- end
43
+ argument_names = if current_field_name
44
+ argument_names_by_field_by_class[class_name][current_field_name]
45
+ else
46
+ argument_names_by_field_by_class[class_name][:root]
47
+ end
40
48
 
41
49
  unless argument_names.include?(current_argument_name)
42
50
  argument_names.add(current_argument_name)
@@ -27,13 +27,19 @@ module RuboCop
27
27
  # end
28
28
  #
29
29
  class FieldUniqueness < Base
30
+ include RuboCop::GraphQL::NodeUniqueness
31
+
30
32
  MSG = "Field names should only be defined once per type. "\
31
33
  "Field `%<current>s` is duplicated."
32
34
 
33
35
  def on_class(node)
34
- field_names = Set.new
36
+ return if ::RuboCop::GraphQL::Class.new(node).nested?
37
+
38
+ field_names_by_class = Hash.new { |h, k| h[k] = Set.new }
35
39
 
36
40
  field_declarations(node).each do |current|
41
+ current_class = current_class_full_name(current)
42
+ field_names = field_names_by_class[current_class]
37
43
  current_field_name = field_name(current)
38
44
 
39
45
  unless field_names.include?(current_field_name)
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # Arguments should either be listed explicitly or **rest should be in the resolve signature.
7
+ #
8
+ # @example
9
+ # # good
10
+ #
11
+ # class SomeResolver < Resolvers::Base
12
+ # argument :arg1, String, required: true
13
+ # argument :user_id, String, required: true, loads: Types::UserType
14
+ # argument :post_id, String, loads: Types::PostType, as: :article
15
+ # argument :comment_ids, String, loads: Types::CommentType
16
+ #
17
+ # def resolve(arg1:, user:, article:, comments:); end
18
+ # end
19
+ #
20
+ # # good
21
+ #
22
+ # class SomeResolver < Resolvers::Base
23
+ # argument :arg1, String, required: true
24
+ # argument :user_id, String, required: true, loads: Types::UserType
25
+ # argument :comment_ids, String, loads: Types::CommentType
26
+ #
27
+ # def resolve(arg1:, **rest); end
28
+ # end
29
+ #
30
+ # # good
31
+ #
32
+ # class SomeResolver < Resolvers::Base
33
+ # type SomeType, null: false
34
+ #
35
+ # argument :arg1, String, required: true
36
+ # argument :arg2, String, required: true
37
+ #
38
+ # def resolve(args); end
39
+ # end
40
+ #
41
+ # # bad
42
+ #
43
+ # class SomeResolver < Resolvers::Base
44
+ # type SomeType, null: false
45
+ #
46
+ # argument :arg1, String, required: true
47
+ # argument :arg2, String, required: true
48
+ #
49
+ # def resolve(arg1:); end
50
+ # end
51
+ #
52
+ # # bad
53
+ #
54
+ # class SomeResolver < Resolvers::Base
55
+ # type SomeType, null: false
56
+ #
57
+ # argument :arg1, String, required: true
58
+ # argument :arg2, String, required: true
59
+ #
60
+ # def resolve; end
61
+ # end
62
+ #
63
+ class UnusedArgument < Base
64
+ extend AutoCorrector
65
+
66
+ MSG = "Argument%<ending>s `%<unresolved_args>s` should be listed in the resolve signature."
67
+
68
+ def on_class(node)
69
+ resolve_method_node = find_resolve_method_node(node)
70
+ return if resolve_method_node.nil? ||
71
+ resolve_method_node.arguments.any? do |arg|
72
+ arg.arg_type? || arg.kwrestarg_type?
73
+ end
74
+
75
+ declared_arg_nodes = argument_declarations(node)
76
+ return unless declared_arg_nodes.any?
77
+
78
+ unresolved_args = find_unresolved_args(resolve_method_node, declared_arg_nodes)
79
+ register_offense(resolve_method_node, unresolved_args) if unresolved_args.any?
80
+ end
81
+
82
+ private
83
+
84
+ def find_resolve_method_node(node)
85
+ resolve_method_nodes = resolve_method_definition(node)
86
+ resolve_method_nodes.to_a.last if resolve_method_nodes.any?
87
+ end
88
+
89
+ def find_unresolved_args(method_node, declared_arg_nodes)
90
+ resolve_method_kwargs = method_node.arguments.select do |arg|
91
+ arg.kwarg_type? || arg.kwoptarg_type?
92
+ end
93
+ resolve_method_kwargs_names = resolve_method_kwargs.map do |node|
94
+ node.node_parts[0]
95
+ end.to_set
96
+ declared_args = declared_arg_nodes.map { |node| RuboCop::GraphQL::Argument.new(node) }
97
+ declared_args.map(&method(:arg_name)).uniq.reject do |declared_arg_name|
98
+ resolve_method_kwargs_names.include?(declared_arg_name)
99
+ end
100
+ end
101
+
102
+ def register_offense(node, unresolved_args)
103
+ unresolved_args_source = unresolved_args.map { |v| "#{v}:" }.join(", ")
104
+
105
+ message = format(
106
+ self.class::MSG,
107
+ unresolved_args: unresolved_args_source,
108
+ ending: unresolved_args.size == 1 ? "" : "s"
109
+ )
110
+
111
+ add_offense(node, message: message) do |corrector|
112
+ if node.arguments?
113
+ corrector.insert_after(arg_end(node.arguments.last), ", #{unresolved_args_source}")
114
+ else
115
+ corrector.insert_after(method_name(node), "(#{unresolved_args_source})")
116
+ end
117
+ end
118
+ end
119
+
120
+ def method_name(node)
121
+ node.loc.keyword.end.resize(node.method_name.to_s.size + 1)
122
+ end
123
+
124
+ def arg_end(node)
125
+ node.loc.expression.end
126
+ end
127
+
128
+ def inferred_arg_name(name_as_string)
129
+ case name_as_string
130
+ when /_id$/
131
+ name_as_string.sub(/_id$/, "").to_sym
132
+ when /_ids$/
133
+ name_as_string.sub(/_ids$/, "")
134
+ .sub(/([^s])$/, "\\1s")
135
+ .to_sym
136
+ else
137
+ name
138
+ end
139
+ end
140
+
141
+ def arg_name(declared_arg)
142
+ return declared_arg.as if declared_arg.kwargs.as
143
+ return inferred_arg_name(declared_arg.name.to_s) if declared_arg.kwargs.loads
144
+
145
+ declared_arg.name
146
+ end
147
+
148
+ def_node_search :argument_declarations, <<~PATTERN
149
+ (send nil? :argument (:sym _) ...)
150
+ PATTERN
151
+
152
+ def_node_search :resolve_method_definition, <<~PATTERN
153
+ (def :resolve
154
+ (args ...) ...)
155
+ PATTERN
156
+ end
157
+ end
158
+ end
159
+ end
@@ -16,3 +16,4 @@ require_relative "graphql/resolver_method_length"
16
16
  require_relative "graphql/object_description"
17
17
  require_relative "graphql/ordered_arguments"
18
18
  require_relative "graphql/ordered_fields"
19
+ require_relative "graphql/unused_argument"
@@ -19,6 +19,14 @@ module RuboCop
19
19
  (pair (sym :description) ...)
20
20
  PATTERN
21
21
 
22
+ def_node_matcher :loads_kwarg?, <<~PATTERN
23
+ (pair (sym :loads) ...)
24
+ PATTERN
25
+
26
+ def_node_matcher :as_kwarg?, <<~PATTERN
27
+ (pair (sym :as) ...)
28
+ PATTERN
29
+
22
30
  def initialize(argument_node)
23
31
  @nodes = argument_kwargs(argument_node) || []
24
32
  end
@@ -26,6 +34,14 @@ module RuboCop
26
34
  def description
27
35
  @nodes.find { |kwarg| description_kwarg?(kwarg) }
28
36
  end
37
+
38
+ def loads
39
+ @nodes.find { |kwarg| loads_kwarg?(kwarg) }
40
+ end
41
+
42
+ def as
43
+ @nodes.find { |kwarg| as_kwarg?(kwarg) }
44
+ end
29
45
  end
30
46
  end
31
47
  end
@@ -13,6 +13,10 @@ module RuboCop
13
13
  (send nil? :argument (:sym $_) ...)
14
14
  PATTERN
15
15
 
16
+ def_node_matcher :argument_as, <<~PATTERN
17
+ (pair (sym :as) (sym $_))
18
+ PATTERN
19
+
16
20
  attr_reader :node
17
21
 
18
22
  def initialize(node)
@@ -23,6 +27,10 @@ module RuboCop
23
27
  @name ||= argument_name(@node)
24
28
  end
25
29
 
30
+ def as
31
+ @as ||= argument_as(kwargs.as)
32
+ end
33
+
26
34
  def description
27
35
  @description ||= argument_description(@node) || kwargs.description || block.description
28
36
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ class Class
6
+ attr_reader :node
7
+
8
+ def initialize(node)
9
+ @node = node
10
+ end
11
+
12
+ def nested?
13
+ node.each_ancestor(:class).any?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ # Shared methods to check duplicated definitions
6
+ module NodeUniqueness
7
+ def current_class_full_name(node)
8
+ node.each_ancestor(:class).map(&:defined_module_name).join("::")
9
+ end
10
+
11
+ def nested_class?(node)
12
+ node.each_ancestor(:class).any?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.11.2".freeze
3
+ VERSION = "0.12.2".freeze
4
4
  end
5
5
  end
@@ -9,11 +9,13 @@ require_relative "rubocop/graphql/version"
9
9
  require_relative "rubocop/graphql/inject"
10
10
  require_relative "rubocop/graphql/description_method"
11
11
  require_relative "rubocop/graphql/node_pattern"
12
+ require_relative "rubocop/graphql/node_uniqueness"
12
13
  require_relative "rubocop/graphql/swap_range"
13
14
 
14
15
  require_relative "rubocop/graphql/argument"
15
16
  require_relative "rubocop/graphql/argument/block"
16
17
  require_relative "rubocop/graphql/argument/kwargs"
18
+ require_relative "rubocop/graphql/class"
17
19
  require_relative "rubocop/graphql/field"
18
20
  require_relative "rubocop/graphql/field/block"
19
21
  require_relative "rubocop/graphql/field/kwargs"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.2
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Tsepelev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-03 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -100,11 +100,13 @@ files:
100
100
  - lib/rubocop/cop/graphql/ordered_arguments.rb
101
101
  - lib/rubocop/cop/graphql/ordered_fields.rb
102
102
  - lib/rubocop/cop/graphql/resolver_method_length.rb
103
+ - lib/rubocop/cop/graphql/unused_argument.rb
103
104
  - lib/rubocop/cop/graphql_cops.rb
104
105
  - lib/rubocop/graphql.rb
105
106
  - lib/rubocop/graphql/argument.rb
106
107
  - lib/rubocop/graphql/argument/block.rb
107
108
  - lib/rubocop/graphql/argument/kwargs.rb
109
+ - lib/rubocop/graphql/class.rb
108
110
  - lib/rubocop/graphql/description_method.rb
109
111
  - lib/rubocop/graphql/ext/snake_case.rb
110
112
  - lib/rubocop/graphql/field.rb
@@ -112,6 +114,7 @@ files:
112
114
  - lib/rubocop/graphql/field/kwargs.rb
113
115
  - lib/rubocop/graphql/inject.rb
114
116
  - lib/rubocop/graphql/node_pattern.rb
117
+ - lib/rubocop/graphql/node_uniqueness.rb
115
118
  - lib/rubocop/graphql/schema_member.rb
116
119
  - lib/rubocop/graphql/swap_range.rb
117
120
  - lib/rubocop/graphql/version.rb
@@ -121,8 +124,8 @@ licenses:
121
124
  metadata:
122
125
  homepage_uri: https://github.com/DmitryTsepelev/rubocop-graphql
123
126
  source_code_uri: https://github.com/DmitryTsepelev/rubocop-graphql
124
- changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/CHANGELOG.md
125
- post_install_message:
127
+ changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/blob/master/CHANGELOG.md
128
+ post_install_message:
126
129
  rdoc_options: []
127
130
  require_paths:
128
131
  - lib
@@ -137,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
140
  - !ruby/object:Gem::Version
138
141
  version: '0'
139
142
  requirements: []
140
- rubygems_version: 3.2.31
141
- signing_key:
143
+ rubygems_version: 3.0.3.1
144
+ signing_key:
142
145
  specification_version: 4
143
146
  summary: Automatic performance checking tool for Ruby code.
144
147
  test_files: []