Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/avatarable.rb6
-rw-r--r--app/models/concerns/chronic_duration_attribute.rb7
-rw-r--r--app/models/concerns/ci/deployable.rb6
-rw-r--r--app/models/concerns/ci/has_runner_executor.rb4
-rw-r--r--app/models/concerns/ci/maskable.rb4
-rw-r--r--app/models/concerns/ci/partitionable.rb5
-rw-r--r--app/models/concerns/clusters/agents/authorizations/ci_access/config_scopes.rb2
-rw-r--r--app/models/concerns/cross_database_ignored_tables.rb11
-rw-r--r--app/models/concerns/diff_positionable_note.rb2
-rw-r--r--app/models/concerns/each_batch.rb4
-rw-r--r--app/models/concerns/editable.rb2
-rw-r--r--app/models/concerns/enums/prometheus_metric.rb14
-rw-r--r--app/models/concerns/has_unique_internal_users.rb51
-rw-r--r--app/models/concerns/has_user_type.rb17
-rw-r--r--app/models/concerns/integrations/enable_ssl_verification.rb17
-rw-r--r--app/models/concerns/integrations/reset_secret_fields.rb4
-rw-r--r--app/models/concerns/integrations/slack_mattermost_fields.rb43
-rw-r--r--app/models/concerns/issuable.rb17
-rw-r--r--app/models/concerns/issue_available_features.rb8
-rw-r--r--app/models/concerns/linkable_item.rb1
-rw-r--r--app/models/concerns/mentionable/reference_regexes.rb2
-rw-r--r--app/models/concerns/noteable.rb27
-rw-r--r--app/models/concerns/packages/nuget/version_normalizable.rb2
-rw-r--r--app/models/concerns/pg_full_text_searchable.rb6
-rw-r--r--app/models/concerns/protected_ref.rb5
-rw-r--r--app/models/concerns/redactable.rb2
-rw-r--r--app/models/concerns/require_email_verification.rb5
-rw-r--r--app/models/concerns/resolvable_discussion.rb2
-rw-r--r--app/models/concerns/resolvable_note.rb2
-rw-r--r--app/models/concerns/restricted_signup.rb2
-rw-r--r--app/models/concerns/routable.rb71
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb2
-rw-r--r--app/models/concerns/taskable.rb6
-rw-r--r--app/models/concerns/transitionable.rb21
-rw-r--r--app/models/concerns/users/visitable.rb18
-rw-r--r--app/models/concerns/with_uploads.rb2
36 files changed, 254 insertions, 146 deletions
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index f419fa8518e..e342939b3d6 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -48,12 +48,6 @@ module Avatarable
end
end
- class_methods do
- def bot_avatar(image:)
- Rails.root.join('lib', 'assets', 'images', 'bot_avatars', image).open
- end
- end
-
def avatar_type
unless self.avatar.image?
errors.add :avatar, "file format is not supported. Please try one of the following supported formats: #{AvatarUploader::SAFE_IMAGE_EXT.join(', ')}"
diff --git a/app/models/concerns/chronic_duration_attribute.rb b/app/models/concerns/chronic_duration_attribute.rb
index af4905115b1..7b7b61fdf06 100644
--- a/app/models/concerns/chronic_duration_attribute.rb
+++ b/app/models/concerns/chronic_duration_attribute.rb
@@ -17,7 +17,12 @@ module ChronicDurationAttribute
chronic_duration_attributes[virtual_attribute] = value.presence || parameters[:default].presence.to_s
begin
- new_value = value.present? ? ChronicDuration.parse(value).to_i : parameters[:default].presence
+ new_value = if value.present?
+ ChronicDuration.parse(value, use_complete_matcher: true).to_i
+ else
+ parameters[:default].presence
+ end
+
assign_attributes(source_attribute => new_value)
rescue ChronicDuration::DurationParseError
# ignore error as it will be caught by validation
diff --git a/app/models/concerns/ci/deployable.rb b/app/models/concerns/ci/deployable.rb
index b3b80989410..d25151f9a34 100644
--- a/app/models/concerns/ci/deployable.rb
+++ b/app/models/concerns/ci/deployable.rb
@@ -138,7 +138,11 @@ module Ci
end
def environment_url
- options&.dig(:environment, :url) || persisted_environment&.external_url
+ options&.dig(:environment, :url) || persisted_environment.try(:external_url)
+ end
+
+ def environment_slug
+ persisted_environment.try(:slug)
end
def environment_status
diff --git a/app/models/concerns/ci/has_runner_executor.rb b/app/models/concerns/ci/has_runner_executor.rb
index dc70cdb2018..6d4622945fe 100644
--- a/app/models/concerns/ci/has_runner_executor.rb
+++ b/app/models/concerns/ci/has_runner_executor.rb
@@ -17,7 +17,9 @@ module Ci
virtualbox: 8,
docker_machine: 9,
docker_ssh_machine: 10,
- kubernetes: 11
+ kubernetes: 11,
+ docker_autoscaler: 12,
+ instance: 13
}, _suffix: true
end
end
diff --git a/app/models/concerns/ci/maskable.rb b/app/models/concerns/ci/maskable.rb
index e2cef0981d1..15240385dd8 100644
--- a/app/models/concerns/ci/maskable.rb
+++ b/app/models/concerns/ci/maskable.rb
@@ -11,12 +11,12 @@ module Ci
# * Minimal length of 8 characters
# * Characters must be from the Base64 alphabet (RFC4648) with the addition of '@', ':', '.', and '~'
# * Absolutely no fun is allowed
- REGEX = %r{\A[a-zA-Z0-9_+=/@:.~-]{8,}\z}.freeze
+ REGEX = %r{\A[a-zA-Z0-9_+=/@:.~-]{8,}\z}
# * Single line
# * No spaces
# * Minimal length of 8 characters
# * Some fun is allowed
- MASK_AND_RAW_REGEX = %r{\A\S{8,}\z}.freeze
+ MASK_AND_RAW_REGEX = %r{\A\S{8,}\z}
included do
validates :masked, inclusion: { in: [true, false] }
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
index ec6c85d888d..c4b1281fa72 100644
--- a/app/models/concerns/ci/partitionable.rb
+++ b/app/models/concerns/ci/partitionable.rb
@@ -107,7 +107,10 @@ module Ci
partitioned_by :partition_id,
strategy: :ci_sliding_list,
next_partition_if: proc { false },
- detach_partition_if: proc { false }
+ detach_partition_if: proc { false },
+ # Most of the db tasks are run in a weekly basis, e.g. execute_batched_migrations.
+ # Therefore, let's start with 1.week and see how it'd go.
+ analyze_interval: 1.week
end
end
end
diff --git a/app/models/concerns/clusters/agents/authorizations/ci_access/config_scopes.rb b/app/models/concerns/clusters/agents/authorizations/ci_access/config_scopes.rb
index eef68bfd349..9528a708ee1 100644
--- a/app/models/concerns/clusters/agents/authorizations/ci_access/config_scopes.rb
+++ b/app/models/concerns/clusters/agents/authorizations/ci_access/config_scopes.rb
@@ -17,7 +17,7 @@ module Clusters
class_methods do
def available_ci_access_fields(_project)
- %w(agent)
+ %w[agent]
end
end
end
diff --git a/app/models/concerns/cross_database_ignored_tables.rb b/app/models/concerns/cross_database_ignored_tables.rb
index c97e405cce4..14a9703a734 100644
--- a/app/models/concerns/cross_database_ignored_tables.rb
+++ b/app/models/concerns/cross_database_ignored_tables.rb
@@ -4,6 +4,12 @@ module CrossDatabaseIgnoredTables
extend ActiveSupport::Concern
class_methods do
+ def temporary_ignore_cross_database_tables(tables, url:, &blk)
+ Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
+ tables, url: url, &blk
+ )
+ end
+
def cross_database_ignore_tables(tables, options = {})
raise "missing issue url" if options[:url].blank?
@@ -40,8 +46,7 @@ module CrossDatabaseIgnoredTables
return yield unless options[:if].nil? || instance_eval(&options[:if])
url = options[:url]
- Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
- tables, url: url, &blk
- )
+
+ self.class.temporary_ignore_cross_database_tables(tables, url: url, &blk)
end
end
diff --git a/app/models/concerns/diff_positionable_note.rb b/app/models/concerns/diff_positionable_note.rb
index b10b318fb7c..2f64129b65f 100644
--- a/app/models/concerns/diff_positionable_note.rb
+++ b/app/models/concerns/diff_positionable_note.rb
@@ -14,7 +14,7 @@ module DiffPositionableNote
validates :position, json_schema: { filename: "position", hash_conversion: true }
end
- %i(original_position position change_position).each do |meth|
+ %i[original_position position change_position].each do |meth|
define_method "#{meth}=" do |new_position|
if new_position.is_a?(String)
new_position = begin
diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb
index 945d286a2fd..0c8cf861c38 100644
--- a/app/models/concerns/each_batch.rb
+++ b/app/models/concerns/each_batch.rb
@@ -54,7 +54,7 @@ module EachBatch
'the column: argument must be set to a column name to use for ordering rows'
end
- start = except(:select)
+ start = except(:select, :includes, :preload)
.select(column)
.reorder(column => order)
@@ -69,7 +69,7 @@ module EachBatch
1.step do |index|
start_cond = arel_table[column].gteq(start_id)
start_cond = arel_table[column].lteq(start_id) if order == :desc
- stop = except(:select)
+ stop = except(:select, :includes, :preload)
.select(column)
.where(start_cond)
.reorder(column => order)
diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb
index 2e49e720ac9..be9858bf49b 100644
--- a/app/models/concerns/editable.rb
+++ b/app/models/concerns/editable.rb
@@ -8,6 +8,6 @@ module Editable
end
def last_edited_by
- super || User.ghost
+ super || Users::Internal.ghost
end
end
diff --git a/app/models/concerns/enums/prometheus_metric.rb b/app/models/concerns/enums/prometheus_metric.rb
index e65a01990a3..2cc765b7a3c 100644
--- a/app/models/concerns/enums/prometheus_metric.rb
+++ b/app/models/concerns/enums/prometheus_metric.rb
@@ -30,37 +30,37 @@ module Enums
# built-in groups
nginx_ingress_vts: {
group_title: _('Response metrics (NGINX Ingress VTS)'),
- required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
+ required_metrics: %w[nginx_upstream_responses_total nginx_upstream_response_msecs_avg],
priority: 10
}.freeze,
nginx_ingress: {
group_title: _('Response metrics (NGINX Ingress)'),
- required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum),
+ required_metrics: %w[nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum],
priority: 10
}.freeze,
ha_proxy: {
group_title: _('Response metrics (HA Proxy)'),
- required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
+ required_metrics: %w[haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total],
priority: 10
}.freeze,
aws_elb: {
group_title: _('Response metrics (AWS ELB)'),
- required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
+ required_metrics: %w[aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum],
priority: 10
}.freeze,
nginx: {
group_title: _('Response metrics (NGINX)'),
- required_metrics: %w(nginx_server_requests nginx_server_requestMsec),
+ required_metrics: %w[nginx_server_requests nginx_server_requestMsec],
priority: 10
}.freeze,
kubernetes: {
group_title: _('System metrics (Kubernetes)'),
- required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
+ required_metrics: %w[container_memory_usage_bytes container_cpu_usage_seconds_total],
priority: 5
}.freeze,
cluster_health: {
group_title: _('Cluster Health'),
- required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
+ required_metrics: %w[container_memory_usage_bytes container_cpu_usage_seconds_total],
priority: 10
}.freeze
}.merge(custom_group_details).freeze
diff --git a/app/models/concerns/has_unique_internal_users.rb b/app/models/concerns/has_unique_internal_users.rb
deleted file mode 100644
index 25b56f6d70f..00000000000
--- a/app/models/concerns/has_unique_internal_users.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-
-module HasUniqueInternalUsers
- extend ActiveSupport::Concern
-
- class_methods do
- private
-
- def unique_internal(scope, username, email_pattern, &block)
- scope.first || create_unique_internal(scope, username, email_pattern, &block)
- end
-
- def create_unique_internal(scope, username, email_pattern, &creation_block)
- # Since we only want a single one of these in an instance, we use an
- # exclusive lease to ensure than this block is never run concurrently.
- lease_key = "user:unique_internal:#{username}"
- lease = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.minute.to_i)
-
- until uuid = lease.try_obtain
- # Keep trying until we obtain the lease. To prevent hammering Redis too
- # much we'll wait for a bit between retries.
- sleep(1)
- end
-
- # Recheck if the user is already present. One might have been
- # added between the time we last checked (first line of this method)
- # and the time we acquired the lock.
- existing_user = uncached { scope.first }
- return existing_user if existing_user.present?
-
- uniquify = Gitlab::Utils::Uniquify.new
-
- username = uniquify.string(username) { |s| User.find_by_username(s) }
-
- email = uniquify.string(-> (n) { Kernel.sprintf(email_pattern, n) }) do |s|
- User.find_by_email(s)
- end
-
- user = scope.build(
- username: username,
- email: email,
- &creation_block
- )
-
- Users::UpdateService.new(user, user: user).execute(validate: false)
- user
- ensure
- Gitlab::ExclusiveLease.cancel(lease_key, uuid)
- end
- end
-end
diff --git a/app/models/concerns/has_user_type.rb b/app/models/concerns/has_user_type.rb
index 2d0ff82e624..c3f702a4e69 100644
--- a/app/models/concerns/has_user_type.rb
+++ b/app/models/concerns/has_user_type.rb
@@ -74,4 +74,21 @@ module HasUserType
# https://gitlab.com/gitlab-org/gitlab/-/issues/346058
'****'
end
+
+ def resource_bot_resource
+ return unless project_bot?
+
+ projects&.first || groups&.first
+ end
+
+ def resource_bot_owners
+ return [] unless project_bot?
+
+ resource = resource_bot_resource
+ return [] unless resource
+
+ return resource.maintainers if resource.is_a?(Project)
+
+ resource.owners
+ end
end
diff --git a/app/models/concerns/integrations/enable_ssl_verification.rb b/app/models/concerns/integrations/enable_ssl_verification.rb
index 11dc8a76a2b..9735a9bf5f6 100644
--- a/app/models/concerns/integrations/enable_ssl_verification.rb
+++ b/app/models/concerns/integrations/enable_ssl_verification.rb
@@ -19,13 +19,16 @@ module Integrations
url_index = fields.index { |field| field[:name].ends_with?('_url') }
insert_index = url_index ? url_index + 1 : -1
- fields.insert(insert_index, {
- type: 'checkbox',
- name: 'enable_ssl_verification',
- title: s_('Integrations|SSL verification'),
- checkbox_label: s_('Integrations|Enable SSL verification'),
- help: s_('Integrations|Clear if using a self-signed certificate.')
- })
+ fields.insert(insert_index,
+ Field.new(
+ name: 'enable_ssl_verification',
+ integration_class: self,
+ type: :checkbox,
+ title: s_('Integrations|SSL verification'),
+ checkbox_label: s_('Integrations|Enable SSL verification'),
+ help: s_('Integrations|Clear if using a self-signed certificate.')
+ )
+ )
end
end
end
diff --git a/app/models/concerns/integrations/reset_secret_fields.rb b/app/models/concerns/integrations/reset_secret_fields.rb
index f79c4392f19..24d716fe5dd 100644
--- a/app/models/concerns/integrations/reset_secret_fields.rb
+++ b/app/models/concerns/integrations/reset_secret_fields.rb
@@ -12,9 +12,7 @@ module Integrations
end
def exposing_secrets_fields
- # TODO: Once all integrations use `Integrations::Field` we can remove the `.try` here.
- # See: https://gitlab.com/groups/gitlab-org/-/epics/7652
- fields.select { _1.try(:exposes_secrets) }.pluck(:name)
+ fields.select(&:exposes_secrets).pluck(:name)
end
private
diff --git a/app/models/concerns/integrations/slack_mattermost_fields.rb b/app/models/concerns/integrations/slack_mattermost_fields.rb
new file mode 100644
index 00000000000..a8e63c4e405
--- /dev/null
+++ b/app/models/concerns/integrations/slack_mattermost_fields.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Integrations
+ module SlackMattermostFields
+ extend ActiveSupport::Concern
+
+ included do
+ field :webhook,
+ help: -> { webhook_help },
+ required: true,
+ if: -> { requires_webhook? }
+
+ field :username,
+ placeholder: 'GitLab-integration',
+ if: -> { requires_webhook? }
+
+ field :notify_only_broken_pipelines,
+ type: :checkbox,
+ section: Integration::SECTION_TYPE_CONFIGURATION,
+ help: 'Do not send notifications for successful pipelines.'
+
+ field :branches_to_be_notified,
+ type: :select,
+ section: Integration::SECTION_TYPE_CONFIGURATION,
+ title: -> { s_('Integration|Branches for which notifications are to be sent') },
+ choices: -> { branch_choices }
+
+ field :labels_to_be_notified,
+ section: Integration::SECTION_TYPE_CONFIGURATION,
+ placeholder: '~backend,~frontend',
+ help: 'Send notifications for issue, merge request, and comment events with the listed labels only. ' \
+ 'Leave blank to receive notifications for all events.'
+
+ field :labels_to_be_notified_behavior,
+ type: :select,
+ section: Integration::SECTION_TYPE_CONFIGURATION,
+ choices: [
+ ['Match any of the labels', Integrations::BaseChatNotification::MATCH_ANY_LABEL],
+ ['Match all of the labels', Integrations::BaseChatNotification::MATCH_ALL_LABELS]
+ ]
+ end
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 9a513ea0e5b..a9a00ab1c44 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -19,6 +19,7 @@ module Issuable
include Awardable
include Taskable
include Importable
+ include Transitionable
include Editable
include AfterCommitQueue
include Sortable
@@ -33,7 +34,7 @@ module Issuable
TITLE_HTML_LENGTH_MAX = 800
DESCRIPTION_LENGTH_MAX = 1.megabyte
DESCRIPTION_HTML_LENGTH_MAX = 5.megabytes
- SEARCHABLE_FIELDS = %w(title description).freeze
+ SEARCHABLE_FIELDS = %w[title description].freeze
MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS = 200
STATE_ID_MAP = {
@@ -225,6 +226,10 @@ module Issuable
false
end
+ def supports_lock_on_merge?
+ false
+ end
+
def severity
return IssuableSeverity::DEFAULT unless supports_severity?
@@ -235,6 +240,10 @@ module Issuable
super + [:notes]
end
+ def importing_or_transitioning?
+ importing? || transitioning?
+ end
+
private
def validate_description_length?
@@ -408,14 +417,14 @@ module Issuable
sort = sort.to_s
grouping_columns = [arel_table[:id]]
- if %w(milestone_due_desc milestone_due_asc milestone).include?(sort)
+ if %w[milestone_due_desc milestone_due_asc milestone].include?(sort)
milestone_table = Milestone.arel_table
grouping_columns << milestone_table[:id]
grouping_columns << milestone_table[:due_date]
- elsif %w(merged_at_desc merged_at_asc merged_at).include?(sort)
+ elsif %w[merged_at_desc merged_at_asc merged_at].include?(sort)
grouping_columns << MergeRequest::Metrics.arel_table[:id]
grouping_columns << MergeRequest::Metrics.arel_table[:merged_at]
- elsif %w(closed_at_desc closed_at_asc closed_at).include?(sort)
+ elsif %w[closed_at_desc closed_at_asc closed_at].include?(sort)
grouping_columns << MergeRequest::Metrics.arel_table[:id]
grouping_columns << MergeRequest::Metrics.arel_table[:latest_closed_at]
end
diff --git a/app/models/concerns/issue_available_features.rb b/app/models/concerns/issue_available_features.rb
index 3f65e701da7..2969f1e1928 100644
--- a/app/models/concerns/issue_available_features.rb
+++ b/app/models/concerns/issue_available_features.rb
@@ -10,10 +10,10 @@ module IssueAvailableFeatures
# EE only features are listed on EE::IssueAvailableFeatures
def available_features_for_issue_types
{
- assignee: %w(issue incident),
- confidentiality: %w(issue incident),
- time_tracking: %w(issue incident),
- move_and_clone: %w(issue incident)
+ assignee: %w[issue incident],
+ confidentiality: %w[issue incident objective key_result],
+ time_tracking: %w[issue incident],
+ move_and_clone: %w[issue incident]
}.with_indifferent_access
end
end
diff --git a/app/models/concerns/linkable_item.rb b/app/models/concerns/linkable_item.rb
index 135252727ab..c91e3615ba7 100644
--- a/app/models/concerns/linkable_item.rb
+++ b/app/models/concerns/linkable_item.rb
@@ -16,6 +16,7 @@ module LinkableItem
scope :for_source, ->(item) { where(source_id: item.id) }
scope :for_target, ->(item) { where(target_id: item.id) }
+ scope :for_source_and_target, ->(source, target) { where(source: source, target: target) }
scope :for_items, ->(source, target) do
where(source: source, target: target).or(where(source: target, target: source))
end
diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb
index 0b6075fbeb8..b5634ba3b6d 100644
--- a/app/models/concerns/mentionable/reference_regexes.rb
+++ b/app/models/concerns/mentionable/reference_regexes.rb
@@ -28,7 +28,7 @@ module Mentionable
def self.external_pattern
strong_memoize(:external_pattern) do
issue_pattern = Integrations::BaseIssueTracker.base_reference_pattern
- link_patterns = URI::DEFAULT_PARSER.make_regexp(%w(http https))
+ link_patterns = URI::DEFAULT_PARSER.make_regexp(%w[http https])
reference_pattern(link_patterns, issue_pattern)
end
end
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index 40a91c8ac94..06cee46645b 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -12,17 +12,17 @@ module Noteable
class_methods do
# `Noteable` class names that support replying to individual notes.
def replyable_types
- %w(Issue MergeRequest)
+ %w[Issue MergeRequest]
end
# `Noteable` class names that support resolvable notes.
def resolvable_types
- %w(Issue MergeRequest DesignManagement::Design)
+ %w[Issue MergeRequest DesignManagement::Design]
end
# `Noteable` class names that support creating/forwarding individual notes.
def email_creatable_types
- %w(Issue)
+ %w[Issue]
end
end
@@ -164,28 +164,15 @@ module Noteable
[MergeRequest, Issue].include?(self.class)
end
- def etag_caching_enabled?
+ def real_time_notes_enabled?
false
end
- def expire_note_etag_cache
+ def broadcast_notes_changed
return unless discussions_rendered_on_frontend?
- return unless etag_caching_enabled?
+ return unless real_time_notes_enabled?
- # TODO: We need to figure out a way to make ETag caching work for group-level work items
- Gitlab::EtagCaching::Store.new.touch(note_etag_key) unless is_a?(Issue) && project.nil?
-
- Noteable::NotesChannel.broadcast_to(self, event: 'updated') if Feature.enabled?(:action_cable_notes, project || try(:group))
- end
-
- def note_etag_key
- return Gitlab::Routing.url_helpers.designs_project_issue_path(project, issue, { vueroute: filename }) if self.is_a?(DesignManagement::Design)
-
- Gitlab::Routing.url_helpers.project_noteable_notes_path(
- project,
- target_type: noteable_target_type_name,
- target_id: id
- )
+ Noteable::NotesChannel.broadcast_to(self, event: 'updated')
end
def after_note_created(_note)
diff --git a/app/models/concerns/packages/nuget/version_normalizable.rb b/app/models/concerns/packages/nuget/version_normalizable.rb
index 473e5f07811..4bcfec89570 100644
--- a/app/models/concerns/packages/nuget/version_normalizable.rb
+++ b/app/models/concerns/packages/nuget/version_normalizable.rb
@@ -13,7 +13,7 @@ module Packages
private
def set_normalized_version
- return unless package && Feature.enabled?(:nuget_normalized_version, package.project)
+ return unless package
self.normalized_version = normalize
end
diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb
index 562c8cf23f3..b7ca6f61573 100644
--- a/app/models/concerns/pg_full_text_searchable.rb
+++ b/app/models/concerns/pg_full_text_searchable.rb
@@ -21,11 +21,11 @@
module PgFullTextSearchable
extend ActiveSupport::Concern
- LONG_WORDS_REGEX = %r([A-Za-z0-9+/@]{50,}).freeze
+ LONG_WORDS_REGEX = %r([A-Za-z0-9+/@]{50,})
TSVECTOR_MAX_LENGTH = 1.megabyte.freeze
TEXT_SEARCH_DICTIONARY = 'english'
- URL_SCHEME_REGEX = %r{(?<=\A|\W)\w+://(?=\w+)}.freeze
- TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-_"]}.freeze
+ URL_SCHEME_REGEX = %r{(?<=\A|\W)\w+://(?=\w+)}
+ TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-_"]}
def update_search_data!
tsvector_sql_nodes = self.class.pg_full_text_searchable_columns.map do |column, weight|
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index a87eadb9332..ea8a1640bea 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -3,6 +3,8 @@
module ProtectedRef
extend ActiveSupport::Concern
+ include Importable
+
included do
belongs_to :project, touch: true
@@ -32,12 +34,13 @@ module ProtectedRef
# to fail.
has_many :"#{type}_access_levels", inverse_of: self.model_name.singular
+ # Overridden in EE with `if: -> { false }` so this validation does not apply on an EE instance.
validates :"#{type}_access_levels",
length: {
is: 1,
message: "are restricted to a single instance per #{self.model_name.human}."
},
- unless: -> { allow_multiple?(type) }
+ unless: -> { allow_multiple?(type) || importing? }
accepts_nested_attributes_for :"#{type}_access_levels", allow_destroy: true
end
diff --git a/app/models/concerns/redactable.rb b/app/models/concerns/redactable.rb
index 53ae300ee2d..5ad96d6cc46 100644
--- a/app/models/concerns/redactable.rb
+++ b/app/models/concerns/redactable.rb
@@ -10,7 +10,7 @@
module Redactable
extend ActiveSupport::Concern
- UNSUBSCRIBE_PATTERN = %r{/sent_notifications/\h{32}/unsubscribe}.freeze
+ UNSUBSCRIBE_PATTERN = %r{/sent_notifications/\h{32}/unsubscribe}
class_methods do
def redact_field(field)
diff --git a/app/models/concerns/require_email_verification.rb b/app/models/concerns/require_email_verification.rb
index d7182778b36..6581928f637 100644
--- a/app/models/concerns/require_email_verification.rb
+++ b/app/models/concerns/require_email_verification.rb
@@ -7,10 +7,7 @@ module RequireEmailVerification
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
- # This value is twice the amount we want it to be, because due to a bug in the devise-two-factor
- # gem every failed login attempt increments the value of failed_attempts by 2 instead of 1.
- # See: https://github.com/tinfoil/devise-two-factor/issues/127
- MAXIMUM_ATTEMPTS = 3 * 2
+ MAXIMUM_ATTEMPTS = 3
UNLOCK_IN = 24.hours
included do
diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb
index e967c78154d..5c2f0aa04ac 100644
--- a/app/models/concerns/resolvable_discussion.rb
+++ b/app/models/concerns/resolvable_discussion.rb
@@ -116,7 +116,7 @@ module ResolvableDiscussion
# Set the notes array to the updated notes
@notes = notes_relation.fresh.to_a # rubocop:disable Gitlab/ModuleWithInstanceVariables
- noteable.expire_note_etag_cache
+ noteable.broadcast_notes_changed
clear_memoized_values
end
diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb
index 7f9a7faa3f5..23abc5d5c22 100644
--- a/app/models/concerns/resolvable_note.rb
+++ b/app/models/concerns/resolvable_note.rb
@@ -4,7 +4,7 @@ module ResolvableNote
extend ActiveSupport::Concern
# Names of all subclasses of `Note` that can be resolvable.
- RESOLVABLE_TYPES = %w(DiffNote DiscussionNote).freeze
+ RESOLVABLE_TYPES = %w[DiffNote DiscussionNote].freeze
included do
belongs_to :resolved_by, class_name: "User"
diff --git a/app/models/concerns/restricted_signup.rb b/app/models/concerns/restricted_signup.rb
index cf97be21165..6af9ede5e8b 100644
--- a/app/models/concerns/restricted_signup.rb
+++ b/app/models/concerns/restricted_signup.rb
@@ -84,3 +84,5 @@ module RestrictedSignup
end
end
end
+
+::RestrictedSignup.prepend_mod
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index f2badfe48dd..ef14ff5fbe2 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -14,7 +14,17 @@ module Routable
# Routable.find_by_full_path('groupname/projectname') # -> Project
#
# Returns a single object, or nil.
- def self.find_by_full_path(path, follow_redirects: false, route_scope: Route, redirect_route_scope: RedirectRoute)
+
+ # rubocop:disable Metrics/CyclomaticComplexity
+ # rubocop:disable Metrics/PerceivedComplexity
+ def self.find_by_full_path(
+ path,
+ follow_redirects: false,
+ route_scope: Route,
+ redirect_route_scope: RedirectRoute,
+ optimize_routable: Routable.optimize_routable_enabled?
+ )
+
return unless path.present?
# Convert path to string to prevent DB error: function lower(integer) does not exist
@@ -25,20 +35,50 @@ module Routable
#
# We need to qualify the columns with the table name, to support both direct lookups on
# Route/RedirectRoute, and scoped lookups through the Routable classes.
- Gitlab::Database.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420046") do
+ if optimize_routable
+ path_condition = { path: path }
+
+ source_type_condition = if route_scope == Route
+ {}
+ else
+ { source_type: route_scope.klass.base_class }
+ end
+
route =
- route_scope.find_by(routes: { path: path }) ||
- route_scope.iwhere(Route.arel_table[:path] => path).take
+ Route.where(source_type_condition).find_by(path_condition) ||
+ Route.where(source_type_condition).iwhere(path_condition).take
if follow_redirects
- route ||= redirect_route_scope.iwhere(RedirectRoute.arel_table[:path] => path).take
+ route ||= RedirectRoute.where(source_type_condition).iwhere(path_condition).take
end
- next unless route
+ return unless route
+ return route.source if route_scope == Route
+
+ route_scope.find_by(id: route.source_id)
+ else
+ Gitlab::Database.allow_cross_joins_across_databases(url:
+ "https://gitlab.com/gitlab-org/gitlab/-/issues/420046") do
+ route =
+ route_scope.find_by(routes: { path: path }) ||
+ route_scope.iwhere(Route.arel_table[:path] => path).take
+
+ if follow_redirects
+ route ||= redirect_route_scope.iwhere(RedirectRoute.arel_table[:path] => path).take
+ end
- route.is_a?(Routable) ? route : route.source
+ next unless route
+
+ route.is_a?(Routable) ? route : route.source
+ end
end
end
+ # rubocop:enable Metrics/PerceivedComplexity
+ # rubocop:enable Metrics/CyclomaticComplexity
+
+ def self.optimize_routable_enabled?
+ Feature.enabled?(:optimize_routable)
+ end
included do
# Remove `inverse_of: source` when upgraded to rails 5.2
@@ -67,13 +107,22 @@ module Routable
#
# Returns a single object, or nil.
def find_by_full_path(path, follow_redirects: false)
- # TODO: Optimize these queries by avoiding joins
- # https://gitlab.com/gitlab-org/gitlab/-/issues/292252
+ optimize_routable = Routable.optimize_routable_enabled?
+
+ if optimize_routable
+ route_scope = all
+ redirect_route_scope = RedirectRoute
+ else
+ route_scope = includes(:route).references(:routes)
+ redirect_route_scope = joins(:redirect_routes)
+ end
+
Routable.find_by_full_path(
path,
follow_redirects: follow_redirects,
- route_scope: includes(:route).references(:routes),
- redirect_route_scope: joins(:redirect_routes)
+ route_scope: route_scope,
+ redirect_route_scope: redirect_route_scope,
+ optimize_routable: optimize_routable
)
end
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index b73ed937b5d..5455a2159cd 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -17,8 +17,6 @@ module Storage
Namespace.find(parent_id_before_last_save) # raise NotFound early if needed
end
- move_repositories
-
if saved_change_to_parent?
former_parent_full_path = parent_was&.full_path
parent_full_path = parent&.full_path
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index bf645e99b5e..96f684522d2 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -11,8 +11,8 @@ require 'task_list/filter'
module Taskable
COMPLETED = 'completed'
INCOMPLETE = 'incomplete'
- COMPLETE_PATTERN = /\[[xX]\]/.freeze
- INCOMPLETE_PATTERN = /\[[[:space:]]\]/.freeze
+ COMPLETE_PATTERN = /\[[xX]\]/
+ INCOMPLETE_PATTERN = /\[[[:space:]]\]/
ITEM_PATTERN = %r{
^
(?:(?:>\s{0,4})*) # optional blockquote characters
@@ -22,7 +22,7 @@ module Taskable
#{COMPLETE_PATTERN}|#{INCOMPLETE_PATTERN}
)
(\s.+) # followed by whitespace and some text.
- }x.freeze
+ }x
ITEM_PATTERN_UNTRUSTED =
'^' \
diff --git a/app/models/concerns/transitionable.rb b/app/models/concerns/transitionable.rb
new file mode 100644
index 00000000000..70e1fc8b78a
--- /dev/null
+++ b/app/models/concerns/transitionable.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Transitionable
+ extend ActiveSupport::Concern
+
+ attr_accessor :transitioning
+
+ def transitioning?
+ return false unless transitioning && Feature.enabled?(:skip_validations_during_transitions, project)
+
+ true
+ end
+
+ def enable_transitioning
+ self.transitioning = true
+ end
+
+ def disable_transitioning
+ self.transitioning = false
+ end
+end
diff --git a/app/models/concerns/users/visitable.rb b/app/models/concerns/users/visitable.rb
new file mode 100644
index 00000000000..cb8e5fdc682
--- /dev/null
+++ b/app/models/concerns/users/visitable.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Users
+ module Visitable
+ extend ActiveSupport::Concern
+
+ included do
+ def self.visited_around?(entity_id:, user_id:, time:)
+ visits_around(entity_id: entity_id, user_id: user_id, time: time).any?
+ end
+
+ def self.visits_around(entity_id:, user_id:, time:)
+ time = time.to_datetime
+ where(entity_id: entity_id, user_id: user_id, visited_at: (time - 15.minutes)..(time + 15.minutes))
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/with_uploads.rb b/app/models/concerns/with_uploads.rb
index caaf2b33ef0..319509ea69a 100644
--- a/app/models/concerns/with_uploads.rb
+++ b/app/models/concerns/with_uploads.rb
@@ -22,7 +22,7 @@ module WithUploads
# Currently there is no simple way how to select only not-mounted
# uploads, it should be all FileUploaders so we select them by
# `uploader` class
- FILE_UPLOADERS = %w(PersonalFileUploader NamespaceFileUploader FileUploader).freeze
+ FILE_UPLOADERS = %w[PersonalFileUploader NamespaceFileUploader FileUploader].freeze
included do
around_destroy :ignore_uploads_table_in_transaction