summaryrefslogtreecommitdiff
path: root/app/models/concerns
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/approvable.rb (renamed from app/models/concerns/approvable_base.rb)9
-rw-r--r--app/models/concerns/ci/artifactable.rb11
-rw-r--r--app/models/concerns/ci/has_deployment_name.rb15
-rw-r--r--app/models/concerns/ci/lockable.rb20
-rw-r--r--app/models/concerns/ci/metadatable.rb2
-rw-r--r--app/models/concerns/ci/partitionable.rb47
-rw-r--r--app/models/concerns/ci/track_environment_usage.rb31
-rw-r--r--app/models/concerns/counter_attribute.rb20
-rw-r--r--app/models/concerns/enums/ci/commit_status.rb6
-rw-r--r--app/models/concerns/enums/internal_id.rb3
-rw-r--r--app/models/concerns/from_set_operator.rb25
-rw-r--r--app/models/concerns/integrations/slack_mattermost_notifier.rb14
-rw-r--r--app/models/concerns/merge_request_reviewer_state.rb11
-rw-r--r--app/models/concerns/pg_full_text_searchable.rb1
-rw-r--r--app/models/concerns/project_features_compatibility.rb4
-rw-r--r--app/models/concerns/sortable.rb27
16 files changed, 193 insertions, 53 deletions
diff --git a/app/models/concerns/approvable_base.rb b/app/models/concerns/approvable.rb
index 8240f9bd6ea..1566c53217d 100644
--- a/app/models/concerns/approvable_base.rb
+++ b/app/models/concerns/approvable.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module ApprovableBase
+module Approvable
extend ActiveSupport::Concern
include FromUnion
@@ -27,12 +27,11 @@ module ApprovableBase
scope :not_approved_by_users_with_usernames, -> (usernames) do
users = User.where(username: usernames).select(:id)
- self_table = self.arel_table
app_table = Approval.arel_table
where(
Approval.where(approvals: { user_id: users })
- .where(app_table[:merge_request_id].eq(self_table[:id]))
+ .where(app_table[:merge_request_id].eq(arel_table[:id]))
.select('true')
.arel.exists.not
)
@@ -48,7 +47,7 @@ module ApprovableBase
def approved_by?(user)
return false unless user
- approved_by_users.include?(user)
+ approvals.where(user: user).any?
end
def can_be_approved_by?(user)
@@ -59,3 +58,5 @@ module ApprovableBase
user && approved_by?(user) && user.can?(:approve_merge_request, self)
end
end
+
+Approvable.prepend_mod
diff --git a/app/models/concerns/ci/artifactable.rb b/app/models/concerns/ci/artifactable.rb
index ee8e98ec1bf..3fdbd6a8789 100644
--- a/app/models/concerns/ci/artifactable.rb
+++ b/app/models/concerns/ci/artifactable.rb
@@ -10,8 +10,17 @@ module Ci
STORE_COLUMN = :file_store
NotSupportedAdapterError = Class.new(StandardError)
FILE_FORMAT_ADAPTERS = {
+ # While zip is a streamable file format, performing streaming
+ # reads requires that each entry in the zip has certain headers
+ # present at the front of the entry. These headers are OPTIONAL
+ # according to the file format specification. GitLab Runner uses
+ # Go's `archive/zip` to create zip archives, which does not include
+ # these headers. Go maintainers have expressed that they don't intend
+ # to support them: https://github.com/golang/go/issues/23301#issuecomment-363240781
+ #
+ # If you need GitLab to be able to read Artifactables, store them in
+ # raw or gzip format instead of zip.
gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream,
- zip: Gitlab::Ci::Build::Artifacts::Adapters::ZipStream,
raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream
}.freeze
diff --git a/app/models/concerns/ci/has_deployment_name.rb b/app/models/concerns/ci/has_deployment_name.rb
deleted file mode 100644
index 887653e846e..00000000000
--- a/app/models/concerns/ci/has_deployment_name.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- module HasDeploymentName
- extend ActiveSupport::Concern
-
- def count_user_deployment?
- deployment_name?
- end
-
- def deployment_name?
- self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) }
- end
- end
-end
diff --git a/app/models/concerns/ci/lockable.rb b/app/models/concerns/ci/lockable.rb
new file mode 100644
index 00000000000..31ba93775e2
--- /dev/null
+++ b/app/models/concerns/ci/lockable.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Ci
+ module Lockable
+ extend ActiveSupport::Concern
+
+ included do
+ # `locked` will be populated from the source of truth on Ci::Pipeline
+ # in order to clean up expired job artifacts in a performant way.
+ # The values should be the same as `Ci::Pipeline.lockeds` with the
+ # additional value of `unknown` to indicate rows that have not
+ # yet been populated from the parent Ci::Pipeline
+ enum locked: {
+ unlocked: 0,
+ artifacts_locked: 1,
+ unknown: 2
+ }, _prefix: :artifact
+ end
+ end
+end
diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb
index 8c3a05c23f0..71b26b70bbf 100644
--- a/app/models/concerns/ci/metadatable.rb
+++ b/app/models/concerns/ci/metadatable.rb
@@ -34,7 +34,7 @@ module Ci
end
def ensure_metadata
- metadata || build_metadata(project: project)
+ metadata || build_metadata(project: project, partition_id: partition_id)
end
def degenerated?
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
new file mode 100644
index 00000000000..710ee1ba64f
--- /dev/null
+++ b/app/models/concerns/ci/partitionable.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Ci
+ ##
+ # This module implements a way to set the `partion_id` value on a dependent
+ # resource from a parent record.
+ # Usage:
+ #
+ # class PipelineVariable < Ci::ApplicationRecord
+ # include Ci::Partitionable
+ #
+ # belongs_to :pipeline
+ # partitionable scope: :pipeline
+ # # Or
+ # partitionable scope: ->(record) { record.partition_value }
+ #
+ #
+ module Partitionable
+ extend ActiveSupport::Concern
+ include ::Gitlab::Utils::StrongMemoize
+
+ included do
+ before_validation :set_partition_id, on: :create
+ validates :partition_id, presence: true
+
+ def set_partition_id
+ return if partition_id_changed? && partition_id.present?
+ return unless partition_scope_value
+
+ self.partition_id = partition_scope_value
+ end
+ end
+
+ class_methods do
+ private
+
+ def partitionable(scope:)
+ define_method(:partition_scope_value) do
+ strong_memoize(:partition_scope_value) do
+ record = scope.to_proc.call(self)
+ record.respond_to?(:partition_id) ? record.partition_id : record
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/ci/track_environment_usage.rb b/app/models/concerns/ci/track_environment_usage.rb
new file mode 100644
index 00000000000..45d9cdeeb59
--- /dev/null
+++ b/app/models/concerns/ci/track_environment_usage.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Ci
+ module TrackEnvironmentUsage
+ extend ActiveSupport::Concern
+
+ def track_deployment_usage
+ return unless user_id.present? && count_user_deployment?
+
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id)
+ end
+
+ def track_verify_environment_usage
+ return unless user_id.present? && verifies_environment?
+
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id)
+ end
+
+ def verifies_environment?
+ has_environment? && environment_action == 'verify'
+ end
+
+ def count_user_deployment?
+ deployment_name?
+ end
+
+ def deployment_name?
+ self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) }
+ end
+ end
+end
diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb
index 65cf3246d11..64d178b7507 100644
--- a/app/models/concerns/counter_attribute.rb
+++ b/app/models/concerns/counter_attribute.rb
@@ -65,6 +65,10 @@ module CounterAttribute
def counter_attribute_after_flush(&callback)
after_flush_callbacks << callback
end
+
+ def counter_attribute_enabled?(attribute)
+ counter_attributes.include?(attribute)
+ end
end
# This method must only be called by FlushCounterIncrementsWorker
@@ -103,16 +107,14 @@ module CounterAttribute
end
def delayed_increment_counter(attribute, increment)
+ raise ArgumentError, "#{attribute} is not a counter attribute" unless counter_attribute_enabled?(attribute)
+
return if increment == 0
run_after_commit_or_now do
- if counter_attribute_enabled?(attribute)
- increment_counter(attribute, increment)
+ increment_counter(attribute, increment)
- FlushCounterIncrementsWorker.perform_in(WORKER_DELAY, self.class.name, self.id, attribute)
- else
- legacy_increment!(attribute, increment)
- end
+ FlushCounterIncrementsWorker.perform_in(WORKER_DELAY, self.class.name, self.id, attribute)
end
true
@@ -157,7 +159,7 @@ module CounterAttribute
end
def counter_attribute_enabled?(attribute)
- self.class.counter_attributes.include?(attribute)
+ self.class.counter_attribute_enabled?(attribute)
end
private
@@ -168,10 +170,6 @@ module CounterAttribute
end
end
- def legacy_increment!(attribute, increment)
- increment!(attribute, increment)
- end
-
def unsafe_update_counters(id, increments)
self.class.update_counters(id, increments)
end
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
index ecb120d8013..9de2da5aac3 100644
--- a/app/models/concerns/enums/ci/commit_status.rb
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -19,7 +19,7 @@ module Enums
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
- forward_deployment_failure: 13,
+ forward_deployment_failure: 13, # Deprecated in favor of failed_outdated_deployment_job.
user_blocked: 14,
project_deleted: 15,
ci_quota_exceeded: 16,
@@ -29,6 +29,7 @@ module Enums
builds_disabled: 20,
environment_creation_failure: 21,
deployment_rejected: 22,
+ failed_outdated_deployment_job: 23,
protected_environment_failure: 1_000,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
@@ -39,7 +40,8 @@ module Enums
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008,
reached_max_descendant_pipelines_depth: 1_009,
- ip_restriction_failure: 1_010
+ ip_restriction_failure: 1_010,
+ reached_max_pipeline_hierarchy_size: 1_011
}
end
end
diff --git a/app/models/concerns/enums/internal_id.rb b/app/models/concerns/enums/internal_id.rb
index 71c86bab136..a8227363a22 100644
--- a/app/models/concerns/enums/internal_id.rb
+++ b/app/models/concerns/enums/internal_id.rb
@@ -16,7 +16,8 @@ module Enums
alert_management_alerts: 8,
sprints: 9, # iterations
design_management_designs: 10,
- incident_management_oncall_schedules: 11
+ incident_management_oncall_schedules: 11,
+ ml_experiments: 12
}
end
end
diff --git a/app/models/concerns/from_set_operator.rb b/app/models/concerns/from_set_operator.rb
index ce3a83e9fa1..56b788eb1ab 100644
--- a/app/models/concerns/from_set_operator.rb
+++ b/app/models/concerns/from_set_operator.rb
@@ -10,7 +10,9 @@ module FromSetOperator
raise "Trying to redefine method '#{method(method_name)}'" if methods.include?(method_name)
- define_method(method_name) do |members, remove_duplicates: true, remove_order: true, alias_as: table_name|
+ define_method(method_name) do |*members, remove_duplicates: true, remove_order: true, alias_as: table_name|
+ members = flatten_ar_array(members)
+
operator_sql =
if members.any?
operator.new(members, remove_duplicates: remove_duplicates, remove_order: remove_order).to_sql
@@ -20,5 +22,26 @@ module FromSetOperator
from(Arel.sql("(#{operator_sql}) #{alias_as}"))
end
+
+ # Array#flatten with ActiveRecord::Relation items will load the ActiveRecord::Relation.
+ # Therefore we need to roll our own flatten method.
+ unless method_defined?(:flatten_ar_array) # rubocop:disable Style/GuardClause
+ define_method :flatten_ar_array do |ary|
+ arrays = ary.dup
+ result = []
+
+ until arrays.empty?
+ item = arrays.shift
+ if item.is_a?(Array)
+ arrays.concat(item.dup)
+ else
+ result.push(item)
+ end
+ end
+
+ result
+ end
+ private :flatten_ar_array
+ end
end
end
diff --git a/app/models/concerns/integrations/slack_mattermost_notifier.rb b/app/models/concerns/integrations/slack_mattermost_notifier.rb
index 142e62bb501..1ecddc015ab 100644
--- a/app/models/concerns/integrations/slack_mattermost_notifier.rb
+++ b/app/models/concerns/integrations/slack_mattermost_notifier.rb
@@ -21,13 +21,13 @@ module Integrations
)
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
+ next if response.success?
+
+ log_error('SlackMattermostNotifier HTTP error response',
+ request_host: response.request.uri.host,
+ response_code: response.code,
+ response_body: response.body
+ )
end
end
diff --git a/app/models/concerns/merge_request_reviewer_state.rb b/app/models/concerns/merge_request_reviewer_state.rb
index 18ec1c253e1..412b1da55da 100644
--- a/app/models/concerns/merge_request_reviewer_state.rb
+++ b/app/models/concerns/merge_request_reviewer_state.rb
@@ -6,20 +6,11 @@ module MergeRequestReviewerState
included do
enum state: {
unreviewed: 0,
- reviewed: 1,
- attention_requested: 2
+ reviewed: 1
}
validates :state,
presence: true,
inclusion: { in: self.states.keys }
-
- belongs_to :updated_state_by, class_name: 'User', foreign_key: :updated_state_by_user_id
-
- def attention_requested_by
- return unless attention_requested?
-
- updated_state_by
- end
end
end
diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb
index 813827478da..335fcec2611 100644
--- a/app/models/concerns/pg_full_text_searchable.rb
+++ b/app/models/concerns/pg_full_text_searchable.rb
@@ -108,6 +108,7 @@ module PgFullTextSearchable
# This fixes an inconsistency with how to_tsvector and websearch_to_tsquery process URLs
# See https://gitlab.com/gitlab-org/gitlab/-/issues/354784#note_905431920
search_term = remove_url_scheme(search_term)
+ search_term = ActiveSupport::Inflector.transliterate(search_term)
joins(:search_data).where(
Arel::Nodes::InfixOperation.new(
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 7613691bc2e..2976b6f02a7 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -86,6 +86,10 @@ module ProjectFeaturesCompatibility
write_feature_attribute_string(:operations_access_level, value)
end
+ def monitor_access_level=(value)
+ write_feature_attribute_string(:monitor_access_level, value)
+ end
+
def security_and_compliance_access_level=(value)
write_feature_attribute_string(:security_and_compliance_access_level, value)
end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 65fb62a814f..eccb004b503 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -43,6 +43,33 @@ module Sortable
}
end
+ def build_keyset_order_on_joined_column(scope:, attribute_name:, column:, direction:, nullable:)
+ reversed_direction = direction == :asc ? :desc : :asc
+
+ # rubocop: disable GitlabSecurity/PublicSend
+ order = ::Gitlab::Pagination::Keyset::Order.build(
+ [
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: attribute_name,
+ column_expression: column,
+ order_expression: column.send(direction).send(nullable),
+ reversed_order_expression: column.send(reversed_direction).send(nullable),
+ order_direction: direction,
+ distinct: false,
+ add_to_projections: true,
+ nullable: nullable
+ ),
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'id',
+ order_expression: arel_table['id'].desc
+ )
+ ]
+ )
+ # rubocop: enable GitlabSecurity/PublicSend
+
+ order.apply_cursor_conditions(scope).reorder(order)
+ end
+
private
def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])