diff options
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.rb | 11 | ||||
-rw-r--r-- | app/models/concerns/ci/has_deployment_name.rb | 15 | ||||
-rw-r--r-- | app/models/concerns/ci/lockable.rb | 20 | ||||
-rw-r--r-- | app/models/concerns/ci/metadatable.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/ci/partitionable.rb | 47 | ||||
-rw-r--r-- | app/models/concerns/ci/track_environment_usage.rb | 31 | ||||
-rw-r--r-- | app/models/concerns/counter_attribute.rb | 20 | ||||
-rw-r--r-- | app/models/concerns/enums/ci/commit_status.rb | 6 | ||||
-rw-r--r-- | app/models/concerns/enums/internal_id.rb | 3 | ||||
-rw-r--r-- | app/models/concerns/from_set_operator.rb | 25 | ||||
-rw-r--r-- | app/models/concerns/integrations/slack_mattermost_notifier.rb | 14 | ||||
-rw-r--r-- | app/models/concerns/merge_request_reviewer_state.rb | 11 | ||||
-rw-r--r-- | app/models/concerns/pg_full_text_searchable.rb | 1 | ||||
-rw-r--r-- | app/models/concerns/project_features_compatibility.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/sortable.rb | 27 |
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: []) |