summaryrefslogtreecommitdiff
path: root/app/models/concerns
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2021-01-20 13:34:23 -0600
committerRobert Speicher <rspeicher@gmail.com>2021-01-20 13:34:23 -0600
commit6438df3a1e0fb944485cebf07976160184697d72 (patch)
tree00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /app/models/concerns
parent42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff)
downloadgitlab-ce-6438df3a1e0fb944485cebf07976160184697d72.tar.gz
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/boards/listable.rb52
-rw-r--r--app/models/concerns/bulk_insert_safe.rb4
-rw-r--r--app/models/concerns/ci/artifactable.rb3
-rw-r--r--app/models/concerns/each_batch.rb16
-rw-r--r--app/models/concerns/enums/ci/commit_status.rb36
-rw-r--r--app/models/concerns/enums/commit_status.rb35
-rw-r--r--app/models/concerns/enums/vulnerability.rb46
-rw-r--r--app/models/concerns/issuable.rb11
-rw-r--r--app/models/concerns/issue_available_features.rb5
-rw-r--r--app/models/concerns/milestoneable.rb2
-rw-r--r--app/models/concerns/noteable.rb21
-rw-r--r--app/models/concerns/packages/debian/architecture.rb25
-rw-r--r--app/models/concerns/packages/debian/distribution.rb115
-rw-r--r--app/models/concerns/repositories/can_housekeep_repository.rb25
14 files changed, 350 insertions, 46 deletions
diff --git a/app/models/concerns/boards/listable.rb b/app/models/concerns/boards/listable.rb
new file mode 100644
index 00000000000..b7c0a8b3489
--- /dev/null
+++ b/app/models/concerns/boards/listable.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Boards
+ module Listable
+ extend ActiveSupport::Concern
+
+ included do
+ validates :label, :position, presence: true, if: :label?
+ validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :movable?
+
+ before_destroy :can_be_destroyed
+
+ scope :ordered, -> { order(:list_type, :position) }
+ scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) }
+ scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) }
+ end
+
+ class_methods do
+ def destroyable_types
+ [:label]
+ end
+
+ def movable_types
+ [:label]
+ end
+ end
+
+ def destroyable?
+ self.class.destroyable_types.include?(list_type&.to_sym)
+ end
+
+ def movable?
+ self.class.movable_types.include?(list_type&.to_sym)
+ end
+
+ def title
+ if label?
+ label.name
+ elsif backlog?
+ _('Open')
+ else
+ list_type.humanize
+ end
+ end
+
+ private
+
+ def can_be_destroyed
+ throw(:abort) unless destroyable? # rubocop:disable Cop/BanCatchThrow
+ end
+ end
+end
diff --git a/app/models/concerns/bulk_insert_safe.rb b/app/models/concerns/bulk_insert_safe.rb
index f9eb3fb875e..3748e77e933 100644
--- a/app/models/concerns/bulk_insert_safe.rb
+++ b/app/models/concerns/bulk_insert_safe.rb
@@ -53,9 +53,9 @@ module BulkInsertSafe
class_methods do
def set_callback(name, *args)
unless _bulk_insert_callback_allowed?(name, args)
- raise MethodNotAllowedError.new(
+ raise MethodNotAllowedError,
"Not allowed to call `set_callback(#{name}, #{args})` when model extends `BulkInsertSafe`." \
- "Callbacks that fire per each record being inserted do not work with bulk-inserts.")
+ "Callbacks that fire per each record being inserted do not work with bulk-inserts."
end
super
diff --git a/app/models/concerns/ci/artifactable.rb b/app/models/concerns/ci/artifactable.rb
index 24df86dbc3c..cbe7d3b6abb 100644
--- a/app/models/concerns/ci/artifactable.rb
+++ b/app/models/concerns/ci/artifactable.rb
@@ -18,7 +18,8 @@ module Ci
gzip: 3
}, _suffix: true
- scope :expired, -> (limit) { where('expire_at < ?', Time.current).limit(limit) }
+ scope :expired_before, -> (timestamp) { where(arel_table[:expire_at].lt(timestamp)) }
+ scope :expired, -> (limit) { expired_before(Time.current).limit(limit) }
end
def each_blob(&blk)
diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb
index af5f4e30d06..a59f00d73ec 100644
--- a/app/models/concerns/each_batch.rb
+++ b/app/models/concerns/each_batch.rb
@@ -47,7 +47,7 @@ module EachBatch
# order_hint does not affect the search results. For example,
# `ORDER BY id ASC, updated_at ASC` means the same thing as `ORDER
# BY id ASC`.
- def each_batch(of: 1000, column: primary_key, order_hint: nil)
+ def each_batch(of: 1000, column: primary_key, order: :asc, order_hint: nil)
unless column
raise ArgumentError,
'the column: argument must be set to a column name to use for ordering rows'
@@ -55,7 +55,7 @@ module EachBatch
start = except(:select)
.select(column)
- .reorder(column => :asc)
+ .reorder(column => order)
start = start.order(order_hint) if order_hint
start = start.take
@@ -66,10 +66,12 @@ module EachBatch
arel_table = self.arel_table
1.step do |index|
+ start_cond = arel_table[column].gteq(start_id)
+ start_cond = arel_table[column].lteq(start_id) if order == :desc
stop = except(:select)
.select(column)
- .where(arel_table[column].gteq(start_id))
- .reorder(column => :asc)
+ .where(start_cond)
+ .reorder(column => order)
stop = stop.order(order_hint) if order_hint
stop = stop
@@ -77,12 +79,14 @@ module EachBatch
.limit(1)
.take
- relation = where(arel_table[column].gteq(start_id))
+ relation = where(start_cond)
if stop
stop_id = stop[column]
start_id = stop_id
- relation = relation.where(arel_table[column].lt(stop_id))
+ stop_cond = arel_table[column].lt(stop_id)
+ stop_cond = arel_table[column].gt(stop_id) if order == :desc
+ relation = relation.where(stop_cond)
end
# Any ORDER BYs are useless for this relation and can lead to less
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
new file mode 100644
index 00000000000..48b4a402974
--- /dev/null
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+module Enums
+ module Ci
+ module CommitStatus
+ # Returns the Hash to use for creating the `failure_reason` enum for
+ # `CommitStatus`.
+ def self.failure_reasons
+ {
+ unknown_failure: nil,
+ script_failure: 1,
+ api_failure: 2,
+ stuck_or_timeout_failure: 3,
+ runner_system_failure: 4,
+ missing_dependency_failure: 5,
+ runner_unsupported: 6,
+ stale_schedule: 7,
+ job_execution_timeout: 8,
+ archived_failure: 9,
+ unmet_prerequisites: 10,
+ scheduler_failure: 11,
+ data_integrity_failure: 12,
+ forward_deployment_failure: 13,
+ insufficient_bridge_permissions: 1_001,
+ downstream_bridge_project_not_found: 1_002,
+ invalid_bridge_trigger: 1_003,
+ bridge_pipeline_is_child_pipeline: 1_006, # not used anymore, but cannot be deleted because of old data
+ downstream_pipeline_creation_failed: 1_007,
+ secrets_provider_not_found: 1_008,
+ reached_max_descendant_pipelines_depth: 1_009
+ }
+ end
+ end
+ end
+end
+
+Enums::Ci::CommitStatus.prepend_if_ee('EE::Enums::Ci::CommitStatus')
diff --git a/app/models/concerns/enums/commit_status.rb b/app/models/concerns/enums/commit_status.rb
deleted file mode 100644
index faeed7276ab..00000000000
--- a/app/models/concerns/enums/commit_status.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-module Enums
- module CommitStatus
- # Returns the Hash to use for creating the `failure_reason` enum for
- # `CommitStatus`.
- def self.failure_reasons
- {
- unknown_failure: nil,
- script_failure: 1,
- api_failure: 2,
- stuck_or_timeout_failure: 3,
- runner_system_failure: 4,
- missing_dependency_failure: 5,
- runner_unsupported: 6,
- stale_schedule: 7,
- job_execution_timeout: 8,
- archived_failure: 9,
- unmet_prerequisites: 10,
- scheduler_failure: 11,
- data_integrity_failure: 12,
- forward_deployment_failure: 13,
- insufficient_bridge_permissions: 1_001,
- downstream_bridge_project_not_found: 1_002,
- invalid_bridge_trigger: 1_003,
- bridge_pipeline_is_child_pipeline: 1_006, # not used anymore, but cannot be deleted because of old data
- downstream_pipeline_creation_failed: 1_007,
- secrets_provider_not_found: 1_008,
- reached_max_descendant_pipelines_depth: 1_009
- }
- end
- end
-end
-
-Enums::CommitStatus.prepend_if_ee('EE::Enums::CommitStatus')
diff --git a/app/models/concerns/enums/vulnerability.rb b/app/models/concerns/enums/vulnerability.rb
new file mode 100644
index 00000000000..4b2e9e9e0b2
--- /dev/null
+++ b/app/models/concerns/enums/vulnerability.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Enums
+ module Vulnerability
+ CONFIDENCE_LEVELS = {
+ # undefined: 0, no longer applicable
+ ignore: 1,
+ unknown: 2,
+ experimental: 3,
+ low: 4,
+ medium: 5,
+ high: 6,
+ confirmed: 7
+ }.with_indifferent_access.freeze
+
+ REPORT_TYPES = {
+ sast: 0,
+ secret_detection: 4
+ }.with_indifferent_access.freeze
+
+ SEVERITY_LEVELS = {
+ # undefined: 0, no longer applicable
+ info: 1,
+ unknown: 2,
+ # experimental: 3, formerly used by confidence, no longer applicable
+ low: 4,
+ medium: 5,
+ high: 6,
+ critical: 7
+ }.with_indifferent_access.freeze
+
+ def self.confidence_levels
+ CONFIDENCE_LEVELS
+ end
+
+ def self.report_types
+ REPORT_TYPES
+ end
+
+ def self.severity_levels
+ SEVERITY_LEVELS
+ end
+ end
+end
+
+Enums::Vulnerability.prepend_if_ee('EE::Enums::Vulnerability')
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index c3a394c1ca5..83ff5b16efe 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -196,6 +196,10 @@ module Issuable
is_a?(Issue)
end
+ def supports_assignee?
+ false
+ end
+
def severity
return IssuableSeverity::DEFAULT unless supports_severity?
@@ -216,6 +220,10 @@ module Issuable
end
class_methods do
+ def participant_includes
+ [:assignees, :author, { notes: [:author, :award_emoji] }]
+ end
+
# Searches for records with a matching title.
#
# This method uses ILIKE on PostgreSQL.
@@ -344,12 +352,15 @@ module Issuable
#
# Returns an array of arel columns
def grouping_columns(sort)
+ sort = sort.to_s
grouping_columns = [arel_table[:id]]
if %w(milestone_due_desc milestone_due_asc milestone).include?(sort)
milestone_table = Milestone.arel_table
grouping_columns << milestone_table[:id]
grouping_columns << milestone_table[:due_date]
+ elsif %w(merged_at_desc merged_at_asc).include?(sort)
+ grouping_columns << MergeRequest::Metrics.arel_table[:merged_at]
end
grouping_columns
diff --git a/app/models/concerns/issue_available_features.rb b/app/models/concerns/issue_available_features.rb
index 886db133a94..a5ffa959174 100644
--- a/app/models/concerns/issue_available_features.rb
+++ b/app/models/concerns/issue_available_features.rb
@@ -9,7 +9,10 @@ module IssueAvailableFeatures
class_methods do
# EE only features are listed on EE::IssueAvailableFeatures
def available_features_for_issue_types
- {}.with_indifferent_access
+ {
+ assignee: %w(issue incident),
+ confidentiality: %(issue incident)
+ }.with_indifferent_access
end
end
diff --git a/app/models/concerns/milestoneable.rb b/app/models/concerns/milestoneable.rb
index b1698bc2ee3..ccb334343ff 100644
--- a/app/models/concerns/milestoneable.rb
+++ b/app/models/concerns/milestoneable.rb
@@ -51,7 +51,7 @@ module Milestoneable
# Overridden on EE module
#
def supports_milestone?
- respond_to?(:milestone_id) && !incident?
+ respond_to?(:milestone_id)
end
end
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index 2dbe9360d42..f3cc68e4b85 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -19,6 +19,11 @@ module Noteable
def resolvable_types
%w(MergeRequest DesignManagement::Design)
end
+
+ # `Noteable` class names that support creating/forwarding individual notes.
+ def email_creatable_types
+ %w(Issue)
+ end
end
# The timestamp of the note (e.g. the :created_at or :updated_at attribute if provided via
@@ -55,6 +60,10 @@ module Noteable
supports_discussions? && self.class.replyable_types.include?(base_class_name)
end
+ def supports_creating_notes_by_email?
+ self.class.email_creatable_types.include?(base_class_name)
+ end
+
def supports_suggestion?
false
end
@@ -158,6 +167,18 @@ module Noteable
def after_note_destroyed(_note)
# no-op
end
+
+ # Email address that an authorized user can send/forward an email to be added directly
+ # to an issue or merge request.
+ # example: incoming+h5bp-html5-boilerplate-8-1234567890abcdef123456789-issue-34@localhost.com
+ def creatable_note_email_address(author)
+ return unless supports_creating_notes_by_email?
+
+ project_email = project.new_issuable_address(author, self.class.name.underscore)
+ return unless project_email
+
+ project_email.sub('@', "-#{iid}@")
+ end
end
Noteable.extend(Noteable::ClassMethods)
diff --git a/app/models/concerns/packages/debian/architecture.rb b/app/models/concerns/packages/debian/architecture.rb
new file mode 100644
index 00000000000..4aa633e0357
--- /dev/null
+++ b/app/models/concerns/packages/debian/architecture.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ module Architecture
+ extend ActiveSupport::Concern
+
+ included do
+ belongs_to :distribution, class_name: "Packages::Debian::#{container_type.capitalize}Distribution", inverse_of: :architectures
+
+ validates :distribution,
+ presence: true
+
+ validates :name,
+ presence: true,
+ length: { maximum: 255 },
+ uniqueness: { scope: %i[distribution_id] },
+ format: { with: Gitlab::Regex.debian_architecture_regex }
+
+ scope :with_distribution, ->(distribution) { where(distribution: distribution) }
+ scope :with_name, ->(name) { where(name: name) }
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/packages/debian/distribution.rb b/app/models/concerns/packages/debian/distribution.rb
new file mode 100644
index 00000000000..285d293c9ee
--- /dev/null
+++ b/app/models/concerns/packages/debian/distribution.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ module Distribution
+ extend ActiveSupport::Concern
+
+ included do
+ include FileStoreMounter
+
+ def self.container_foreign_key
+ "#{container_type}_id".to_sym
+ end
+
+ alias_attribute :container, container_type
+ alias_attribute :container_id, "#{container_type}_id"
+
+ belongs_to container_type
+ belongs_to :creator, class_name: 'User'
+
+ has_many :architectures,
+ class_name: "Packages::Debian::#{container_type.capitalize}Architecture",
+ foreign_key: :distribution_id,
+ inverse_of: :distribution
+
+ validates :codename,
+ presence: true,
+ uniqueness: { scope: [container_foreign_key] },
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+
+ validates :suite,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+ validates :suite,
+ uniqueness: { scope: [container_foreign_key] },
+ if: :suite
+
+ validate :unique_codename_and_suite
+
+ validates :origin,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+
+ validates :label,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_distribution_regex }
+
+ validates :version,
+ allow_nil: true,
+ format: { with: Gitlab::Regex.debian_version_regex }
+
+ # The Valid-Until field is a security measure to prevent malicious attackers to
+ # serve an outdated repository, with vulnerable packages
+ # (keeping in mind that most Debian repository are not using TLS but use GPG
+ # signatures instead).
+ # A minimum of 24 hours is simply to avoid generating indices too often
+ # (which generates load).
+ # Official Debian repositories are generated 4 times a day, and valid for 7 days.
+ # Full ref: https://wiki.debian.org/DebianRepository/Format#Date.2C_Valid-Until
+ validates :valid_time_duration_seconds,
+ allow_nil: true,
+ numericality: { greater_than_or_equal_to: 24.hours.to_i }
+
+ validates container_type, presence: true
+ validates :file_store, presence: true
+
+ validates :file_signature, absence: true
+ validates :signing_keys, absence: true
+
+ scope :with_container, ->(subject) { where(container_type => subject) }
+ scope :with_codename, ->(codename) { where(codename: codename) }
+ scope :with_suite, ->(suite) { where(suite: suite) }
+ scope :with_codename_or_suite, ->(codename_or_suite) { with_codename(codename_or_suite).or(with_suite(codename_or_suite)) }
+
+ attr_encrypted :signing_keys,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-gcm',
+ encode: false,
+ encode_iv: false
+
+ mount_file_store_uploader Packages::Debian::DistributionReleaseFileUploader
+
+ def needs_update?
+ !file.exists? || time_duration_expired?
+ end
+
+ private
+
+ def time_duration_expired?
+ return false unless valid_time_duration_seconds.present?
+
+ updated_at + valid_time_duration_seconds.seconds + 6.hours < Time.current
+ end
+
+ def unique_codename_and_suite
+ errors.add(:codename, _('has already been taken as Suite')) if codename_exists_as_suite?
+ errors.add(:suite, _('has already been taken as Codename')) if suite_exists_as_codename?
+ end
+
+ def codename_exists_as_suite?
+ return false unless codename.present?
+
+ self.class.with_container(container).with_suite(codename).exists?
+ end
+
+ def suite_exists_as_codename?
+ return false unless suite.present?
+
+ self.class.with_container(container).with_codename(suite).exists?
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/repositories/can_housekeep_repository.rb b/app/models/concerns/repositories/can_housekeep_repository.rb
new file mode 100644
index 00000000000..2b79851a07c
--- /dev/null
+++ b/app/models/concerns/repositories/can_housekeep_repository.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Repositories
+ module CanHousekeepRepository
+ extend ActiveSupport::Concern
+
+ def pushes_since_gc
+ Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i }
+ end
+
+ def increment_pushes_since_gc
+ Gitlab::Redis::SharedState.with { |redis| redis.incr(pushes_since_gc_redis_shared_state_key) }
+ end
+
+ def reset_pushes_since_gc
+ Gitlab::Redis::SharedState.with { |redis| redis.del(pushes_since_gc_redis_shared_state_key) }
+ end
+
+ private
+
+ def pushes_since_gc_redis_shared_state_key
+ "#{self.class.name.underscore.pluralize}/#{id}/pushes_since_gc"
+ end
+ end
+end