rubocop-graphql 0.11.3 → 0.12.3

Sign up to get free protection for your applications and to get access to all the features.
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: