Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns')
-rw-r--r--app/models/concerns/cacheable_attributes.rb2
-rw-r--r--app/models/concerns/ci/contextable.rb2
-rw-r--r--app/models/concerns/each_batch.rb2
-rw-r--r--app/models/concerns/featurable.rb99
-rw-r--r--app/models/concerns/has_status.rb4
-rw-r--r--app/models/concerns/import_state/sidekiq_job_tracker.rb7
-rw-r--r--app/models/concerns/integration.rb19
-rw-r--r--app/models/concerns/issuable.rb30
-rw-r--r--app/models/concerns/limitable.rb29
-rw-r--r--app/models/concerns/mentionable.rb4
-rw-r--r--app/models/concerns/milestoneish.rb20
-rw-r--r--app/models/concerns/noteable.rb2
-rw-r--r--app/models/concerns/prometheus_adapter.rb2
-rw-r--r--app/models/concerns/relative_positioning.rb2
-rw-r--r--app/models/concerns/resolvable_discussion.rb7
-rw-r--r--app/models/concerns/resolvable_note.rb4
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb2
-rw-r--r--app/models/concerns/timebox.rb51
-rw-r--r--app/models/concerns/token_authenticatable.rb5
-rw-r--r--app/models/concerns/update_highest_role.rb4
20 files changed, 240 insertions, 57 deletions
diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb
index d459af23a2f..de176ffde5c 100644
--- a/app/models/concerns/cacheable_attributes.rb
+++ b/app/models/concerns/cacheable_attributes.rb
@@ -55,7 +55,7 @@ module CacheableAttributes
current_without_cache.tap { |current_record| current_record&.cache! }
rescue => e
if Rails.env.production?
- Rails.logger.warn("Cached record for #{name} couldn't be loaded, falling back to uncached record: #{e}") # rubocop:disable Gitlab/RailsLogger
+ Gitlab::AppLogger.warn("Cached record for #{name} couldn't be loaded, falling back to uncached record: #{e}")
else
raise e
end
diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb
index ccd90ea5900..7ea5382a4fa 100644
--- a/app/models/concerns/ci/contextable.rb
+++ b/app/models/concerns/ci/contextable.rb
@@ -18,7 +18,7 @@ module Ci
variables.concat(deployment_variables(environment: environment))
variables.concat(yaml_variables)
variables.concat(user_variables)
- variables.concat(dependency_variables) if Feature.enabled?(:ci_dependency_variables, project)
+ variables.concat(dependency_variables)
variables.concat(secret_instance_variables)
variables.concat(secret_group_variables)
variables.concat(secret_project_variables(environment: environment))
diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb
index 6314b46a7e3..af5f4e30d06 100644
--- a/app/models/concerns/each_batch.rb
+++ b/app/models/concerns/each_batch.rb
@@ -17,7 +17,7 @@ module EachBatch
# Example:
#
# User.each_batch do |relation|
- # relation.update_all(updated_at: Time.now)
+ # relation.update_all(updated_at: Time.current)
# end
#
# The supplied block is also passed an optional batch index:
diff --git a/app/models/concerns/featurable.rb b/app/models/concerns/featurable.rb
new file mode 100644
index 00000000000..60aa46ce04c
--- /dev/null
+++ b/app/models/concerns/featurable.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+# == Featurable concern
+#
+# This concern adds features (tools) functionality to Project and Group
+# To enable features you need to call `set_available_features`
+#
+# Example:
+#
+# class ProjectFeature
+# include Featurable
+# set_available_features %i(wiki merge_request)
+
+module Featurable
+ extend ActiveSupport::Concern
+
+ # Can be enabled only for members, everyone or disabled
+ # Access control is made only for non private containers.
+ #
+ # Permission levels:
+ #
+ # Disabled: not enabled for anyone
+ # Private: enabled only for team members
+ # Enabled: enabled for everyone able to access the project
+ # Public: enabled for everyone (only allowed for pages)
+ DISABLED = 0
+ PRIVATE = 10
+ ENABLED = 20
+ PUBLIC = 30
+
+ STRING_OPTIONS = HashWithIndifferentAccess.new({
+ 'disabled' => DISABLED,
+ 'private' => PRIVATE,
+ 'enabled' => ENABLED,
+ 'public' => PUBLIC
+ }).freeze
+
+ class_methods do
+ def set_available_features(available_features = [])
+ @available_features = available_features
+
+ class_eval do
+ available_features.each do |feature|
+ define_method("#{feature}_enabled?") do
+ public_send("#{feature}_access_level") > DISABLED # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+
+ def available_features
+ @available_features
+ end
+
+ def access_level_attribute(feature)
+ feature = ensure_feature!(feature)
+
+ "#{feature}_access_level".to_sym
+ end
+
+ def quoted_access_level_column(feature)
+ attribute = connection.quote_column_name(access_level_attribute(feature))
+ table = connection.quote_table_name(table_name)
+
+ "#{table}.#{attribute}"
+ end
+
+ def access_level_from_str(level)
+ STRING_OPTIONS.fetch(level)
+ end
+
+ def str_from_access_level(level)
+ STRING_OPTIONS.key(level)
+ end
+
+ def ensure_feature!(feature)
+ feature = feature.model_name.plural if feature.respond_to?(:model_name)
+ feature = feature.to_sym
+ raise ArgumentError, "invalid feature: #{feature}" unless available_features.include?(feature)
+
+ feature
+ end
+ end
+
+ def access_level(feature)
+ public_send(self.class.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def feature_available?(feature, user)
+ # This feature might not be behind a feature flag at all, so default to true
+ return false unless ::Feature.enabled?(feature, user, default_enabled: true)
+
+ get_permission(user, feature)
+ end
+
+ def string_access_level(feature)
+ self.class.str_from_access_level(access_level(feature))
+ end
+end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index b80f8c2bbb2..c885dea862f 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -66,7 +66,7 @@ module HasStatus
# 1. By plucking all related objects,
# 2. Or executes expensive SQL query
def slow_composite_status(project:)
- if Feature.enabled?(:ci_composite_status, project, default_enabled: false)
+ if ::Gitlab::Ci::Features.composite_status?(project)
Gitlab::Ci::Status::Composite
.new(all, with_allow_failure: columns_hash.key?('allow_failure'))
.status
@@ -160,7 +160,7 @@ module HasStatus
if started_at && finished_at
finished_at - started_at
elsif started_at
- Time.now - started_at
+ Time.current - started_at
end
end
end
diff --git a/app/models/concerns/import_state/sidekiq_job_tracker.rb b/app/models/concerns/import_state/sidekiq_job_tracker.rb
index 55f171d158d..b7d0ed0f51b 100644
--- a/app/models/concerns/import_state/sidekiq_job_tracker.rb
+++ b/app/models/concerns/import_state/sidekiq_job_tracker.rb
@@ -5,14 +5,17 @@ module ImportState
extend ActiveSupport::Concern
included do
+ scope :with_jid, -> { where.not(jid: nil) }
+ scope :without_jid, -> { where(jid: nil) }
+
# Refreshes the expiration time of the associated import job ID.
#
# This method can be used by asynchronous importers to refresh the status,
- # preventing the StuckImportJobsWorker from marking the import as failed.
+ # preventing the Gitlab::Import::StuckProjectImportJobsWorker from marking the import as failed.
def refresh_jid_expiration
return unless jid
- Gitlab::SidekiqStatus.set(jid, StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION)
+ Gitlab::SidekiqStatus.set(jid, Gitlab::Import::StuckImportJob::IMPORT_JOBS_EXPIRATION)
end
def self.jid_by(project_id:, status:)
diff --git a/app/models/concerns/integration.rb b/app/models/concerns/integration.rb
new file mode 100644
index 00000000000..644a0ba1b5e
--- /dev/null
+++ b/app/models/concerns/integration.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Integration
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def with_custom_integration_for(integration, page = nil, per = nil)
+ custom_integration_project_ids = Service
+ .where(type: integration.type)
+ .where(inherit_from_id: nil)
+ .distinct # Required until https://gitlab.com/gitlab-org/gitlab/-/issues/207385
+ .page(page)
+ .per(per)
+ .pluck(:project_id)
+
+ Project.where(id: custom_integration_project_ids)
+ end
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index a1b14dca4ac..220af8ab7c7 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -39,15 +39,6 @@ module Issuable
locked: 4
}.with_indifferent_access.freeze
- # This object is used to gather issuable meta data for displaying
- # upvotes, downvotes, notes and closing merge requests count for issues and merge requests
- # lists avoiding n+1 queries and improving performance.
- IssuableMeta = Struct.new(:upvotes, :downvotes, :user_notes_count, :mrs_count) do
- def merge_requests_count(user = nil)
- mrs_count
- end
- end
-
included do
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_state_filter_enabled: true
@@ -139,7 +130,6 @@ module Issuable
scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
scope :with_label_ids, ->(label_ids) { joins(:label_links).where(label_links: { label_id: label_ids }) }
- scope :any_label, -> { joins(:label_links).distinct }
scope :join_project, -> { joins(:project) }
scope :inc_notes_with_associations, -> { includes(notes: [:project, :author, :award_emoji]) }
scope :references_project, -> { references(:project) }
@@ -185,6 +175,10 @@ module Issuable
assignees.count > 1
end
+ def supports_weight?
+ false
+ end
+
private
def description_max_length_for_new_records_is_valid
@@ -201,7 +195,7 @@ module Issuable
class_methods do
# Searches for records with a matching title.
#
- # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
+ # This method uses ILIKE on PostgreSQL.
#
# query - The search query as a String
#
@@ -225,7 +219,7 @@ module Issuable
# Searches for records with a matching title or description.
#
- # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
+ # This method uses ILIKE on PostgreSQL.
#
# query - The search query as a String
# matched_columns - Modify the scope of the query. 'title', 'description' or joining them with a comma.
@@ -316,6 +310,14 @@ module Issuable
end
end
+ def any_label(sort = nil)
+ if sort
+ joins(:label_links).group(*grouping_columns(sort))
+ else
+ joins(:label_links).distinct
+ end
+ end
+
# Includes table keys in group by clause when sorting
# preventing errors in postgres
#
@@ -401,6 +403,10 @@ module Issuable
participants(user).include?(user)
end
+ def can_assign_epic?(user)
+ false
+ end
+
def to_hook_data(user, old_associations: {})
changes = previous_changes
diff --git a/app/models/concerns/limitable.rb b/app/models/concerns/limitable.rb
index f320f54bb82..3cb0bd85936 100644
--- a/app/models/concerns/limitable.rb
+++ b/app/models/concerns/limitable.rb
@@ -2,6 +2,7 @@
module Limitable
extend ActiveSupport::Concern
+ GLOBAL_SCOPE = :limitable_global_scope
included do
class_attribute :limit_scope
@@ -14,14 +15,34 @@ module Limitable
private
def validate_plan_limit_not_exceeded
+ if GLOBAL_SCOPE == limit_scope
+ validate_global_plan_limit_not_exceeded
+ else
+ validate_scoped_plan_limit_not_exceeded
+ end
+ end
+
+ def validate_scoped_plan_limit_not_exceeded
scope_relation = self.public_send(limit_scope) # rubocop:disable GitlabSecurity/PublicSend
return unless scope_relation
relation = self.class.where(limit_scope => scope_relation)
+ limits = scope_relation.actual_limits
- if scope_relation.actual_limits.exceeded?(limit_name, relation)
- errors.add(:base, _("Maximum number of %{name} (%{count}) exceeded") %
- { name: limit_name.humanize(capitalize: false), count: scope_relation.actual_limits.public_send(limit_name) }) # rubocop:disable GitlabSecurity/PublicSend
- end
+ check_plan_limit_not_exceeded(limits, relation)
+ end
+
+ def validate_global_plan_limit_not_exceeded
+ relation = self.class.all
+ limits = Plan.default.actual_limits
+
+ check_plan_limit_not_exceeded(limits, relation)
+ end
+
+ def check_plan_limit_not_exceeded(limits, relation)
+ return unless limits.exceeded?(limit_name, relation)
+
+ errors.add(:base, _("Maximum number of %{name} (%{count}) exceeded") %
+ { name: limit_name.humanize(capitalize: false), count: limits.public_send(limit_name) }) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index d157404f7bc..7b4485376d4 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -259,8 +259,8 @@ module Mentionable
# for the test period.
# During the test period the flag should be enabled at the group level.
def store_mentioned_users_to_db_enabled?
- return Feature.enabled?(:store_mentioned_users_to_db, self.project&.group) if self.respond_to?(:project)
- return Feature.enabled?(:store_mentioned_users_to_db, self.group) if self.respond_to?(:group)
+ return Feature.enabled?(:store_mentioned_users_to_db, self.project&.group, default_enabled: true) if self.respond_to?(:project)
+ return Feature.enabled?(:store_mentioned_users_to_db, self.group, default_enabled: true) if self.respond_to?(:group)
end
end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index fa5a79cc12b..5f24564dc56 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -97,26 +97,6 @@ module Milestoneish
due_date && due_date.past?
end
- def group_milestone?
- false
- end
-
- def project_milestone?
- false
- end
-
- def legacy_group_milestone?
- false
- end
-
- def dashboard_milestone?
- false
- end
-
- def global_milestone?
- false
- end
-
def total_time_spent
@total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent)
end
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index 933a0b167e2..183b902dd37 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -24,7 +24,7 @@ module Noteable
# The timestamp of the note (e.g. the :created_at or :updated_at attribute if provided via
# API call)
def system_note_timestamp
- @system_note_timestamp || Time.now # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @system_note_timestamp || Time.current # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
attr_writer :system_note_timestamp
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index 761a151a474..adb6a59e11c 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -44,7 +44,7 @@ module PrometheusAdapter
{
success: true,
data: data,
- last_update: Time.now.utc
+ last_update: Time.current.utc
}
rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err.message }
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index 1653ecdb305..1d89a4497d9 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -50,7 +50,7 @@ module RelativePositioning
# This method takes two integer values (positions) and
# calculates the position between them. The range is huge as
# the maximum integer value is 2147483647. We are incrementing position by IDEAL_DISTANCE * 2 every time
- # when we have enough space. If distance is less then IDEAL_DISTANCE we are calculating an average number
+ # when we have enough space. If distance is less than IDEAL_DISTANCE, we are calculating an average number.
def position_between(pos_before, pos_after)
pos_before ||= MIN_POSITION
pos_after ||= MAX_POSITION
diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb
index 5d78eea7fca..5174ae05d15 100644
--- a/app/models/concerns/resolvable_discussion.rb
+++ b/app/models/concerns/resolvable_discussion.rb
@@ -23,7 +23,10 @@ module ResolvableDiscussion
:last_note
)
- delegate :potentially_resolvable?, to: :first_note
+ delegate :potentially_resolvable?,
+ :noteable_id,
+ :noteable_type,
+ to: :first_note
delegate :resolved_at,
:resolved_by,
@@ -79,7 +82,7 @@ module ResolvableDiscussion
return false unless current_user
return false unless resolvable?
- current_user == self.noteable.author ||
+ current_user == self.noteable.try(:author) ||
current_user.can?(:resolve_note, self.project)
end
diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb
index 2d2d5fb7168..4e8a1bb643e 100644
--- a/app/models/concerns/resolvable_note.rb
+++ b/app/models/concerns/resolvable_note.rb
@@ -23,7 +23,7 @@ module ResolvableNote
class_methods do
# This method must be kept in sync with `#resolve!`
def resolve!(current_user)
- unresolved.update_all(resolved_at: Time.now, resolved_by_id: current_user.id)
+ unresolved.update_all(resolved_at: Time.current, resolved_by_id: current_user.id)
end
# This method must be kept in sync with `#unresolve!`
@@ -57,7 +57,7 @@ module ResolvableNote
return false unless resolvable?
return false if resolved?
- self.resolved_at = Time.now
+ self.resolved_at = Time.current
self.resolved_by = current_user
self.resolved_by_push = resolved_by_push
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index da4f2a79895..250889fdf8b 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -67,7 +67,7 @@ module Storage
unless gitlab_shell.mv_namespace(repository_storage, full_path_before_last_save, full_path)
- Rails.logger.error "Exception moving path #{repository_storage} from #{full_path_before_last_save} to #{full_path}" # rubocop:disable Gitlab/RailsLogger
+ Gitlab::AppLogger.error("Exception moving path #{repository_storage} from #{full_path_before_last_save} to #{full_path}")
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
diff --git a/app/models/concerns/timebox.rb b/app/models/concerns/timebox.rb
index d29e6a01c56..8927e42dd97 100644
--- a/app/models/concerns/timebox.rb
+++ b/app/models/concerns/timebox.rb
@@ -7,7 +7,9 @@ module Timebox
include CacheMarkdownField
include Gitlab::SQL::Pattern
include IidRoutes
+ include Referable
include StripAttribute
+ include FromUnion
TimeboxStruct = Struct.new(:title, :name, :id) do
# Ensure these models match the interface required for exporting
@@ -64,7 +66,11 @@ module Timebox
groups = groups.compact if groups.is_a? Array
groups = [] if groups.nil?
- where(project_id: projects).or(where(group_id: groups))
+ if Feature.enabled?(:optimized_timebox_queries, default_enabled: true)
+ from_union([where(project_id: projects), where(group_id: groups)], remove_duplicates: false)
+ else
+ where(project_id: projects).or(where(group_id: groups))
+ end
end
scope :within_timeframe, -> (start_date, end_date) do
@@ -122,6 +128,35 @@ module Timebox
end
end
+ ##
+ # Returns the String necessary to reference a Timebox in Markdown. Group
+ # timeboxes only support name references, and do not support cross-project
+ # references.
+ #
+ # format - Symbol format to use (default: :iid, optional: :name)
+ #
+ # Examples:
+ #
+ # Milestone.first.to_reference # => "%1"
+ # Iteration.first.to_reference(format: :name) # => "*iteration:\"goal\""
+ # Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-foss%1"
+ # Iteration.first.to_reference(same_namespace_project) # => "gitlab-foss*iteration:1"
+ #
+ def to_reference(from = nil, format: :name, full: false)
+ format_reference = timebox_format_reference(format)
+ reference = "#{self.class.reference_prefix}#{format_reference}"
+
+ if project
+ "#{project.to_reference_base(from, full: full)}#{reference}"
+ else
+ reference
+ end
+ end
+
+ def reference_link_text(from = nil)
+ self.class.reference_prefix + self.title
+ end
+
def title=(value)
write_attribute(:title, sanitize_title(value)) if value.present?
end
@@ -162,6 +197,20 @@ module Timebox
private
+ def timebox_format_reference(format = :iid)
+ raise ArgumentError, _('Unknown format') unless [:iid, :name].include?(format)
+
+ if group_timebox? && format == :iid
+ raise ArgumentError, _('Cannot refer to a group %{timebox_type} by an internal id!') % { timebox_type: timebox_name }
+ end
+
+ if format == :name && !name.include?('"')
+ %("#{name}")
+ else
+ iid
+ end
+ end
+
# Timebox titles must be unique across project and group timeboxes
def uniqueness_of_title
if project
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index 4099039dd96..a1f83884f02 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -4,6 +4,10 @@ module TokenAuthenticatable
extend ActiveSupport::Concern
class_methods do
+ def encrypted_token_authenticatable_fields
+ @encrypted_token_authenticatable_fields ||= []
+ end
+
private
def add_authentication_token_field(token_field, options = {})
@@ -12,6 +16,7 @@ module TokenAuthenticatable
end
token_authenticatable_fields.push(token_field)
+ encrypted_token_authenticatable_fields.push(token_field) if options[:encrypted]
attr_accessor :cleartext_tokens
diff --git a/app/models/concerns/update_highest_role.rb b/app/models/concerns/update_highest_role.rb
index 7efc436c6c8..6432cc794a5 100644
--- a/app/models/concerns/update_highest_role.rb
+++ b/app/models/concerns/update_highest_role.rb
@@ -29,9 +29,7 @@ module UpdateHighestRole
UpdateHighestRoleWorker.perform_in(HIGHEST_ROLE_JOB_DELAY, update_highest_role_attribute)
else
# use same logging as ExclusiveLeaseGuard
- # rubocop:disable Gitlab/RailsLogger
- Rails.logger.error('Cannot obtain an exclusive lease. There must be another instance already in execution.')
- # rubocop:enable Gitlab/RailsLogger
+ Gitlab::AppLogger.error('Cannot obtain an exclusive lease. There must be another instance already in execution.')
end
end
end