rubocop-graphql 0.19.0 → 1.0.1

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: 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