rubocop-graphql 0.19.0 → 1.0.1

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: 80b3609f1de18b1e5db7198fdf29f7deed28cfe0e505c9485203dd38172ff4f9
4
- data.tar.gz: 0d894dad1bc37192b76372d235d10fab4181e171574f471091e9bad0033212d2
3
+ metadata.gz: a7531302386361a75baf74bcb65e70041bf5b414fb55151c1bcb6eb9e40bbb92
4
+ data.tar.gz: 43943f69aacfebc7ea8060af236796c499b671ac8825853a77961105bc57afe5
5
5
  SHA512:
6
- metadata.gz: 3c2b2d1bcbae2e53e28567c0f13397f77427cc0b9d87a96171cd27fec5d33649568366a623c6ea7c6960f452709d02081c04e3dae1368b465da4fad304ae9ce5
7
- data.tar.gz: b6b5588432b38eaa545bff20b2f52d966859ba1fac6fcad5004f22405fd4d1dab56c04238124f99f3ff1df860b385773e539981221b94d0e8c4c27266d4a7c9e
6
+ metadata.gz: e67d5bba4243ca1e4122af69449871465e9e8124574d0a379e2125f4e0b13ce05a6d48f0c44f129ad8fbcc3fb4af692bb5e366ce4fa9d1b8c125d276d1f17e53
7
+ data.tar.gz: c6020f17e1828c0fe135e7ca398c36b8ed322ff9daf86d3616ade152b8391e213565a6b1a5c1b185a436c0f2b9d68c805135c39973f18e0fbb0e58d4ccab3895
data/config/default.yml CHANGED
@@ -8,95 +8,129 @@ GraphQL:
8
8
 
9
9
  GraphQL/ArgumentDescription:
10
10
  Enabled: true
11
- VersionAdded: '0.80'
11
+ VersionAdded: '0.2.0'
12
12
  Description: 'Ensures all arguments have a description'
13
13
 
14
14
  GraphQL/ArgumentName:
15
15
  Enabled: true
16
- VersionAdded: '0.80'
16
+ VersionAdded: '0.2.0'
17
17
  Description: 'This cop checks whether argument names are snake_case'
18
18
 
19
19
  GraphQL/ArgumentUniqueness:
20
20
  Enabled: true
21
- VersionAdded: '0.80'
21
+ VersionAdded: '0.11.0'
22
22
  Description: 'This cop enforces arguments to be defined once per block'
23
23
 
24
- GraphQL/ResolverMethodLength:
24
+ GraphQL/ExtractInputType:
25
25
  Enabled: true
26
- VersionAdded: '0.80'
27
- Description: 'Checks resolver methods are not too long'
28
- Max: 10
29
- CountComments: false
30
- ExcludedMethods: []
26
+ VersionAdded: '0.2.0'
27
+ Description: 'Suggests using input type instead of many arguments'
28
+ MaxArguments: 2
29
+ Include:
30
+ - '**/graphql/mutations/**/*.rb'
31
+
32
+ GraphQL/ExtractType:
33
+ Enabled: true
34
+ VersionAdded: '0.2.0'
35
+ Description: 'Suggests extracting fields with common prefixes to the separate type'
36
+ MaxFields: 2
37
+ Prefixes:
38
+ - is
39
+ - has
40
+ - with
41
+ - avg
42
+ - min
43
+ - max
31
44
 
32
45
  GraphQL/FieldDefinitions:
33
46
  Enabled: true
34
- VersionAdded: '0.80'
47
+ VersionAdded: '0.1.0'
35
48
  Description: 'Checks consistency of field definitions'
36
49
  EnforcedStyle: group_definitions
37
50
  SupportedStyles:
38
51
  - group_definitions
39
52
  - define_resolver_after_definition
40
53
 
41
- GraphQL/MultipleFieldDefinitions:
42
- Enabled: true
43
- Description: 'Ensures that fields with multiple definitions are grouped together'
44
-
45
54
  GraphQL/FieldDescription:
46
55
  Enabled: true
47
- VersionAdded: '0.80'
56
+ VersionAdded: '0.1.0'
48
57
  Description: 'Ensures all fields have a description'
49
58
 
50
59
  GraphQL/FieldHashKey:
51
60
  Enabled: true
52
- VersionAdded: '0.80'
61
+ VersionAdded: '0.4.0'
53
62
  Description: 'Checks :hash_key option is used for appropriate fields'
54
63
 
55
64
  GraphQL/FieldMethod:
56
65
  Enabled: true
57
- VersionAdded: '0.80'
66
+ VersionAdded: '0.2.0'
58
67
  Description: 'Checks :method option is used for appropriate fields'
59
68
 
60
69
  GraphQL/FieldName:
61
70
  Enabled: true
62
- VersionAdded: '0.80'
71
+ VersionAdded: '0.2.0'
63
72
  Description: 'This cop checks whether field names are snake_case'
64
73
  SafeAutoCorrect: false
65
74
 
66
75
  GraphQL/FieldUniqueness:
67
76
  Enabled: true
68
- VersionAdded: '0.80'
77
+ VersionAdded: '0.11.0'
69
78
  Description: 'This cop enforces fields to be defined once'
70
79
 
71
- GraphQL/ExtractInputType:
80
+ GraphQL/GraphqlName:
72
81
  Enabled: true
73
- VersionAdded: '0.80'
74
- Description: 'Suggests using input type instead of many arguments'
75
- MaxArguments: 2
82
+ VersionAdded: '1.0.0'
83
+ Description: 'This cop check proper configuration of graphql_name'
76
84
  Include:
77
- - '**/graphql/mutations/**/*.rb'
78
-
79
- GraphQL/ExtractType:
80
- Enabled: true
81
- VersionAdded: '0.80'
82
- Description: 'Suggests extracting fields with common prefixes to the separate type'
83
- MaxFields: 2
84
- Prefixes:
85
- - is
86
- - has
87
- - with
88
- - avg
89
- - min
90
- - max
85
+ - "**/graphql/types/**/*"
86
+ - "**/graphql/mutations/**/*"
87
+ EnforcedStyle: only_override
88
+ SupportedStyles:
89
+ - required
90
+ - only_override
91
91
 
92
92
  GraphQL/LegacyDsl:
93
93
  Enabled: true
94
- VersionAdded: '0.80'
94
+ VersionAdded: '0.9.0'
95
95
  Description: 'Checks that types are defined with class-based API'
96
96
 
97
+ GraphQL/MaxComplexitySchema:
98
+ Enabled: true
99
+ VersionAdded: '1.0.0'
100
+ Description: 'Enforces max_complexity configuration in schema'
101
+ Include:
102
+ - '**/graphql/**/*_schema.rb'
103
+
104
+ GraphQL/MaxDepthSchema:
105
+ Enabled: true
106
+ VersionAdded: '1.0.0'
107
+ Description: 'Enforces max_depth configuration in schema'
108
+ Include:
109
+ - '**/graphql/**/*_schema.rb'
110
+
111
+ GraphQL/MultipleFieldDefinitions:
112
+ Enabled: true
113
+ VersionAdded: '0.15.0'
114
+ Description: 'Ensures that fields with multiple definitions are grouped together'
115
+
116
+ GraphQL/NotAuthorizedNodeType:
117
+ Enabled: true
118
+ VersionAdded: '1.0.0'
119
+ Description: 'Detects types that implement Node interface and not have `.authorized?` check'
120
+ Include:
121
+ - '**/graphql/types/**/*'
122
+
123
+ GraphQL/ResolverMethodLength:
124
+ Enabled: true
125
+ VersionAdded: '0.1.0'
126
+ Description: 'Checks resolver methods are not too long'
127
+ Max: 10
128
+ CountComments: false
129
+ ExcludedMethods: []
130
+
97
131
  GraphQL/ObjectDescription:
98
132
  Enabled: true
99
- VersionAdded: '0.80'
133
+ VersionAdded: '0.3.0'
100
134
  Description: 'Ensures all types have a description'
101
135
  Exclude:
102
136
  - '**/*_schema.rb'
@@ -105,26 +139,31 @@ GraphQL/ObjectDescription:
105
139
 
106
140
  GraphQL/OrderedArguments:
107
141
  Enabled: true
108
- VersionAdded: '0.80'
142
+ VersionAdded: '0.7.0'
109
143
  Description: 'Arguments should be alphabetically sorted within groups'
110
144
 
111
145
  GraphQL/OrderedFields:
112
146
  Enabled: true
113
- VersionAdded: '0.80'
147
+ VersionAdded: '0.5.0'
114
148
  Description: 'Fields should be alphabetically sorted within groups'
149
+ Groups: true
115
150
 
116
151
  GraphQL/UnusedArgument:
117
152
  Enabled: true
153
+ VersionAdded: '0.12.0'
118
154
  Description: 'Arguments should either be listed explicitly or **rest should be in the resolve signature'
119
155
 
120
156
  GraphQL/UnnecessaryFieldAlias:
121
157
  Enabled: true
158
+ VersionAdded: '0.18.0'
122
159
  Description: 'Field aliases should be different than their field names'
123
160
 
124
161
  GraphQL/UnnecessaryArgumentCamelize:
125
162
  Enabled: true
163
+ VersionAdded: '0.18.0'
126
164
  Description: "Camelize isn't necessary if the argument name doesn't contain underscores"
127
165
 
128
166
  GraphQL/UnnecessaryFieldCamelize:
129
167
  Enabled: true
168
+ VersionAdded: '0.18.0'
130
169
  Description: "Camelize isn't necessary if the field name doesn't contain underscores"
@@ -80,6 +80,16 @@ module RuboCop
80
80
  end
81
81
  end
82
82
 
83
+ def on_module(node)
84
+ return if style != :group_definitions
85
+
86
+ schema_member = RuboCop::GraphQL::SchemaMember.new(node)
87
+
88
+ if (body = schema_member.body)
89
+ check_grouped_field_declarations(body)
90
+ end
91
+ end
92
+
83
93
  private
84
94
 
85
95
  GROUP_DEFS_MSG = "Group all field definitions together."
@@ -100,7 +110,6 @@ module RuboCop
100
110
 
101
111
  def group_field_declarations(corrector, node)
102
112
  field = RuboCop::GraphQL::Field.new(node)
103
-
104
113
  first_field = field.schema_member.body.find do |node|
105
114
  field_definition?(node) || field_definition_with_body?(node)
106
115
  end
@@ -211,7 +220,7 @@ module RuboCop
211
220
 
212
221
  def remove_old_resolver(corrector, resolver_definition)
213
222
  range_to_remove = range_with_surrounding_space(
214
- range: resolver_definition.loc.expression, side: :left
223
+ range: resolver_definition.source_range, side: :left
215
224
  )
216
225
  corrector.remove(range_to_remove)
217
226
 
@@ -220,7 +229,7 @@ module RuboCop
220
229
  return unless resolver_signature
221
230
 
222
231
  range_to_remove = range_with_surrounding_space(
223
- range: resolver_signature.loc.expression, side: :left
232
+ range: resolver_signature.source_range, side: :left
224
233
  )
225
234
  corrector.remove(range_to_remove)
226
235
  end
@@ -74,11 +74,11 @@ module RuboCop
74
74
  suggested_hash_key_name = hash_key_to_use(method_definition)
75
75
 
76
76
  corrector.insert_after(
77
- node.loc.expression, ", hash_key: #{suggested_hash_key_name.inspect}"
77
+ node.source_range, ", hash_key: #{suggested_hash_key_name.inspect}"
78
78
  )
79
79
 
80
80
  range = range_with_surrounding_space(
81
- range: method_definition.loc.expression, side: :left
81
+ range: method_definition.source_range, side: :left
82
82
  )
83
83
 
84
84
  corrector.remove(range)
@@ -68,10 +68,10 @@ module RuboCop
68
68
  method_definition = suggest_method_name_for(field)
69
69
  suggested_method_name = method_to_use(method_definition)
70
70
 
71
- corrector.insert_after(node.loc.expression, ", method: :#{suggested_method_name}")
71
+ corrector.insert_after(node.source_range, ", method: :#{suggested_method_name}")
72
72
 
73
73
  range = range_with_surrounding_space(
74
- range: method_definition.loc.expression, side: :left
74
+ range: method_definition.source_range, side: :left
75
75
  )
76
76
  corrector.remove(range)
77
77
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # Checks consistency of graphql_name usage
7
+ #
8
+ # EnforcedStyle supports two modes:
9
+ #
10
+ # `only_override` : types and mutations should have graphql_name configured only if it's
11
+ # different from the default name
12
+ #
13
+ # `required` : all types and mutations should have graphql_name configured
14
+ #
15
+ # @example EnforcedStyle: only_override (default)
16
+ # # good
17
+ #
18
+ # class UserType < BaseType
19
+ # graphql_name 'Viewer'
20
+ # end
21
+ #
22
+ # # bad
23
+ #
24
+ # class UserType < BaseType
25
+ # graphql_name 'User'
26
+ # end
27
+ #
28
+ # @example EnforcedStyle: required
29
+ # # good
30
+ #
31
+ # class UserType < BaseType
32
+ # graphql_name 'User'
33
+ # end
34
+ #
35
+ # # bad
36
+ #
37
+ # class UserType < BaseType
38
+ # end
39
+ #
40
+ class GraphqlName < Base
41
+ include ConfigurableEnforcedStyle
42
+
43
+ # @!method graphql_name(node)
44
+ def_node_matcher :graphql_name, <<~PATTERN
45
+ `(send nil? :graphql_name (str $_))
46
+ PATTERN
47
+
48
+ # @!method class_name(node)
49
+ def_node_matcher :class_name, <<~PATTERN
50
+ (class (const _ $_) ...)
51
+ PATTERN
52
+
53
+ MISSING_NAME = "graphql_name should be configured."
54
+ UNNEEDED_OVERRIDE = "graphql_name should be specified only for overrides."
55
+
56
+ def on_class(node)
57
+ specified_name = graphql_name(node)
58
+
59
+ case style
60
+ when :required
61
+ add_offense(node, message: MISSING_NAME) if specified_name.nil?
62
+ when :only_override
63
+ default_graphql_name = class_name(node).to_s.sub(/Type\Z/, "")
64
+ add_offense(node, message: UNNEEDED_OVERRIDE) if specified_name == default_graphql_name
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # Detects missing max_complexity configuration in schema files
7
+ #
8
+ # @example
9
+ # # good
10
+ #
11
+ # class AppSchema < BaseSchema
12
+ # max_complexity 42
13
+ # end
14
+ #
15
+ class MaxComplexitySchema < Base
16
+ # @!method max_complexity(node)
17
+ def_node_matcher :max_complexity, <<~PATTERN
18
+ `(send nil? :max_complexity ...)
19
+ PATTERN
20
+
21
+ MSG = "max_complexity should be configured for schema."
22
+
23
+ def on_class(node)
24
+ return if max_complexity(node)
25
+
26
+ add_offense(node)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # Detects missing max_depth configuration in schema files
7
+ #
8
+ # @example
9
+ # # good
10
+ #
11
+ # class AppSchema < BaseSchema
12
+ # max_depth 42
13
+ # end
14
+ #
15
+ class MaxDepthSchema < Base
16
+ # @!method max_depth(node)
17
+ def_node_matcher :max_depth, <<~PATTERN
18
+ `(send nil? :max_depth ...)
19
+ PATTERN
20
+
21
+ MSG = "max_depth should be configured for schema."
22
+
23
+ def on_class(node)
24
+ return if max_depth(node)
25
+
26
+ add_offense(node)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # Detects types that implement Node interface and not have `.authorized?` check.
7
+ # Such types can be fetched by ID and therefore should have type level check to
8
+ # avoid accidental information exposure.
9
+ #
10
+ # @example
11
+ # # good
12
+ #
13
+ # class UserType < BaseType
14
+ # implements GraphQL::Types::Relay::Node
15
+ #
16
+ # field :uuid, ID, null: false
17
+ #
18
+ # def self.authorized?(object, context)
19
+ # super && object.owner == context[:viewer]
20
+ # end
21
+ # end
22
+ #
23
+ # # good
24
+ #
25
+ # class UserType < BaseType
26
+ # implements GraphQL::Types::Relay::Node
27
+ #
28
+ # field :uuid, ID, null: false
29
+ #
30
+ # class << self
31
+ # def authorized?(object, context)
32
+ # super && object.owner == context[:viewer]
33
+ # end
34
+ # end
35
+ # end
36
+ #
37
+ # # bad
38
+ #
39
+ # class UserType < BaseType
40
+ # implements GraphQL::Types::Relay::Node
41
+ #
42
+ # field :uuid, ID, null: false
43
+ # end
44
+ #
45
+ class NotAuthorizedNodeType < Base
46
+ MSG = ".authorized? should be defined for types implementing Node interface."
47
+
48
+ # @!method implements_node_type?(node)
49
+ def_node_matcher :implements_node_type?, <<~PATTERN
50
+ `(send nil? :implements
51
+ (const
52
+ (const
53
+ (const
54
+ (const nil? :GraphQL) :Types) :Relay) :Node))
55
+ PATTERN
56
+
57
+ # @!method has_authorized_method?(node)
58
+ def_node_matcher :has_authorized_method?, <<~PATTERN
59
+ {`(:defs (:self) :authorized? ...) | `(:sclass (:self) `(:def :authorized? ...))}
60
+ PATTERN
61
+
62
+ def on_class(node)
63
+ add_offense(node) if implements_node_type?(node) && !has_authorized_method?(node)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -39,9 +39,18 @@ module RuboCop
39
39
  "section. "\
40
40
  "Field `%<current>s` should appear before `%<previous>s`."
41
41
 
42
+ # @!method field_declarations(node)
43
+ def_node_search :field_declarations, <<~PATTERN
44
+ {
45
+ (send nil? :field (:sym _) ...)
46
+ (block
47
+ (send nil? :field (:sym _) ...) ...)
48
+ }
49
+ PATTERN
50
+
42
51
  def on_class(node)
43
52
  field_declarations(node).each_cons(2) do |previous, current|
44
- next unless consecutive_lines(previous, current)
53
+ next unless consecutive_fields(previous, current)
45
54
  next if field_name(current) >= field_name(previous)
46
55
 
47
56
  register_offense(previous, current)
@@ -50,6 +59,14 @@ module RuboCop
50
59
 
51
60
  private
52
61
 
62
+ def consecutive_fields(previous, current)
63
+ return true if cop_config["Groups"] == false
64
+
65
+ (previous.source_range.last_line == current.source_range.first_line - 1) ||
66
+ (previous.parent.block_type? &&
67
+ previous.parent.last_line == current.source_range.first_line - 1)
68
+ end
69
+
53
70
  def register_offense(previous, current)
54
71
  message = format(
55
72
  self.class::MSG,
@@ -69,19 +86,6 @@ module RuboCop
69
86
  node.first_argument.value.to_s
70
87
  end
71
88
  end
72
-
73
- def consecutive_lines(previous, current)
74
- previous.source_range.last_line == current.source_range.first_line - 1
75
- end
76
-
77
- # @!method field_declarations(node)
78
- def_node_search :field_declarations, <<~PATTERN
79
- {
80
- (send nil? :field (:sym _) ...)
81
- (block
82
- (send nil? :field (:sym _) ...) ...)
83
- }
84
- PATTERN
85
89
  end
86
90
  end
87
91
  end
@@ -144,7 +144,7 @@ module RuboCop
144
144
  end
145
145
 
146
146
  def arg_end(node)
147
- node.loc.expression.end
147
+ node.source_range.end
148
148
  end
149
149
 
150
150
  def inferred_arg_name(name_as_string)
@@ -11,8 +11,12 @@ require_relative "graphql/field_hash_key"
11
11
  require_relative "graphql/field_method"
12
12
  require_relative "graphql/field_name"
13
13
  require_relative "graphql/field_uniqueness"
14
+ require_relative "graphql/graphql_name"
14
15
  require_relative "graphql/legacy_dsl"
16
+ require_relative "graphql/max_complexity_schema"
17
+ require_relative "graphql/max_depth_schema"
15
18
  require_relative "graphql/multiple_field_definitions"
19
+ require_relative "graphql/not_authorized_node_type"
16
20
  require_relative "graphql/resolver_method_length"
17
21
  require_relative "graphql/object_description"
18
22
  require_relative "graphql/ordered_arguments"
@@ -84,7 +84,10 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def root_node?(node)
87
- node.parent.nil? || node.parent.module_type? || root_with_siblings?(node.parent)
87
+ node.parent.nil? ||
88
+ node.module_type? && node.children.none?(&:module_type?) ||
89
+ node.class_type? ||
90
+ root_with_siblings?(node.parent)
88
91
  end
89
92
 
90
93
  def root_with_siblings?(node)
@@ -13,7 +13,7 @@ module RuboCop
13
13
  heredoc?(kwarg.value)
14
14
  end&.value
15
15
 
16
- range = node.loc.expression
16
+ range = node.source_range
17
17
  range = range.join(last_heredoc.loc.heredoc_end) if last_heredoc
18
18
 
19
19
  range_by_whole_lines(range)
@@ -7,7 +7,7 @@ module RuboCop
7
7
 
8
8
  # @!method class_contents(node)
9
9
  def_node_matcher :class_contents, <<~PATTERN
10
- (class _ _ $_)
10
+ {(class _ _ $_) | (module _ $_)}
11
11
  PATTERN
12
12
 
13
13
  attr_reader :node
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.19.0".freeze
3
+ VERSION = "1.0.1".freeze
4
4
  end
5
5
  end
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.19.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Tsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-15 00:00:00.000000000 Z
11
+ date: 2023-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -95,8 +95,12 @@ files:
95
95
  - lib/rubocop/cop/graphql/field_method.rb
96
96
  - lib/rubocop/cop/graphql/field_name.rb
97
97
  - lib/rubocop/cop/graphql/field_uniqueness.rb
98
+ - lib/rubocop/cop/graphql/graphql_name.rb
98
99
  - lib/rubocop/cop/graphql/legacy_dsl.rb
100
+ - lib/rubocop/cop/graphql/max_complexity_schema.rb
101
+ - lib/rubocop/cop/graphql/max_depth_schema.rb
99
102
  - lib/rubocop/cop/graphql/multiple_field_definitions.rb
103
+ - lib/rubocop/cop/graphql/not_authorized_node_type.rb
100
104
  - lib/rubocop/cop/graphql/object_description.rb
101
105
  - lib/rubocop/cop/graphql/ordered_arguments.rb
102
106
  - lib/rubocop/cop/graphql/ordered_fields.rb