summaryrefslogtreecommitdiff
path: root/app/models/concerns
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/bulk_member_access_load.rb5
-rw-r--r--app/models/concerns/ci/has_deployment_name.rb2
-rw-r--r--app/models/concerns/ci/has_status.rb19
-rw-r--r--app/models/concerns/cross_database_modification.rb2
-rw-r--r--app/models/concerns/deployment_platform.rb2
-rw-r--r--app/models/concerns/integrations/loggable.rb37
-rw-r--r--app/models/concerns/integrations/reset_secret_fields.rb41
-rw-r--r--app/models/concerns/integrations/slack_mattermost_notifier.rb12
-rw-r--r--app/models/concerns/issuable.rb4
-rw-r--r--app/models/concerns/limitable.rb4
-rw-r--r--app/models/concerns/merge_request_reviewer_state.rb2
-rw-r--r--app/models/concerns/packages/destructible.rb2
-rw-r--r--app/models/concerns/pg_full_text_searchable.rb14
-rw-r--r--app/models/concerns/project_services_loggable.rb28
-rw-r--r--app/models/concerns/routable.rb4
-rw-r--r--app/models/concerns/sha256_attribute.rb45
-rw-r--r--app/models/concerns/sha_attribute.rb64
17 files changed, 174 insertions, 113 deletions
diff --git a/app/models/concerns/bulk_member_access_load.rb b/app/models/concerns/bulk_member_access_load.rb
index efc65e55e40..c3aa3019abb 100644
--- a/app/models/concerns/bulk_member_access_load.rb
+++ b/app/models/concerns/bulk_member_access_load.rb
@@ -12,6 +12,11 @@ module BulkMemberAccessLoad
end
end
+ def purge_resource_id_from_request_store(resource_klass, resource_id)
+ Gitlab::SafeRequestPurger.execute(resource_key: max_member_access_for_resource_key(resource_klass),
+ resource_ids: [resource_id])
+ end
+
def max_member_access_for_resource_key(klass)
"max_member_access_for_#{klass.name.underscore.pluralize}:#{self.class}:#{self.id}"
end
diff --git a/app/models/concerns/ci/has_deployment_name.rb b/app/models/concerns/ci/has_deployment_name.rb
index fe288134872..887653e846e 100644
--- a/app/models/concerns/ci/has_deployment_name.rb
+++ b/app/models/concerns/ci/has_deployment_name.rb
@@ -5,7 +5,7 @@ module Ci
extend ActiveSupport::Concern
def count_user_deployment?
- Feature.enabled?(:job_deployment_count) && deployment_name?
+ deployment_name?
end
def deployment_name?
diff --git a/app/models/concerns/ci/has_status.rb b/app/models/concerns/ci/has_status.rb
index 313c767e59f..cca66c3ec94 100644
--- a/app/models/concerns/ci/has_status.rb
+++ b/app/models/concerns/ci/has_status.rb
@@ -7,16 +7,15 @@ module Ci
DEFAULT_STATUS = 'created'
BLOCKED_STATUS = %w[manual scheduled].freeze
AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze
- # TODO: replace STARTED_STATUSES with data from BUILD_STARTED_RUNNING_STATUSES in https://gitlab.com/gitlab-org/gitlab/-/issues/273378
- # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82149#note_865508501
- BUILD_STARTED_RUNNING_STATUSES = %w[running success failed].freeze
- STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
+ STARTED_STATUSES = %w[running success failed].freeze
ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
+ STOPPED_STATUSES = COMPLETED_STATUSES + BLOCKED_STATUS
ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
EXCLUDE_IGNORED_STATUSES = %w[manual failed canceled].to_set.freeze
- CANCELABLE_STATUSES = %w[running waiting_for_resource preparing pending created scheduled].freeze
+ ALIVE_STATUSES = (ACTIVE_STATUSES + ['created']).freeze
+ CANCELABLE_STATUSES = (ALIVE_STATUSES + ['scheduled']).freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze
@@ -47,6 +46,10 @@ module Ci
def completed_statuses
COMPLETED_STATUSES.map(&:to_sym)
end
+
+ def stopped_statuses
+ STOPPED_STATUSES.map(&:to_sym)
+ end
end
included do
@@ -78,8 +81,8 @@ module Ci
scope :skipped, -> { with_status(:skipped) }
scope :manual, -> { with_status(:manual) }
scope :scheduled, -> { with_status(:scheduled) }
- scope :alive, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running) }
- scope :alive_or_scheduled, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running, :scheduled) }
+ scope :alive, -> { with_status(*ALIVE_STATUSES) }
+ scope :alive_or_scheduled, -> { with_status(*klass::CANCELABLE_STATUSES) }
scope :created_or_pending, -> { with_status(:created, :pending) }
scope :running_or_pending, -> { with_status(:running, :pending) }
scope :finished, -> { with_status(:success, :failed, :canceled) }
@@ -98,7 +101,7 @@ module Ci
end
def started?
- STARTED_STATUSES.include?(status) && started_at
+ STARTED_STATUSES.include?(status) && !!started_at
end
def active?
diff --git a/app/models/concerns/cross_database_modification.rb b/app/models/concerns/cross_database_modification.rb
index 85645e482f6..dea62f03f91 100644
--- a/app/models/concerns/cross_database_modification.rb
+++ b/app/models/concerns/cross_database_modification.rb
@@ -103,7 +103,7 @@ module CrossDatabaseModification
def track_gitlab_schema_in_current_transaction?
return false unless Feature::FlipperFeature.table_exists?
- Feature.enabled?(:track_gitlab_schema_in_current_transaction, default_enabled: :yaml)
+ Feature.enabled?(:track_gitlab_schema_in_current_transaction)
rescue ActiveRecord::NoDatabaseError, PG::ConnectionBad
false
end
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index d9c622f247a..2b5e1a204cb 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -3,7 +3,7 @@
module DeploymentPlatform
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def deployment_platform(environment: nil)
- return if Feature.disabled?(:certificate_based_clusters, default_enabled: :yaml, type: :ops)
+ return unless self.namespace.certificate_based_clusters_enabled?
@deployment_platform ||= {}
diff --git a/app/models/concerns/integrations/loggable.rb b/app/models/concerns/integrations/loggable.rb
new file mode 100644
index 00000000000..57847ea335c
--- /dev/null
+++ b/app/models/concerns/integrations/loggable.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Integrations
+ module Loggable
+ def log_info(message, params = {})
+ message = build_message(message, params)
+
+ logger.info(message)
+ end
+
+ def log_error(message, params = {})
+ message = build_message(message, params)
+
+ logger.error(message)
+ end
+
+ def log_exception(error, params = {})
+ Gitlab::ExceptionLogFormatter.format!(error, params)
+
+ log_error(params[:message] || error.message, params)
+ end
+
+ def build_message(message, params = {})
+ {
+ integration_class: self.class.name,
+ integration_id: id,
+ project_id: project&.id,
+ project_path: project&.full_path,
+ message: message
+ }.merge(params)
+ end
+
+ def logger
+ Gitlab::IntegrationsLogger
+ end
+ end
+end
diff --git a/app/models/concerns/integrations/reset_secret_fields.rb b/app/models/concerns/integrations/reset_secret_fields.rb
new file mode 100644
index 00000000000..f79c4392f19
--- /dev/null
+++ b/app/models/concerns/integrations/reset_secret_fields.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+# Integrations should reset their "secret" fields (type: 'password') when certain "exposing"
+# fields are changed (e.g. URLs), to avoid leaking secrets to unauthorized parties.
+# The result of this is that users have to reenter the secrets to confirm the change.
+module Integrations
+ module ResetSecretFields
+ extend ActiveSupport::Concern
+
+ included do
+ before_validation :reset_secret_fields!, if: :reset_secret_fields?
+ 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)
+ end
+
+ private
+
+ def reset_secret_fields?
+ exposing_secrets_fields.any? do |field|
+ public_send("#{field}_changed?") # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+
+ def reset_secret_fields!
+ secret_fields.each do |field|
+ next if public_send("#{field}_touched?") # rubocop:disable GitlabSecurity/PublicSend
+
+ public_send("#{field}=", nil) # rubocop:disable GitlabSecurity/PublicSend
+
+ # NOTE: Some of our specs also write to properties in addition to data fields,
+ # in order to test backwards compatibility. So in those cases we also need to
+ # clear the field in properties, since the setter above will only affect the data field.
+ self.properties = properties.except(field) if properties.present?
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/integrations/slack_mattermost_notifier.rb b/app/models/concerns/integrations/slack_mattermost_notifier.rb
index be13701289a..3bdaa852ddf 100644
--- a/app/models/concerns/integrations/slack_mattermost_notifier.rb
+++ b/app/models/concerns/integrations/slack_mattermost_notifier.rb
@@ -14,11 +14,21 @@ module Integrations
# - https://gitlab.com/gitlab-org/slack-notifier#middleware
# - https://gitlab.com/gitlab-org/gitlab/-/issues/347048
notifier = ::Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
- notifier.ping(
+ responses = notifier.ping(
message.pretext,
attachments: message.attachments,
fallback: message.fallback
)
+
+ responses.each do |response|
+ unless response.success?
+ log_error('SlackMattermostNotifier HTTP error response',
+ request_host: response.request.uri.host,
+ response_code: response.code,
+ response_body: response.body
+ )
+ end
+ end
end
class HTTPClient
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index dbd760a9c45..713a4386fee 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -195,7 +195,7 @@ module Issuable
end
def supports_escalation?
- return false unless ::Feature.enabled?(:incident_escalations, project, default_enabled: :yaml)
+ return false unless ::Feature.enabled?(:incident_escalations, project)
incident?
end
@@ -520,7 +520,7 @@ module Issuable
changes.merge!(hook_association_changes(old_associations))
end
- Gitlab::HookData::IssuableBuilder.new(self).build(user: user, changes: changes)
+ Gitlab::DataBuilder::Issuable.new(self).build(user: user, changes: changes)
end
def labels_array
diff --git a/app/models/concerns/limitable.rb b/app/models/concerns/limitable.rb
index fab1aa21634..6ff540b7866 100644
--- a/app/models/concerns/limitable.rb
+++ b/app/models/concerns/limitable.rb
@@ -28,8 +28,8 @@ module Limitable
def validate_scoped_plan_limit_not_exceeded
scope_relation = self.public_send(limit_scope) # rubocop:disable GitlabSecurity/PublicSend
return unless scope_relation
- return if limit_feature_flag && ::Feature.disabled?(limit_feature_flag, scope_relation, default_enabled: :yaml)
- return if limit_feature_flag_for_override && ::Feature.enabled?(limit_feature_flag_for_override, scope_relation, default_enabled: :yaml)
+ return if limit_feature_flag && ::Feature.disabled?(limit_feature_flag, scope_relation)
+ return if limit_feature_flag_for_override && ::Feature.enabled?(limit_feature_flag_for_override, scope_relation)
relation = limit_relation ? self.public_send(limit_relation) : self.class.where(limit_scope => scope_relation) # rubocop:disable GitlabSecurity/PublicSend
limits = scope_relation.actual_limits
diff --git a/app/models/concerns/merge_request_reviewer_state.rb b/app/models/concerns/merge_request_reviewer_state.rb
index 893d06b4da8..18ec1c253e1 100644
--- a/app/models/concerns/merge_request_reviewer_state.rb
+++ b/app/models/concerns/merge_request_reviewer_state.rb
@@ -16,8 +16,6 @@ module MergeRequestReviewerState
belongs_to :updated_state_by, class_name: 'User', foreign_key: :updated_state_by_user_id
- after_initialize :set_state, unless: :persisted?
-
def attention_requested_by
return unless attention_requested?
diff --git a/app/models/concerns/packages/destructible.rb b/app/models/concerns/packages/destructible.rb
index a3b7d8580c1..647c63b7f60 100644
--- a/app/models/concerns/packages/destructible.rb
+++ b/app/models/concerns/packages/destructible.rb
@@ -5,7 +5,7 @@ module Packages
extend ActiveSupport::Concern
class_methods do
- def next_pending_destruction(order_by: nil)
+ def next_pending_destruction(order_by:)
set = pending_destruction.limit(1).lock('FOR UPDATE SKIP LOCKED')
set = set.order(order_by) if order_by
set.take
diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb
index 68357c44300..bfc539ee392 100644
--- a/app/models/concerns/pg_full_text_searchable.rb
+++ b/app/models/concerns/pg_full_text_searchable.rb
@@ -80,6 +80,15 @@ module PgFullTextSearchable
pg_full_text_searchable_columns[column[:name]] = column[:weight]
end
+ # When multiple updates are done in a transaction, `saved_changes` will only report the latest save
+ # and we may miss an update to the searchable columns.
+ # As a workaround, we set a dirty flag here and update the search data in `after_save_commit`.
+ after_save do
+ next unless pg_full_text_searchable_columns.keys.any? { |f| saved_changes.has_key?(f) }
+
+ @update_pg_full_text_search_data = true
+ end
+
# We update this outside the transaction because this could raise an error if the resulting tsvector
# is too long. When that happens, we still persist the create / update but the model will not have a
# search data record. This is fine in most cases because this is a very rare occurrence and only happens
@@ -87,9 +96,8 @@ module PgFullTextSearchable
#
# We also do not want to use a subtransaction here due to: https://gitlab.com/groups/gitlab-org/-/epics/6540
after_save_commit do
- next unless pg_full_text_searchable_columns.keys.any? { |f| saved_changes.has_key?(f) }
-
- update_search_data!
+ update_search_data! if @update_pg_full_text_search_data
+ @update_pg_full_text_search_data = nil
end
end
diff --git a/app/models/concerns/project_services_loggable.rb b/app/models/concerns/project_services_loggable.rb
deleted file mode 100644
index e5385435138..00000000000
--- a/app/models/concerns/project_services_loggable.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-module ProjectServicesLoggable
- def log_info(message, params = {})
- message = build_message(message, params)
-
- logger.info(message)
- end
-
- def log_error(message, params = {})
- message = build_message(message, params)
-
- logger.error(message)
- end
-
- def build_message(message, params = {})
- {
- service_class: self.class.name,
- project_id: project&.id,
- project_path: project&.full_path,
- message: message
- }.merge(params)
- end
-
- def logger
- Gitlab::ProjectServiceLogger
- end
-end
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 2cf95ac0dae..5b759dedb26 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -97,7 +97,7 @@ module Routable
def full_name
# We have to test for persistence as the cache key uses #updated_at
- return (route&.name || build_full_name) unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops, default_enabled: :yaml)
+ return (route&.name || build_full_name) unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops)
# Return the name as-is if the parent is missing
return name if route.nil? && parent.nil? && name.present?
@@ -115,7 +115,7 @@ module Routable
def full_path
# We have to test for persistence as the cache key uses #updated_at
- return (route&.path || build_full_path) unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops, default_enabled: :yaml)
+ return (route&.path || build_full_path) unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops)
# Return the path as-is if the parent is missing
return path if route.nil? && parent.nil? && path.present?
diff --git a/app/models/concerns/sha256_attribute.rb b/app/models/concerns/sha256_attribute.rb
deleted file mode 100644
index 3c906642b1a..00000000000
--- a/app/models/concerns/sha256_attribute.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-module Sha256Attribute
- extend ActiveSupport::Concern
-
- class_methods do
- def sha256_attribute(name)
- return if ENV['STATIC_VERIFICATION']
-
- validate_binary_column_exists!(name) unless Rails.env.production?
-
- attribute(name, Gitlab::Database::Sha256Attribute.new)
- end
-
- # This only gets executed in non-production environments as an additional check to ensure
- # the column is the correct type. In production it should behave like any other attribute.
- # See https://gitlab.com/gitlab-org/gitlab/merge_requests/5502 for more discussion
- def validate_binary_column_exists!(name)
- return unless database_exists?
-
- unless table_exists?
- warn "WARNING: sha256_attribute #{name.inspect} is invalid since the table doesn't exist - you may need to run database migrations"
- return
- end
-
- column = columns.find { |c| c.name == name.to_s }
-
- unless column
- warn "WARNING: sha256_attribute #{name.inspect} is invalid since the column doesn't exist - you may need to run database migrations"
- return
- end
-
- unless column.type == :binary
- raise ArgumentError, "sha256_attribute #{name.inspect} is invalid since the column type is not :binary"
- end
- rescue StandardError => error
- Gitlab::AppLogger.error "Sha256Attribute initialization: #{error.message}"
- raise
- end
-
- def database_exists?
- database.exists?
- end
- end
-end
diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb
index e49f4d03bda..701d2fda5c5 100644
--- a/app/models/concerns/sha_attribute.rb
+++ b/app/models/concerns/sha_attribute.rb
@@ -3,39 +3,71 @@
module ShaAttribute
extend ActiveSupport::Concern
- # Needed for the database method
- include DatabaseReflection
+ class ShaAttributeTypeMismatchError < StandardError
+ def initialize(column_name, column_type)
+ @column_name = column_name
+ @column_type = column_type
+ end
+
+ def message
+ "sha_attribute :#{@column_name} should be a :binary column but it is :#{@column_type}"
+ end
+ end
+
+ class Sha256AttributeTypeMismatchError < ShaAttributeTypeMismatchError
+ def message
+ "sha256_attribute :#{@column_name} should be a :binary column but it is :#{@column_type}"
+ end
+ end
class_methods do
def sha_attribute(name)
return if ENV['STATIC_VERIFICATION']
- validate_binary_column_exists!(name) if Rails.env.development? || Rails.env.test?
+ sha_attribute_fields << name
attribute(name, Gitlab::Database::ShaAttribute.new)
end
+ def sha_attribute_fields
+ @sha_attribute_fields ||= []
+ end
+
+ def sha256_attribute(name)
+ return if ENV['STATIC_VERIFICATION']
+
+ sha256_attribute_fields << name
+
+ attribute(name, Gitlab::Database::Sha256Attribute.new)
+ end
+
+ def sha256_attribute_fields
+ @sha256_attribute_fields ||= []
+ end
+
# This only gets executed in non-production environments as an additional check to ensure
# the column is the correct type. In production it should behave like any other attribute.
# See https://gitlab.com/gitlab-org/gitlab/merge_requests/5502 for more discussion
- def validate_binary_column_exists!(name)
- return unless database_exists?
- return unless table_exists?
+ def load_schema!
+ super
- column = columns.find { |c| c.name == name.to_s }
+ return if Rails.env.production?
- return unless column
+ sha_attribute_fields.each do |field|
+ column = columns_hash[field.to_s]
- unless column.type == :binary
- raise ArgumentError, "sha_attribute #{name.inspect} is invalid since the column type is not :binary"
+ if column && column.type != :binary
+ raise ShaAttributeTypeMismatchError.new(column.name, column.type)
+ end
end
- rescue StandardError => error
- Gitlab::AppLogger.error "ShaAttribute initialization: #{error.message}"
- raise
- end
- def database_exists?
- database.exists?
+ sha256_attribute_fields.each do |field|
+ column = columns_hash[field.to_s]
+
+ if column && column.type != :binary
+ raise Sha256AttributeTypeMismatchError.new(column.name, column.type)
+ end
+ end
end
end
end