rubocop-graphql 0.11.3 → 0.12.3

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: ffc9fa90011bdaddbf2d485a5948ede1e7bf2ade1c91c3a960799a1e0cd47bb3
4
- data.tar.gz: 4dcbb755ed55538cfb8539b86f0c186e13b201bbfd0bbf3201d3091b8058c0fe
3
+ metadata.gz: b0b5a6defb854d907bf12c23a7262adc13b55228e3d7cf345f19ea7b1371bacb
4
+ data.tar.gz: 3250274062d10447eb23994e4bc1e99393d4d03a6b17221cdbda5f1d1bd4b2d6
5
5
  SHA512:
6
- metadata.gz: 25cf86cd5e3e3942eafed3383df445c3f71296bc63a617ba4a0a9e597f6c355a1f5f94b1f2718439cd5606c50573758b8d3381a40af59937ba20cba150a9fe3d
7
- data.tar.gz: 745057b345ba218c88b5a21addced87fc6bd972147ac72281efa0e1c1db005694f3ec44d33dc6760bdff68edddf8fedaff1d23f270ca3fc5162b5f01652da902
6
+ metadata.gz: 3f59543ad00f27cf92048d5fa3fb7e09ea99c9e1179839f848316025eb48d66a92012f2cdb13847113d602b740a49aab128d8a4538cf5d8c0f8ea8a2c0f47321
7
+ data.tar.gz: 2b5110aa616044c726f466c6e82a36af0cdad58d096c6359a97a91b3d0c6e023dd4e2ce96f8ecaf44638393bce6b2a4330fa2b3ff76b8c5ecc89d0b07ed6f5e4
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,16 +27,18 @@ 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
- return if nested_class?(node)
36
+ return if ::RuboCop::GraphQL::Class.new(node).nested?
35
37
 
36
38
  field_names_by_class = Hash.new { |h, k| h[k] = Set.new }
37
39
 
38
40
  field_declarations(node).each do |current|
39
- current_class = current_class_name(current)
41
+ current_class = current_class_full_name(current)
40
42
  field_names = field_names_by_class[current_class]
41
43
  current_field_name = field_name(current)
42
44
 
@@ -51,10 +53,6 @@ module RuboCop
51
53
 
52
54
  private
53
55
 
54
- def nested_class?(node)
55
- node.each_ancestor(:class).any?
56
- end
57
-
58
56
  def register_offense(current)
59
57
  message = format(
60
58
  self.class::MSG,
@@ -68,10 +66,6 @@ module RuboCop
68
66
  node.first_argument.value.to_s
69
67
  end
70
68
 
71
- def current_class_name(node)
72
- node.each_ancestor(:class).first.defined_module_name
73
- end
74
-
75
69
  def_node_search :field_declarations, <<~PATTERN
76
70
  {
77
71
  (send nil? :field (:sym _) ...)
@@ -0,0 +1,166 @@
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 = find_declared_arg_nodes(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_declared_arg_nodes(node)
85
+ argument_declarations(node).select do |arg_declaration|
86
+ # argument is declared on the same class that is being analyzed
87
+ arg_declaration.each_ancestor(:class).first == node
88
+ end
89
+ end
90
+
91
+ def find_resolve_method_node(node)
92
+ resolve_method_nodes = resolve_method_definition(node)
93
+ resolve_method_nodes.to_a.last if resolve_method_nodes.any?
94
+ end
95
+
96
+ def find_unresolved_args(method_node, declared_arg_nodes)
97
+ resolve_method_kwargs = method_node.arguments.select do |arg|
98
+ arg.kwarg_type? || arg.kwoptarg_type?
99
+ end
100
+ resolve_method_kwargs_names = resolve_method_kwargs.map do |node|
101
+ node.node_parts[0]
102
+ end.to_set
103
+ declared_args = declared_arg_nodes.map { |node| RuboCop::GraphQL::Argument.new(node) }
104
+ declared_args.map(&method(:arg_name)).uniq.reject do |declared_arg_name|
105
+ resolve_method_kwargs_names.include?(declared_arg_name)
106
+ end
107
+ end
108
+
109
+ def register_offense(node, unresolved_args)
110
+ unresolved_args_source = unresolved_args.map { |v| "#{v}:" }.join(", ")
111
+
112
+ message = format(
113
+ self.class::MSG,
114
+ unresolved_args: unresolved_args_source,
115
+ ending: unresolved_args.size == 1 ? "" : "s"
116
+ )
117
+
118
+ add_offense(node, message: message) do |corrector|
119
+ if node.arguments?
120
+ corrector.insert_after(arg_end(node.arguments.last), ", #{unresolved_args_source}")
121
+ else
122
+ corrector.insert_after(method_name(node), "(#{unresolved_args_source})")
123
+ end
124
+ end
125
+ end
126
+
127
+ def method_name(node)
128
+ node.loc.keyword.end.resize(node.method_name.to_s.size + 1)
129
+ end
130
+
131
+ def arg_end(node)
132
+ node.loc.expression.end
133
+ end
134
+
135
+ def inferred_arg_name(name_as_string)
136
+ case name_as_string
137
+ when /_id$/
138
+ name_as_string.sub(/_id$/, "").to_sym
139
+ when /_ids$/
140
+ name_as_string.sub(/_ids$/, "")
141
+ .sub(/([^s])$/, "\\1s")
142
+ .to_sym
143
+ else
144
+ name
145
+ end
146
+ end
147
+
148
+ def arg_name(declared_arg)
149
+ return declared_arg.as if declared_arg.kwargs.as
150
+ return inferred_arg_name(declared_arg.name.to_s) if declared_arg.kwargs.loads
151
+
152
+ declared_arg.name
153
+ end
154
+
155
+ def_node_search :argument_declarations, <<~PATTERN
156
+ (send nil? :argument (:sym _) ...)
157
+ PATTERN
158
+
159
+ def_node_search :resolve_method_definition, <<~PATTERN
160
+ (def :resolve
161
+ (args ...) ...)
162
+ PATTERN
163
+ end
164
+ end
165
+ end
166
+ 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,12 @@
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
+ end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.11.3".freeze
3
+ VERSION = "0.12.3".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.3
4
+ version: 0.12.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Tsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2022-01-08 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,7 +124,7 @@ 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
127
+ changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/blob/master/CHANGELOG.md
125
128
  post_install_message:
126
129
  rdoc_options: []
127
130
  require_paths: