diff options
Diffstat (limited to 'app/models/concerns')
-rw-r--r-- | app/models/concerns/atomic_internal_id.rb | 118 | ||||
-rw-r--r-- | app/models/concerns/enums/ci/pipeline.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/enums/internal_id.rb | 3 | ||||
-rw-r--r-- | app/models/concerns/featurable.rb | 3 | ||||
-rw-r--r-- | app/models/concerns/from_union.rb | 21 | ||||
-rw-r--r-- | app/models/concerns/has_repository.rb | 5 | ||||
-rw-r--r-- | app/models/concerns/issue_available_features.rb | 17 | ||||
-rw-r--r-- | app/models/concerns/mentionable/reference_regexes.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/project_features_compatibility.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/project_services_loggable.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/protected_ref_access.rb | 8 | ||||
-rw-r--r-- | app/models/concerns/storage/legacy_namespace.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/todoable.rb | 10 | ||||
-rw-r--r-- | app/models/concerns/triggerable_hooks.rb | 4 |
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 |