rubocop-graphql 0.18.0 → 1.0.0

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: fb4e28828d5aa07e959ad98fd9da04872c4832d1c8b1d168d440d1ad7c80762c
4
- data.tar.gz: eaf5932cba816bfb478fac0cec298bb0e3db98b506b4f7112851503712e77068
3
+ metadata.gz: aec3ad39e39612f0b537f18b691a38d2e2a5efc9ec5b284b0fabf0d12521136e
4
+ data.tar.gz: ba04fef687999fdb042a926c27346d89beff1fa59b827c4936b6f819cf5abe74
5
5
  SHA512:
6
- metadata.gz: cf88847a0b7a49cd1274b30fcff4e4e5ecb47b16f11a7d5e3fddcbcd7985a8d43bb8911080064a23e8d444daab330327f8908355a2ad75b2a94b14f2f4a4b2c8
7
- data.tar.gz: a10cf2b637bc06a65338af84928c53ced64c78e634470a7fe988e4bf7a7037cfcd3035895914b99056fa11abc272c328d9be40da6ee608f33c900490dea0504d
6
+ metadata.gz: f8094f6adb04e83515a0fd6307ddc3a255b17d7fdd8bf432ba84a77f6d4ba1921dbea3ba2739761b3103d8271d8bcab6b807c65f6479617eb8e116717e5861e0
7
+ data.tar.gz: f9664a1f5fb19d71dcef3976525f955f8c515c0655d4f86203bfa72508baeb9a000576ec3aafc44ee9b61061f0cfc79b978e1cada637335a9d98d0dd0249aea5
data/README.md CHANGED
@@ -1,13 +1,7 @@
1
- # RuboCop::GraphQL
1
+ # RuboCop::GraphQL ![](https://ruby-gem-downloads-badge.herokuapp.com/rubocop-graphql?type=total)
2
2
 
3
3
  [Rubocop](https://github.com/rubocop-hq/rubocop) extension for enforcing [graphql-ruby](https://github.com/rmosolgo/graphql-ruby) best practices.
4
4
 
5
- <p align="center">
6
- <a href="https://evilmartians.com/?utm_source=graphql-rubocop">
7
- <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
8
- </a>
9
- </p>
10
-
11
5
  ## Installation
12
6
 
13
7
  Install the gem:
@@ -69,6 +63,10 @@ GraphQL/ResolverMethodLength:
69
63
  Max: 3
70
64
  ```
71
65
 
66
+ ## Credits
67
+
68
+ Initially sponsored by [Evil Martians](http://evilmartians.com).
69
+
72
70
  ## Contributing
73
71
 
74
72
  Bug reports and pull requests are welcome on GitHub at https://github.com/DmitryTsepelev/rubocop-graphql.
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
@@ -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,54 @@
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
+ # # bad
24
+ #
25
+ # class UserType < BaseType
26
+ # implements GraphQL::Types::Relay::Node
27
+ #
28
+ # field :uuid, ID, null: false
29
+ # end
30
+ #
31
+ class NotAuthorizedNodeType < Base
32
+ MSG = ".authorized? should be defined for types implementing Node interface."
33
+
34
+ # @!method implements_node_type?(node)
35
+ def_node_matcher :implements_node_type?, <<~PATTERN
36
+ `(send nil? :implements
37
+ (const
38
+ (const
39
+ (const
40
+ (const nil? :GraphQL) :Types) :Relay) :Node))
41
+ PATTERN
42
+
43
+ # @!method has_authorized_method?(node)
44
+ def_node_matcher :has_authorized_method?, <<~PATTERN
45
+ `(:defs (:self) :authorized? ...)
46
+ PATTERN
47
+
48
+ def on_class(node)
49
+ add_offense(node) if implements_node_type?(node) && !has_authorized_method?(node)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ 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
@@ -23,14 +23,14 @@ module RuboCop
23
23
  # # bad
24
24
  #
25
25
  # class UserType < BaseType
26
- # argument :filter, String, required: false, camelize: false
26
+ # argument :filter, String, required: false
27
27
  # end
28
28
  #
29
29
  # # bad
30
30
  #
31
31
  # class UserType < BaseType
32
32
  # field :name, String, "Name of the user", null: true do
33
- # argument :filter, String, required: false, camelize: true
33
+ # argument :filter, String, required: false
34
34
  # end
35
35
  # end
36
36
  #
@@ -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,7 @@ 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? || node.class_type? || root_with_siblings?(node.parent)
88
88
  end
89
89
 
90
90
  def root_with_siblings?(node)
@@ -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
@@ -16,11 +16,20 @@ module RuboCop
16
16
  def declaration(node)
17
17
  buffer = processed_source.buffer
18
18
  begin_pos = range_by_whole_lines(node.source_range).begin_pos
19
- end_line = buffer.line_for_position(node.loc.expression.end_pos)
19
+ end_line = buffer.line_for_position(final_end_location(node).end_pos)
20
20
  end_pos = range_by_whole_lines(buffer.line_range(end_line),
21
21
  include_final_newline: true).end_pos
22
22
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
23
23
  end
24
+
25
+ def final_end_location(start_node)
26
+ heredoc_endings =
27
+ start_node.each_node(:str, :dstr, :xstr)
28
+ .select(&:heredoc?)
29
+ .map { |node| node.loc.heredoc_end }
30
+
31
+ [start_node.source_range.end, *heredoc_endings].max_by(&:line)
32
+ end
24
33
  end
25
34
  end
26
35
  end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.18.0".freeze
3
+ VERSION = "1.0.0".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.18.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Tsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-30 00:00:00.000000000 Z
11
+ date: 2023-02-26 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