diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /rubocop | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) | |
download | gitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'rubocop')
-rw-r--r-- | rubocop/cop/avoid_route_redirect_leading_slash.rb | 4 | ||||
-rw-r--r-- | rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb | 51 | ||||
-rw-r--r-- | rubocop/cop/gitlab/bulk_insert.rb | 2 | ||||
-rw-r--r-- | rubocop/cop/gitlab/except.rb | 23 | ||||
-rw-r--r-- | rubocop/cop/gitlab/intersect.rb | 23 | ||||
-rw-r--r-- | rubocop/cop/gitlab/rails_logger.rb | 35 | ||||
-rw-r--r-- | rubocop/cop/migration/complex_indexes_require_name.rb | 61 | ||||
-rw-r--r-- | rubocop/cop/migration/create_table_with_foreign_keys.rb | 99 | ||||
-rw-r--r-- | rubocop/cop/migration/refer_to_index_by_name.rb | 67 | ||||
-rw-r--r-- | rubocop/cop/migration/safer_boolean_column.rb | 2 | ||||
-rw-r--r-- | rubocop/cop/rspec/timecop_freeze.rb | 41 | ||||
-rw-r--r-- | rubocop/cop/rspec/top_level_describe_path.rb | 2 | ||||
-rw-r--r-- | rubocop/cop/static_translation_definition.rb | 18 | ||||
-rw-r--r-- | rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb | 20 | ||||
-rw-r--r-- | rubocop/migration_helpers.rb | 19 | ||||
-rw-r--r-- | rubocop/routes_under_scope.rb | 9 | ||||
-rw-r--r-- | rubocop/rubocop-migrations.yml | 1 | ||||
-rw-r--r-- | rubocop/rubocop-usage-data.yml | 18 |
18 files changed, 457 insertions, 38 deletions
diff --git a/rubocop/cop/avoid_route_redirect_leading_slash.rb b/rubocop/cop/avoid_route_redirect_leading_slash.rb index d66e434dc9c..0b0dc7d3d33 100644 --- a/rubocop/cop/avoid_route_redirect_leading_slash.rb +++ b/rubocop/cop/avoid_route_redirect_leading_slash.rb @@ -7,10 +7,10 @@ module RuboCop # # @example # # bad - # root to: redirect('/-/instance/statistics/dev_ops_score') + # root to: redirect('/-/autocomplete/users') # # # good - # root to: redirect('-/instance/statistics/dev_ops_score') + # root to: redirect('-/autocomplete/users') # class AvoidRouteRedirectLeadingSlash < RuboCop::Cop::Cop diff --git a/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb b/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb new file mode 100644 index 00000000000..599371aa5a1 --- /dev/null +++ b/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Gitlab + # This cop checks for `UploadedFile.from_params` usage. + # See https://docs.gitlab.com/ee/development/uploads.html#how-to-add-a-new-upload-route + # + # @example + # + # # bad + # class MyAwfulApi < Grape::API::Instance + # params do + # optional 'file.path', type: String + # optional 'file.name', type: String + # optional 'file.type', type: String + # optional 'file.size', type: Integer + # optional 'file.md5', type: String + # optional 'file.sha1', type: String + # optional 'file.sha256', type: String + # end + # put '/files' do + # uploaded_file = UploadedFile.from_params(params, :file, FileUploader.workhorse_local_upload_path) + # end + # end + # + # # good + # class MyMuchBetterApi < Grape::API::Instance + # params do + # requires :file, type: ::API::Validations::Types::WorkhorseFile + # end + # put '/files' do + # uploaded_file = declared_params[:file] + # end + # end + class AvoidUploadedFileFromParams < RuboCop::Cop::Cop + MSG = 'Use the `UploadedFile` set by `multipart.rb` instead of calling `UploadedFile.from_params` directly. See https://docs.gitlab.com/ee/development/uploads.html#how-to-add-a-new-upload-route' + + def_node_matcher :calling_uploaded_file_from_params?, <<~PATTERN + (send (const nil? :UploadedFile) :from_params ...) + PATTERN + + def on_send(node) + return unless calling_uploaded_file_from_params?(node) + + add_offense(node, location: :expression) + end + end + end + end +end diff --git a/rubocop/cop/gitlab/bulk_insert.rb b/rubocop/cop/gitlab/bulk_insert.rb index c03ffbe0b2a..83d879ddf44 100644 --- a/rubocop/cop/gitlab/bulk_insert.rb +++ b/rubocop/cop/gitlab/bulk_insert.rb @@ -9,7 +9,7 @@ module RuboCop MSG = 'Use the `BulkInsertSafe` concern, instead of using `Gitlab::Database.bulk_insert`. See https://docs.gitlab.com/ee/development/insert_into_tables_in_batches.html' def_node_matcher :raw_union?, <<~PATTERN - (send (const (const nil? :Gitlab) :Database) :bulk_insert ...) + (send (const (const _ :Gitlab) :Database) :bulk_insert ...) PATTERN def on_send(node) diff --git a/rubocop/cop/gitlab/except.rb b/rubocop/cop/gitlab/except.rb new file mode 100644 index 00000000000..24da6962457 --- /dev/null +++ b/rubocop/cop/gitlab/except.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Gitlab + # Cop that disallows the use of `Gitlab::SQL::Except`, in favour of using + # the `FromExcept` module. + class Except < RuboCop::Cop::Cop + MSG = 'Use the `FromExcept` concern, instead of using `Gitlab::SQL::Except` directly' + + def_node_matcher :raw_except?, <<~PATTERN + (send (const (const (const nil? :Gitlab) :SQL) :Except) :new ...) + PATTERN + + def on_send(node) + return unless raw_except?(node) + + add_offense(node, location: :expression) + end + end + end + end +end diff --git a/rubocop/cop/gitlab/intersect.rb b/rubocop/cop/gitlab/intersect.rb new file mode 100644 index 00000000000..4b61073b804 --- /dev/null +++ b/rubocop/cop/gitlab/intersect.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Gitlab + # Cop that disallows the use of `Gitlab::SQL::Intersect`, in favour of using + # the `FromIntersect` module. + class Intersect < RuboCop::Cop::Cop + MSG = 'Use the `FromIntersect` concern, instead of using `Gitlab::SQL::Intersect` directly' + + def_node_matcher :raw_intersect?, <<~PATTERN + (send (const (const (const nil? :Gitlab) :SQL) :Intersect) :new ...) + PATTERN + + def on_send(node) + return unless raw_intersect?(node) + + add_offense(node, location: :expression) + end + end + end + end +end diff --git a/rubocop/cop/gitlab/rails_logger.rb b/rubocop/cop/gitlab/rails_logger.rb index d1a06a9a100..ad35d2ccfbb 100644 --- a/rubocop/cop/gitlab/rails_logger.rb +++ b/rubocop/cop/gitlab/rails_logger.rb @@ -8,7 +8,7 @@ module RuboCop class RailsLogger < ::RuboCop::Cop::Cop include CodeReuseHelpers - # This cop checks for the Rails.logger in the codebase + # This cop checks for the Rails.logger log methods in the codebase # # @example # @@ -17,34 +17,29 @@ module RuboCop # # # good # Gitlab::AppLogger.error("Project %{project_path} could not be saved" % { project_path: project.full_path }) + # + # # OK + # Rails.logger.level MSG = 'Use a structured JSON logger instead of `Rails.logger`. ' \ 'https://docs.gitlab.com/ee/development/logging.html'.freeze - def_node_matcher :rails_logger?, <<~PATTERN - (send (const nil? :Rails) :logger ... ) - PATTERN + # See supported log methods: + # https://ruby-doc.org/stdlib-2.6.6/libdoc/logger/rdoc/Logger.html + LOG_METHODS = %i[debug error fatal info warn].freeze + LOG_METHODS_PATTERN = LOG_METHODS.map(&:inspect).join(' ').freeze - WHITELISTED_DIRECTORIES = %w[ - spec - ].freeze + def_node_matcher :rails_logger_log?, <<~PATTERN + (send + (send (const nil? :Rails) :logger) + {#{LOG_METHODS_PATTERN}} ... + ) + PATTERN def on_send(node) - return if in_whitelisted_directory?(node) - return unless rails_logger?(node) + return unless rails_logger_log?(node) add_offense(node, location: :expression) end - - def in_whitelisted_directory?(node) - path = file_path_for_node(node) - - WHITELISTED_DIRECTORIES.any? do |directory| - path.start_with?( - File.join(rails_root, directory), - File.join(rails_root, 'ee', directory) - ) - end - end end end end diff --git a/rubocop/cop/migration/complex_indexes_require_name.rb b/rubocop/cop/migration/complex_indexes_require_name.rb new file mode 100644 index 00000000000..82deb36716d --- /dev/null +++ b/rubocop/cop/migration/complex_indexes_require_name.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require_relative '../../migration_helpers' + +module RuboCop + module Cop + module Migration + class ComplexIndexesRequireName < RuboCop::Cop::Cop + include MigrationHelpers + + MSG = 'indexes added with custom options must be explicitly named' + + def_node_matcher :match_create_table_index_with_options, <<~PATTERN + (send _ {:index } _ (hash $...)) + PATTERN + + def_node_matcher :match_add_index_with_options, <<~PATTERN + (send _ {:add_index :add_concurrent_index} _ _ (hash $...)) + PATTERN + + def_node_matcher :name_option?, <<~PATTERN + (pair {(sym :name) (str "name")} _) + PATTERN + + def_node_matcher :unique_option?, <<~PATTERN + (pair {(:sym :unique) (str "unique")} _) + PATTERN + + def on_def(node) + return unless in_migration?(node) + + node.each_descendant(:send) do |send_node| + next unless create_table_with_index_offense?(send_node) || add_index_offense?(send_node) + + add_offense(send_node, location: :selector) + end + end + + private + + def create_table_with_index_offense?(send_node) + match_create_table_index_with_options(send_node) { |option_nodes| needs_name_option?(option_nodes) } + end + + def add_index_offense?(send_node) + match_add_index_with_options(send_node) { |option_nodes| needs_name_option?(option_nodes) } + end + + def needs_name_option?(option_nodes) + return false if only_unique_option?(option_nodes) + + option_nodes.none? { |node| name_option?(node) } + end + + def only_unique_option?(option_nodes) + option_nodes.size == 1 && unique_option?(option_nodes.first) + end + end + end + end +end diff --git a/rubocop/cop/migration/create_table_with_foreign_keys.rb b/rubocop/cop/migration/create_table_with_foreign_keys.rb new file mode 100644 index 00000000000..01cab032049 --- /dev/null +++ b/rubocop/cop/migration/create_table_with_foreign_keys.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require_relative '../../migration_helpers' + +module RuboCop + module Cop + module Migration + class CreateTableWithForeignKeys < RuboCop::Cop::Cop + include MigrationHelpers + + MSG = 'Creating a table with more than one foreign key at once violates our migration style guide. ' \ + 'For more details check the https://docs.gitlab.com/ce/development/migration_style_guide.html#examples' + + def_node_matcher :create_table_with_block?, <<~PATTERN + (block + (send nil? :create_table ...) + (args (arg _var)) + _) + PATTERN + + def_node_search :belongs_to_and_references, <<~PATTERN + (send _var {:references :belongs_to} $...) + PATTERN + + def_node_search :foreign_key_options, <<~PATTERN + (_pair + {(sym :foreign_key) (str "foreign_key")} + {(hash _) (true)} + ) + PATTERN + + def_node_search :to_table, <<~PATTERN + (_pair + {(sym :to_table) (str "to_table")} {(sym $...) (str $...)} + ) + PATTERN + + def_node_matcher :argument_name?, <<~PATTERN + {(sym $...) (str $...)} + PATTERN + + def_node_search :standalone_foreign_keys, <<~PATTERN + (send _var :foreign_key $...) + PATTERN + + def on_send(node) + return unless in_migration?(node) + return unless node.command?(:create_table) + return unless create_table_with_block?(node.parent) + + add_offense(node) if violates?(node.parent) + end + + private + + def violates?(node) + tables = all_target_tables(node).uniq + + tables.length > 1 && !(tables & high_traffic_tables).empty? + end + + def all_target_tables(node) + belongs_to_and_references_foreign_key_targets(node) + standalone_foreign_key_targets(node) + end + + def belongs_to_and_references_foreign_key_targets(node) + belongs_to_and_references(node).select { |candidate| has_fk_option?(candidate) } + .flat_map { |definition| definition_to_table_names(definition) } + .compact + end + + def standalone_foreign_key_targets(node) + standalone_foreign_keys(node).flat_map { |definition| definition_to_table_names(definition) } + .compact + end + + def has_fk_option?(candidate) + foreign_key_options(candidate.last).first + end + + def definition_to_table_names(definition) + table_name_from_options(definition.last) || arguments_to_table_names(definition) + end + + def table_name_from_options(options) + to_table(options).to_a.first&.first + end + + def arguments_to_table_names(arguments) + arguments.select { |argument| argument_name?(argument) } + .map(&:value) + .map(&:to_s) + .map(&:pluralize) + .map(&:to_sym) + end + end + end + end +end diff --git a/rubocop/cop/migration/refer_to_index_by_name.rb b/rubocop/cop/migration/refer_to_index_by_name.rb new file mode 100644 index 00000000000..fbf3c0d7030 --- /dev/null +++ b/rubocop/cop/migration/refer_to_index_by_name.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require_relative '../../migration_helpers' + +module RuboCop + module Cop + module Migration + class ReferToIndexByName < RuboCop::Cop::Cop + include MigrationHelpers + + MSG = 'migration methods that refer to existing indexes must do so by name' + + def_node_matcher :match_index_exists, <<~PATTERN + (send _ :index_exists? _ _ (hash $...) ?) + PATTERN + + def_node_matcher :match_remove_index, <<~PATTERN + (send _ :remove_index _ $_) + PATTERN + + def_node_matcher :match_remove_concurrent_index, <<~PATTERN + (send _ :remove_concurrent_index _ _ (hash $...) ?) + PATTERN + + def_node_matcher :name_option?, <<~PATTERN + (pair {(sym :name) (str "name")} _) + PATTERN + + def on_def(node) + return unless in_migration?(node) + + node.each_descendant(:send) do |send_node| + next unless index_exists_offense?(send_node) || removing_index_offense?(send_node) + + add_offense(send_node, location: :selector) + end + end + + private + + def index_exists_offense?(send_node) + match_index_exists(send_node) { |option_nodes| needs_name_option?(option_nodes) } + end + + def removing_index_offense?(send_node) + remove_index_offense?(send_node) || remove_concurrent_index_offense?(send_node) + end + + def remove_index_offense?(send_node) + match_remove_index(send_node) do |column_or_options_node| + break true unless column_or_options_node.type == :hash + + column_or_options_node.children.none? { |pair| name_option?(pair) } + end + end + + def remove_concurrent_index_offense?(send_node) + match_remove_concurrent_index(send_node) { |option_nodes| needs_name_option?(option_nodes) } + end + + def needs_name_option?(option_nodes) + option_nodes.empty? || option_nodes.first.none? { |node| name_option?(node) } + end + end + end + end +end diff --git a/rubocop/cop/migration/safer_boolean_column.rb b/rubocop/cop/migration/safer_boolean_column.rb index 25aaf42d00e..22d5d37a83d 100644 --- a/rubocop/cop/migration/safer_boolean_column.rb +++ b/rubocop/cop/migration/safer_boolean_column.rb @@ -37,7 +37,7 @@ module RuboCop table, _, type = matched.to_a.take(3).map(&:children).map(&:first) opts = matched[3] - return unless WHITELISTED_TABLES.include?(table) && type == :boolean + return unless SMALL_TABLES.include?(table) && type == :boolean no_default = no_default?(opts) nulls_allowed = nulls_allowed?(opts) diff --git a/rubocop/cop/rspec/timecop_freeze.rb b/rubocop/cop/rspec/timecop_freeze.rb new file mode 100644 index 00000000000..508b5df7c7f --- /dev/null +++ b/rubocop/cop/rspec/timecop_freeze.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # This cop checks for `Timecop.freeze` usage in specs. + # + # @example + # + # # bad + # Timecop.freeze(Time.current) { example.run } + # + # # good + # freeze_time(Time.current) { example.run } + # + class TimecopFreeze < RuboCop::Cop::Cop + include MatchRange + MESSAGE = 'Do not use `Timecop.freeze`, use `freeze_time` instead. ' \ + 'See https://gitlab.com/gitlab-org/gitlab/-/issues/214432 for more info.' + + def_node_matcher :timecop_freeze?, <<~PATTERN + (send (const nil? :Timecop) :freeze ?_) + PATTERN + + def on_send(node) + return unless timecop_freeze?(node) + + add_offense(node, location: :expression, message: MESSAGE) + end + + def autocorrect(node) + -> (corrector) do + each_match_range(node.source_range, /^(Timecop\.freeze)/) do |match_range| + corrector.replace(match_range, 'freeze_time') + end + end + end + end + end + end +end diff --git a/rubocop/cop/rspec/top_level_describe_path.rb b/rubocop/cop/rspec/top_level_describe_path.rb index 61796e23af0..3cc1ee8df90 100644 --- a/rubocop/cop/rspec/top_level_describe_path.rb +++ b/rubocop/cop/rspec/top_level_describe_path.rb @@ -21,7 +21,7 @@ module RuboCop private def acceptable_file_path?(path) - File.fnmatch?('*_spec.rb', path) || File.fnmatch?('*/frontend/fixtures/*', path) + File.fnmatch?('*_spec.rb', path) || File.fnmatch?('*/frontend/fixtures/*', path) || File.fnmatch?('*/docs_screenshots/*_docs.rb', path) end def shared_example?(node) diff --git a/rubocop/cop/static_translation_definition.rb b/rubocop/cop/static_translation_definition.rb index 736d8767342..55956d9665b 100644 --- a/rubocop/cop/static_translation_definition.rb +++ b/rubocop/cop/static_translation_definition.rb @@ -21,6 +21,8 @@ module RuboCop method_name = node.children[1] return unless TRANSLATION_METHODS.include?(method_name) + translation_memoized = false + node.each_ancestor do |ancestor| receiver, _ = *ancestor break if lambda_node?(receiver) # translations defined in lambda nodes should be allowed @@ -30,6 +32,14 @@ module RuboCop break end + + translation_memoized = true if memoization?(ancestor) + + if translation_memoized && class_method_definition?(ancestor) + add_offense(node, location: :expression) + + break + end end end @@ -38,6 +48,14 @@ module RuboCop def constant_assignment?(node) node.type == :casgn end + + def memoization?(node) + node.type == :or_asgn + end + + def class_method_definition?(node) + node.type == :defs + end end end end diff --git a/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb b/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb index c10ecbcb4ba..9fdf52dac8b 100644 --- a/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb +++ b/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb @@ -22,6 +22,7 @@ module RuboCop def on_send(node) distinct_count?(node) do |method_name, method_arguments| next unless method_arguments && method_arguments.length >= 2 + next if batch_set_to_false?(method_arguments[2]) next if allowed_foreign_key?(method_arguments[1]) add_offense(node, location: :selector, message: format(MSG, method_name)) @@ -31,11 +32,26 @@ module RuboCop private def allowed_foreign_key?(key) - key.type == :sym && allowed_foreign_keys.include?(key.value) + [:sym, :str].include?(key.type) && allowed_foreign_keys.include?(key.value.to_s) end def allowed_foreign_keys - cop_config['AllowedForeignKeys'] || [] + (cop_config['AllowedForeignKeys'] || []).map(&:to_s) + end + + def batch_set_to_false?(options) + return false unless options.is_a?(RuboCop::AST::HashNode) + + batch_set_to_false = false + options.each_pair do |key, value| + next unless value.boolean_type? && value.falsey_literal? + next unless key.type == :sym && key.value == :batch + + batch_set_to_false = true + break + end + + batch_set_to_false end end end diff --git a/rubocop/migration_helpers.rb b/rubocop/migration_helpers.rb index 355450bbf57..c26ba88f269 100644 --- a/rubocop/migration_helpers.rb +++ b/rubocop/migration_helpers.rb @@ -1,14 +1,13 @@ module RuboCop # Module containing helper methods for writing migration cops. module MigrationHelpers - WHITELISTED_TABLES = %i[ + # Tables with permanently small number of records + SMALL_TABLES = %i[ application_settings plan_limits ].freeze - # Blacklisted tables due to: - # - number of columns (> 50 on GitLab.com as of 03/2020) - # - number of records + # Tables with large number of columns (> 50 on GitLab.com as of 03/2020) WIDE_TABLES = %i[ users projects @@ -21,6 +20,10 @@ module RuboCop TABLE_METHODS = %i(create_table create_table_if_not_exists change_table).freeze + def high_traffic_tables + @high_traffic_tables ||= rubocop_migrations_config.dig('Migration/UpdateLargeTable', 'DeniedTables') + end + # Returns true if the given node originated from the db/migrate directory. def in_migration?(node) in_deployment_migration?(node) || in_post_deployment_migration?(node) @@ -53,5 +56,13 @@ module RuboCop def dirname(node) File.dirname(node.location.expression.source_buffer.name) end + + def rubocop_migrations_config + @rubocop_migrations_config ||= YAML.load_file(File.join(rubocop_path, 'rubocop-migrations.yml')) + end + + def rubocop_path + File.expand_path(__dir__) + end end end diff --git a/rubocop/routes_under_scope.rb b/rubocop/routes_under_scope.rb index 3f049bb09fa..7ffb0fd36f2 100644 --- a/rubocop/routes_under_scope.rb +++ b/rubocop/routes_under_scope.rb @@ -12,6 +12,7 @@ module RuboCop def on_send(node) return unless route_method?(node) return unless outside_scope?(node) + return if root_route?(node) add_offense(node) end @@ -25,5 +26,13 @@ module RuboCop def route_method?(node) ROUTE_METHODS.include?(node.method_name) end + + def root_route?(node) + first_argument = node.arguments.first + + if first_argument.respond_to?(:value) + first_argument.value == '/' + end + end end end diff --git a/rubocop/rubocop-migrations.yml b/rubocop/rubocop-migrations.yml index f8820c0c6aa..a919d570ccc 100644 --- a/rubocop/rubocop-migrations.yml +++ b/rubocop/rubocop-migrations.yml @@ -11,6 +11,7 @@ Migration/UpdateLargeTable: - :ci_stages - :deployments - :events + - :gitlab_subscriptions - :issues - :merge_request_diff_commits - :merge_request_diff_files diff --git a/rubocop/rubocop-usage-data.yml b/rubocop/rubocop-usage-data.yml index 46e8a067431..9f594bc5817 100644 --- a/rubocop/rubocop-usage-data.yml +++ b/rubocop/rubocop-usage-data.yml @@ -4,6 +4,7 @@ UsageData/LargeTable: - 'lib/gitlab/usage_data.rb' - 'ee/lib/ee/gitlab/usage_data.rb' NonRelatedClasses: + - :ActionMailer::Base - :Date - :Feature - :Gitlab @@ -37,10 +38,13 @@ UsageData/DistinctCountByLargeForeignKey: - 'lib/gitlab/usage_data.rb' - 'ee/lib/ee/gitlab/usage_data.rb' AllowedForeignKeys: - - :user_id - - :author_id - - :creator_id - - :owner_id - - :project_id - - :issue_id - - :merge_request_id + - 'agent_id' + - 'author_id' + - 'clusters.user_id' + - 'creator_id' + - 'issue_id' + - 'merge_request_id' + - 'merge_requests.target_project_id' + - 'owner_id' + - 'project_id' + - 'user_id' |