summaryrefslogtreecommitdiff
path: root/app/models/concerns
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/atomic_internal_id.rb118
-rw-r--r--app/models/concerns/enums/ci/pipeline.rb4
-rw-r--r--app/models/concerns/enums/internal_id.rb3
-rw-r--r--app/models/concerns/featurable.rb3
-rw-r--r--app/models/concerns/from_union.rb21
-rw-r--r--app/models/concerns/has_repository.rb5
-rw-r--r--app/models/concerns/issue_available_features.rb17
-rw-r--r--app/models/concerns/mentionable/reference_regexes.rb2
-rw-r--r--app/models/concerns/project_features_compatibility.rb2
-rw-r--r--app/models/concerns/project_services_loggable.rb4
-rw-r--r--app/models/concerns/protected_ref_access.rb8
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb2
-rw-r--r--app/models/concerns/todoable.rb10
-rw-r--r--app/models/concerns/triggerable_hooks.rb4
14 files changed, 159 insertions, 44 deletions
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index 4a632e8cd0c..baa99fa5a7f 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -27,16 +27,42 @@ module AtomicInternalId
extend ActiveSupport::Concern
class_methods do
- def has_internal_id(column, scope:, init:, ensure_if: nil, track_if: nil, presence: true, backfill: false) # rubocop:disable Naming/PredicateName
- # We require init here to retain the ability to recalculate in the absence of a
- # InternalId record (we may delete records in `internal_ids` for example).
- raise "has_internal_id requires a init block, none given." unless init
+ def has_internal_id( # rubocop:disable Naming/PredicateName
+ column, scope:, init: :not_given, ensure_if: nil, track_if: nil,
+ presence: true, backfill: false, hook_names: :create)
+ raise "has_internal_id init must not be nil if given." if init.nil?
raise "has_internal_id needs to be defined on association." unless self.reflect_on_association(scope)
- before_validation :"track_#{scope}_#{column}!", on: :create, if: track_if
- before_validation :"ensure_#{scope}_#{column}!", on: :create, if: ensure_if
+ init = infer_init(scope) if init == :not_given
+ before_validation :"track_#{scope}_#{column}!", on: hook_names, if: track_if
+ before_validation :"ensure_#{scope}_#{column}!", on: hook_names, if: ensure_if
validates column, presence: presence
+ define_singleton_internal_id_methods(scope, column, init)
+ define_instance_internal_id_methods(scope, column, init, backfill)
+ end
+
+ private
+
+ def infer_init(scope)
+ case scope
+ when :project
+ AtomicInternalId.project_init(self)
+ when :group
+ AtomicInternalId.group_init(self)
+ else
+ # We require init here to retain the ability to recalculate in the absence of a
+ # InternalId record (we may delete records in `internal_ids` for example).
+ raise "has_internal_id - cannot infer init for scope: #{scope}"
+ end
+ end
+
+ # Defines instance methods:
+ # - ensure_{scope}_{column}!
+ # - track_{scope}_{column}!
+ # - reset_{scope}_{column}
+ # - {column}=
+ def define_instance_internal_id_methods(scope, column, init, backfill)
define_method("ensure_#{scope}_#{column}!") do
return if backfill && self.class.where(column => nil).exists?
@@ -103,19 +129,95 @@ module AtomicInternalId
read_attribute(column)
end
end
+
+ # Defines class methods:
+ #
+ # - with_{scope}_{column}_supply
+ # This method can be used to allocate a block of IID values during
+ # bulk operations (importing/copying, etc). This can be more efficient
+ # than creating instances one-by-one.
+ #
+ # Pass in a block that receives a `Supply` instance. To allocate a new
+ # IID value, call `Supply#next_value`.
+ #
+ # Example:
+ #
+ # MyClass.with_project_iid_supply(project) do |supply|
+ # attributes = MyClass.where(project: project).find_each do |record|
+ # record.attributes.merge(iid: supply.next_value)
+ # end
+ #
+ # bulk_insert(attributes)
+ # end
+ def define_singleton_internal_id_methods(scope, column, init)
+ define_singleton_method("with_#{scope}_#{column}_supply") do |scope_value, &block|
+ subject = find_by(scope => scope_value) || self
+ scope_attrs = ::AtomicInternalId.scope_attrs(scope_value)
+ usage = ::AtomicInternalId.scope_usage(self)
+
+ generator = InternalId::InternalIdGenerator.new(subject, scope_attrs, usage, init)
+
+ generator.with_lock do
+ supply = Supply.new(generator.record.last_value)
+ block.call(supply)
+ ensure
+ generator.track_greatest(supply.current_value) if supply
+ end
+ end
+ end
+ end
+
+ def self.scope_attrs(scope_value)
+ { scope_value.class.table_name.singularize.to_sym => scope_value } if scope_value
end
def internal_id_scope_attrs(scope)
scope_value = internal_id_read_scope(scope)
- { scope_value.class.table_name.singularize.to_sym => scope_value } if scope_value
+ ::AtomicInternalId.scope_attrs(scope_value)
end
def internal_id_scope_usage
- self.class.table_name.to_sym
+ ::AtomicInternalId.scope_usage(self.class)
+ end
+
+ def self.scope_usage(including_class)
+ including_class.table_name.to_sym
+ end
+
+ def self.project_init(klass, column_name = :iid)
+ ->(instance, scope) do
+ if instance
+ klass.where(project_id: instance.project_id).maximum(column_name)
+ elsif scope.present?
+ klass.where(**scope).maximum(column_name)
+ end
+ end
+ end
+
+ def self.group_init(klass, column_name = :iid)
+ ->(instance, scope) do
+ if instance
+ klass.where(group_id: instance.group_id).maximum(column_name)
+ elsif scope.present?
+ klass.where(group: scope[:namespace]).maximum(column_name)
+ end
+ end
end
def internal_id_read_scope(scope)
association(scope).reader
end
+
+ class Supply
+ attr_reader :current_value
+
+ def initialize(start_value)
+ @current_value = start_value
+ end
+
+ def next_value
+ @current_value += 1
+ end
+ end
end
diff --git a/app/models/concerns/enums/ci/pipeline.rb b/app/models/concerns/enums/ci/pipeline.rb
index f1bc43a12d8..bb8df37f649 100644
--- a/app/models/concerns/enums/ci/pipeline.rb
+++ b/app/models/concerns/enums/ci/pipeline.rb
@@ -53,6 +53,10 @@ module Enums
sources.except(*dangling_sources.keys)
end
+ def self.ci_and_parent_sources
+ ci_sources.merge(sources.slice(:parent_pipeline))
+ end
+
# Returns the `Hash` to use for creating the `config_sources` enum for
# `Ci::Pipeline`.
def self.config_sources
diff --git a/app/models/concerns/enums/internal_id.rb b/app/models/concerns/enums/internal_id.rb
index 2d51d232e93..f01bd60ef16 100644
--- a/app/models/concerns/enums/internal_id.rb
+++ b/app/models/concerns/enums/internal_id.rb
@@ -14,7 +14,8 @@ module Enums
operations_feature_flags: 6,
operations_user_lists: 7,
alert_management_alerts: 8,
- sprints: 9 # iterations
+ sprints: 9, # iterations
+ design_management_designs: 10
}
end
end
diff --git a/app/models/concerns/featurable.rb b/app/models/concerns/featurable.rb
index 60aa46ce04c..20b72957ec2 100644
--- a/app/models/concerns/featurable.rb
+++ b/app/models/concerns/featurable.rb
@@ -37,7 +37,8 @@ module Featurable
class_methods do
def set_available_features(available_features = [])
- @available_features = available_features
+ @available_features ||= []
+ @available_features += available_features
class_eval do
available_features.each do |feature|
diff --git a/app/models/concerns/from_union.rb b/app/models/concerns/from_union.rb
index e25d603b802..be6744f1b2a 100644
--- a/app/models/concerns/from_union.rb
+++ b/app/models/concerns/from_union.rb
@@ -37,27 +37,6 @@ module FromUnion
# rubocop: disable Gitlab/Union
extend FromSetOperator
define_set_operator Gitlab::SQL::Union
-
- alias_method :from_union_set_operator, :from_union
- def from_union(members, remove_duplicates: true, alias_as: table_name)
- if Feature.enabled?(:sql_set_operators)
- from_union_set_operator(members, remove_duplicates: remove_duplicates, alias_as: alias_as)
- else
- # The original from_union method.
- standard_from_union(members, remove_duplicates: remove_duplicates, alias_as: alias_as)
- end
- end
-
- private
-
- def standard_from_union(members, remove_duplicates: true, alias_as: table_name)
- union = Gitlab::SQL::Union
- .new(members, remove_duplicates: remove_duplicates)
- .to_sql
-
- from(Arel.sql("(#{union}) #{alias_as}"))
- end
-
# rubocop: enable Gitlab/Union
end
end
diff --git a/app/models/concerns/has_repository.rb b/app/models/concerns/has_repository.rb
index 978a54bdee7..3dea4a9f5fb 100644
--- a/app/models/concerns/has_repository.rb
+++ b/app/models/concerns/has_repository.rb
@@ -109,6 +109,11 @@ module HasRepository
Gitlab::RepositoryUrlBuilder.build(repository.full_path, protocol: :http)
end
+ # Is overridden in EE::Project for Geo support
+ def lfs_http_url_to_repo(_operation = nil)
+ http_url_to_repo
+ end
+
def web_url(only_path: nil)
Gitlab::UrlBuilder.build(self, only_path: only_path)
end
diff --git a/app/models/concerns/issue_available_features.rb b/app/models/concerns/issue_available_features.rb
index 6efb8103b7b..886db133a94 100644
--- a/app/models/concerns/issue_available_features.rb
+++ b/app/models/concerns/issue_available_features.rb
@@ -6,18 +6,25 @@
module IssueAvailableFeatures
extend ActiveSupport::Concern
- # EE only features are listed on EE::IssueAvailableFeatures
- def available_features_for_issue_types
- {}.with_indifferent_access
+ class_methods do
+ # EE only features are listed on EE::IssueAvailableFeatures
+ def available_features_for_issue_types
+ {}.with_indifferent_access
+ end
+ end
+
+ included do
+ scope :with_feature, ->(feature) { where(issue_type: available_features_for_issue_types[feature]) }
end
def issue_type_supports?(feature)
- unless available_features_for_issue_types.has_key?(feature)
+ unless self.class.available_features_for_issue_types.has_key?(feature)
raise ArgumentError, 'invalid feature'
end
- available_features_for_issue_types[feature].include?(issue_type)
+ self.class.available_features_for_issue_types[feature].include?(issue_type)
end
end
IssueAvailableFeatures.prepend_if_ee('EE::IssueAvailableFeatures')
+IssueAvailableFeatures::ClassMethods.prepend_if_ee('EE::IssueAvailableFeatures::ClassMethods')
diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb
index 307d58a3a3c..5a5ce1809d0 100644
--- a/app/models/concerns/mentionable/reference_regexes.rb
+++ b/app/models/concerns/mentionable/reference_regexes.rb
@@ -22,7 +22,7 @@ module Mentionable
def self.default_pattern
strong_memoize(:default_pattern) do
issue_pattern = Issue.reference_pattern
- link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic].map(&:link_reference_pattern).compact)
+ link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic, Vulnerability].map(&:link_reference_pattern).compact)
reference_pattern(link_patterns, issue_pattern)
end
end
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index cedcf164a49..b69fb2931c3 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -88,3 +88,5 @@ module ProjectFeaturesCompatibility
project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend
end
end
+
+ProjectFeaturesCompatibility.prepend_if_ee('EE::ProjectFeaturesCompatibility')
diff --git a/app/models/concerns/project_services_loggable.rb b/app/models/concerns/project_services_loggable.rb
index fecd77cdc98..e5385435138 100644
--- a/app/models/concerns/project_services_loggable.rb
+++ b/app/models/concerns/project_services_loggable.rb
@@ -16,8 +16,8 @@ module ProjectServicesLoggable
def build_message(message, params = {})
{
service_class: self.class.name,
- project_id: project.id,
- project_path: project.full_path,
+ project_id: project&.id,
+ project_path: project&.full_path,
message: message
}.merge(params)
end
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
index d1e3d9b2aff..28dc3366e51 100644
--- a/app/models/concerns/protected_ref_access.rb
+++ b/app/models/concerns/protected_ref_access.rb
@@ -36,10 +36,12 @@ module ProtectedRefAccess
HUMAN_ACCESS_LEVELS[self.access_level]
end
- # CE access levels are always role-based,
- # where as EE allows groups and users too
+ def type
+ :role
+ end
+
def role?
- true
+ type == :role
end
def check_access(user)
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index 71b976c6f11..a82cf338039 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -90,7 +90,7 @@ module Storage
end
def old_repository_storages
- @old_repository_storage_paths ||= repository_storages
+ @old_repository_storage_paths ||= repository_storages(legacy_only: true)
end
def repository_storages(legacy_only: false)
diff --git a/app/models/concerns/todoable.rb b/app/models/concerns/todoable.rb
new file mode 100644
index 00000000000..d93ab463251
--- /dev/null
+++ b/app/models/concerns/todoable.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+# == Todoable concern
+#
+# Specify object types that supports todos.
+#
+# Used by Issue, MergeRequest, Design and Epic.
+#
+module Todoable
+end
diff --git a/app/models/concerns/triggerable_hooks.rb b/app/models/concerns/triggerable_hooks.rb
index b64a9e4f70b..325a5531926 100644
--- a/app/models/concerns/triggerable_hooks.rb
+++ b/app/models/concerns/triggerable_hooks.rb
@@ -13,7 +13,9 @@ module TriggerableHooks
job_hooks: :job_events,
pipeline_hooks: :pipeline_events,
wiki_page_hooks: :wiki_page_events,
- deployment_hooks: :deployment_events
+ deployment_hooks: :deployment_events,
+ feature_flag_hooks: :feature_flag_events,
+ release_hooks: :releases_events
}.freeze
extend ActiveSupport::Concern