diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /app/models/concerns | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/models/concerns')
19 files changed, 189 insertions, 141 deletions
diff --git a/app/models/concerns/after_commit_queue.rb b/app/models/concerns/after_commit_queue.rb new file mode 100644 index 00000000000..7f525bec9e9 --- /dev/null +++ b/app/models/concerns/after_commit_queue.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module AfterCommitQueue + extend ActiveSupport::Concern + + included do + after_commit :_run_after_commit_queue + after_rollback :_clear_after_commit_queue + end + + def run_after_commit(&block) + _after_commit_queue << block if block + + true + end + + def run_after_commit_or_now(&block) + if self.class.inside_transaction? + if connection.current_transaction.records&.include?(self) + run_after_commit(&block) + else + # If the current transaction does not include this record, we can run + # the block now, even if it queues a Sidekiq job. + Sidekiq::Worker.skipping_transaction_check do + instance_eval(&block) + end + end + else + instance_eval(&block) + end + + true + end + + protected + + def _run_after_commit_queue + while action = _after_commit_queue.pop + self.instance_eval(&action) + end + end + + def _after_commit_queue + @after_commit_queue ||= [] + end + + def _clear_after_commit_queue + _after_commit_queue.clear + end +end diff --git a/app/models/concerns/calloutable.rb b/app/models/concerns/calloutable.rb deleted file mode 100644 index 8b9cfae6a32..00000000000 --- a/app/models/concerns/calloutable.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module Calloutable - extend ActiveSupport::Concern - - included do - belongs_to :user - - validates :user, presence: true - end - - def dismissed_after?(dismissed_after) - dismissed_at > dismissed_after - end -end diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb index a9589cea5e9..12ddbc2cc40 100644 --- a/app/models/concerns/ci/contextable.rb +++ b/app/models/concerns/ci/contextable.rb @@ -13,7 +13,6 @@ module Ci track_duration do variables = pipeline.variables_builder.scoped_variables(self, environment: environment, dependencies: dependencies) - variables.concat(predefined_variables) unless pipeline.predefined_vars_in_builder_enabled? variables.concat(project.predefined_variables) variables.concat(pipeline.predefined_variables) variables.concat(runner.predefined_variables) if runnable? && runner @@ -71,24 +70,6 @@ module Ci end end - def predefined_variables - Gitlab::Ci::Variables::Collection.new.tap do |variables| - variables.append(key: 'CI_JOB_NAME', value: name) - variables.append(key: 'CI_JOB_STAGE', value: stage) - variables.append(key: 'CI_JOB_MANUAL', value: 'true') if action? - variables.append(key: 'CI_PIPELINE_TRIGGERED', value: 'true') if trigger_request - - variables.append(key: 'CI_NODE_INDEX', value: self.options[:instance].to_s) if self.options&.include?(:instance) - variables.append(key: 'CI_NODE_TOTAL', value: ci_node_total_value.to_s) - - # legacy variables - variables.append(key: 'CI_BUILD_NAME', value: name) - variables.append(key: 'CI_BUILD_STAGE', value: stage) - variables.append(key: 'CI_BUILD_TRIGGERED', value: 'true') if trigger_request - variables.append(key: 'CI_BUILD_MANUAL', value: 'true') if action? - end - end - def kubernetes_variables ::Gitlab::Ci::Variables::Collection.new.tap do |collection| # Should get merged with the cluster kubeconfig in deployment_variables, see @@ -123,13 +104,5 @@ module Ci def secret_project_variables(environment: expanded_environment_name) project.ci_variables_for(ref: git_ref, environment: environment) end - - private - - def ci_node_total_value - parallel = self.options&.dig(:parallel) - parallel = parallel.dig(:total) if parallel.is_a?(Hash) - parallel || 1 - end end end diff --git a/app/models/concerns/commit_signature.rb b/app/models/concerns/commit_signature.rb new file mode 100644 index 00000000000..5bdfa9a2966 --- /dev/null +++ b/app/models/concerns/commit_signature.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true +module CommitSignature + extend ActiveSupport::Concern + + included do + include ShaAttribute + + sha_attribute :commit_sha + + enum verification_status: { + unverified: 0, + verified: 1, + same_user_different_email: 2, + other_user: 3, + unverified_key: 4, + unknown_key: 5, + multiple_signatures: 6 + } + + belongs_to :project, class_name: 'Project', foreign_key: 'project_id', optional: false + + validates :commit_sha, presence: true + validates :project_id, presence: true + + scope :by_commit_sha, ->(shas) { where(commit_sha: shas) } + end + + class_methods do + def safe_create!(attributes) + create_with(attributes) + .safe_find_or_create_by!(commit_sha: attributes[:commit_sha]) + end + + # Find commits that are lacking a signature in the database at present + def unsigned_commit_shas(commit_shas) + return [] if commit_shas.empty? + + signed = by_commit_sha(commit_shas).pluck(:commit_sha) + commit_shas - signed + end + end + + def commit + project.commit(commit_sha) + end + + def user + commit.committer + end +end diff --git a/app/models/concerns/diff_positionable_note.rb b/app/models/concerns/diff_positionable_note.rb index b13ca4bf06e..051158e5de5 100644 --- a/app/models/concerns/diff_positionable_note.rb +++ b/app/models/concerns/diff_positionable_note.rb @@ -3,7 +3,6 @@ module DiffPositionableNote extend ActiveSupport::Concern included do - delegate :on_text?, :on_image?, to: :position, allow_nil: true before_validation :set_original_position, on: :create before_validation :update_position, on: :create, if: :on_text?, unless: :importing? @@ -34,6 +33,14 @@ module DiffPositionableNote end end + def on_text? + !!position&.on_text? + end + + def on_image? + !!position&.on_image? + end + def supported? for_commit? || self.noteable.has_complete_diff_refs? end diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb index 1b4cc14f4a2..312b88a4d6d 100644 --- a/app/models/concerns/enums/ci/commit_status.rb +++ b/app/models/concerns/enums/ci/commit_status.rb @@ -28,6 +28,7 @@ module Enums trace_size_exceeded: 19, builds_disabled: 20, environment_creation_failure: 21, + deployment_rejected: 22, insufficient_bridge_permissions: 1_001, downstream_bridge_project_not_found: 1_002, invalid_bridge_trigger: 1_003, diff --git a/app/models/concerns/import_state/sidekiq_job_tracker.rb b/app/models/concerns/import_state/sidekiq_job_tracker.rb index b7d0ed0f51b..340bf4279bc 100644 --- a/app/models/concerns/import_state/sidekiq_job_tracker.rb +++ b/app/models/concerns/import_state/sidekiq_job_tracker.rb @@ -15,7 +15,7 @@ module ImportState def refresh_jid_expiration return unless jid - Gitlab::SidekiqStatus.set(jid, Gitlab::Import::StuckImportJob::IMPORT_JOBS_EXPIRATION) + Gitlab::SidekiqStatus.set(jid, Gitlab::Import::StuckImportJob::IMPORT_JOBS_EXPIRATION, value: 2) end def self.jid_by(project_id:, status:) diff --git a/app/models/concerns/incident_management/escalatable.rb b/app/models/concerns/incident_management/escalatable.rb index 78dce63f59e..81eef50603a 100644 --- a/app/models/concerns/incident_management/escalatable.rb +++ b/app/models/concerns/incident_management/escalatable.rb @@ -102,3 +102,5 @@ module IncidentManagement end end end + +::IncidentManagement::Escalatable.prepend_mod diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 4273eb331a1..dcd80201d3f 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -43,7 +43,7 @@ module Issuable included do cache_markdown_field :title, pipeline: :single_line - cache_markdown_field :description, issuable_state_filter_enabled: true + cache_markdown_field :description, issuable_reference_expansion_enabled: true redact_field :description @@ -61,6 +61,16 @@ module Issuable # We check first if we're loaded to not load unnecessarily. loaded? && to_a.all? { |note| note.association(:award_emoji).loaded? } end + + def projects_loaded? + # We check first if we're loaded to not load unnecessarily. + loaded? && to_a.all? { |note| note.association(:project).loaded? } + end + + def system_note_metadata_loaded? + # We check first if we're loaded to not load unnecessarily. + loaded? && to_a.all? { |note| note.association(:system_note_metadata).loaded? } + end end has_many :note_authors, -> { distinct }, through: :notes, source: :author @@ -183,6 +193,10 @@ module Issuable incident? end + def supports_escalation? + incident? + end + def incident? is_a?(Issue) && super end @@ -524,6 +538,8 @@ module Issuable includes = [] includes << :author unless notes.authors_loaded? includes << :award_emoji unless notes.award_emojis_loaded? + includes << :project unless notes.projects_loaded? + includes << :system_note_metadata unless notes.system_note_metadata_loaded? if includes.any? notes.includes(includes) diff --git a/app/models/concerns/loose_foreign_key.rb b/app/models/concerns/loose_foreign_key.rb deleted file mode 100644 index 102292672b3..00000000000 --- a/app/models/concerns/loose_foreign_key.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -module LooseForeignKey - extend ActiveSupport::Concern - - # This concern adds loose foreign key support to ActiveRecord models. - # Loose foreign keys allow delayed processing of associated database records - # with similar guarantees than a database foreign key. - # - # Prerequisites: - # - # To start using the concern, you'll need to install a database trigger to the parent - # table in a standard DB migration (not post-migration). - # - # > track_record_deletions(:projects) - # - # Usage: - # - # > class Ci::Build < ApplicationRecord - # > - # > loose_foreign_key :security_scans, :build_id, on_delete: :async_delete - # > - # > # associations can be still defined, the dependent options is no longer necessary: - # > has_many :security_scans, class_name: 'Security::Scan' - # > - # > end - # - # Options for on_delete: - # - # - :async_delete - deletes the children rows via an asynchronous process. - # - :async_nullify - sets the foreign key column to null via an asynchronous process. - # - # How it works: - # - # When adding loose foreign key support to the table, a DELETE trigger is installed - # which tracks the record deletions (stores primary key value of the deleted row) in - # a database table. - # - # These deletion records are processed asynchronously and records are cleaned up - # according to the loose foreign key definitions described in the model. - # - # The cleanup happens in batches, which reduces the likelyhood of statement timeouts. - # - # When all associations related to the deleted record are cleaned up, the record itself - # is deleted. - included do - class_attribute :loose_foreign_key_definitions, default: [] - end - - class_methods do - def loose_foreign_key(to_table, column, options) - symbolized_options = options.symbolize_keys - - unless base_class? - raise <<~MSG - loose_foreign_key can be only used on base classes, inherited classes are not supported. - Please define the loose_foreign_key on the #{base_class.name} class. - MSG - end - - on_delete_options = %i[async_delete async_nullify] - - unless on_delete_options.include?(symbolized_options[:on_delete]&.to_sym) - raise "Invalid on_delete option given: #{symbolized_options[:on_delete]}. Valid options: #{on_delete_options.join(', ')}" - end - - definition = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new( - table_name.to_s, - to_table.to_s, - { - column: column.to_s, - on_delete: symbolized_options[:on_delete].to_sym - } - ) - - self.loose_foreign_key_definitions += [definition] - end - end -end diff --git a/app/models/concerns/merge_request_reviewer_state.rb b/app/models/concerns/merge_request_reviewer_state.rb index 216a3a0bd64..5859f43a70c 100644 --- a/app/models/concerns/merge_request_reviewer_state.rb +++ b/app/models/concerns/merge_request_reviewer_state.rb @@ -15,11 +15,5 @@ module MergeRequestReviewerState inclusion: { in: self.states.keys } after_initialize :set_state, unless: :persisted? - - def set_state - if Feature.enabled?(:mr_attention_requests, self.merge_request&.project, default_enabled: :yaml) - self.state = :attention_requested - end - end end end diff --git a/app/models/concerns/packages/debian/component_file.rb b/app/models/concerns/packages/debian/component_file.rb index 9cf66c756a0..77409549e85 100644 --- a/app/models/concerns/packages/debian/component_file.rb +++ b/app/models/concerns/packages/debian/component_file.rb @@ -20,13 +20,13 @@ module Packages belongs_to :component, class_name: "Packages::Debian::#{container_type.capitalize}Component", inverse_of: :files belongs_to :architecture, class_name: "Packages::Debian::#{container_type.capitalize}Architecture", inverse_of: :files, optional: true - enum file_type: { packages: 1, source: 2, di_packages: 3 } + enum file_type: { packages: 1, sources: 2, di_packages: 3 } enum compression_type: { gz: 1, bz2: 2, xz: 3 } validates :component, presence: true validates :file_type, presence: true - validates :architecture, presence: true, unless: :source? - validates :architecture, absence: true, if: :source? + validates :architecture, presence: true, unless: :sources? + validates :architecture, absence: true, if: :sources? validates :file, length: { minimum: 0, allow_nil: false } validates :size, presence: true validates :file_store, presence: true @@ -81,7 +81,7 @@ module Packages case file_type when 'packages' "#{component.name}/binary-#{architecture.name}/#{file_name}#{extension}" - when 'source' + when 'sources' "#{component.name}/source/#{file_name}#{extension}" when 'di_packages' "#{component.name}/debian-installer/binary-#{architecture.name}/#{file_name}#{extension}" diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index 25410a859e9..1663aa6c886 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -60,6 +60,15 @@ module Participable filtered_participants_hash[user] end + # Returns only participants visible for the user + # + # Returns an Array of User instances. + def visible_participants(user) + return participants(user) unless Feature.enabled?(:verify_participants_access, project, default_enabled: :yaml) + + filter_by_ability(raw_participants(user, verify_access: true)) + end + # Checks if the user is a participant in a discussion. # # This method processes attributes of objects in breadth-first order. @@ -84,8 +93,7 @@ module Participable end end - def raw_participants(current_user = nil) - current_user ||= author + def raw_participants(current_user = nil, verify_access: false) ext = Gitlab::ReferenceExtractor.new(project, current_user) participants = Set.new process = [self] @@ -97,6 +105,8 @@ module Participable when User participants << source when Participable + next unless !verify_access || source_visible_to_user?(source, current_user) + source.class.participant_attrs.each do |attr| if attr.respond_to?(:call) source.instance_exec(current_user, ext, &attr) @@ -116,6 +126,10 @@ module Participable participants.merge(ext.users) end + def source_visible_to_user?(source, user) + Ability.allowed?(user, "read_#{source.model_name.element}".to_sym, source) + end + def filter_by_ability(participants) case self when PersonalSnippet diff --git a/app/models/concerns/partitioned_table.rb b/app/models/concerns/partitioned_table.rb index 23d2d00b346..f95f9dd8ad7 100644 --- a/app/models/concerns/partitioned_table.rb +++ b/app/models/concerns/partitioned_table.rb @@ -7,7 +7,8 @@ module PartitionedTable attr_reader :partitioning_strategy PARTITIONING_STRATEGIES = { - monthly: Gitlab::Database::Partitioning::MonthlyStrategy + monthly: Gitlab::Database::Partitioning::MonthlyStrategy, + sliding_list: Gitlab::Database::Partitioning::SlidingListStrategy }.freeze def partitioned_by(partitioning_key, strategy:, **kwargs) diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index c32e499c329..9069d3088cd 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -168,6 +168,24 @@ module RelativePositioning self.relative_position = MIN_POSITION end + def next_object_by_relative_position(ignoring: nil, order: :asc) + relation = relative_positioning_scoped_items(ignoring: ignoring).reorder(relative_position: order) + + relation = if order == :asc + relation.where(self.class.arel_table[:relative_position].gt(relative_position)) + else + relation.where(self.class.arel_table[:relative_position].lt(relative_position)) + end + + relation.first + end + + def relative_positioning_scoped_items(ignoring: nil) + relation = self.class.relative_positioning_query_base(self) + relation = exclude_self(relation, excluded: ignoring) if ignoring.present? + relation + end + # This method is used during rebalancing - override it to customise the update # logic: def update_relative_siblings(relation, range, delta) diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb index 60e1dde17b9..aae338e9759 100644 --- a/app/models/concerns/resolvable_discussion.rb +++ b/app/models/concerns/resolvable_discussion.rb @@ -30,11 +30,14 @@ module ResolvableDiscussion delegate :resolved_at, :resolved_by, - :resolved_by_push?, to: :last_resolved_note, allow_nil: true end + def resolved_by_push? + !!last_resolved_note&.resolved_by_push? + end + def resolvable? strong_memoize(:resolvable) do potentially_resolvable? && notes.any?(&:resolvable?) diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index ba7c6c0cd8b..e49f4d03bda 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -3,11 +3,14 @@ module ShaAttribute extend ActiveSupport::Concern + # Needed for the database method + include DatabaseReflection + class_methods do def sha_attribute(name) return if ENV['STATIC_VERIFICATION'] - validate_binary_column_exists!(name) if Rails.env.development? + validate_binary_column_exists!(name) if Rails.env.development? || Rails.env.test? attribute(name, Gitlab::Database::ShaAttribute.new) end diff --git a/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb b/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb index 3be82ed72d3..447521ad8c1 100644 --- a/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb +++ b/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb @@ -11,7 +11,7 @@ module TokenAuthenticatableStrategies # The pattern of the token is "#{DYNAMIC_NONCE_IDENTIFIER}#{token}#{iv_of_12_characters}" if token.start_with?(DYNAMIC_NONCE_IDENTIFIER) && token.size > NONCE_SIZE + DYNAMIC_NONCE_IDENTIFIER.size token_to_decrypt = token[1...-NONCE_SIZE] - iv = token[-NONCE_SIZE..-1] + iv = token[-NONCE_SIZE..] Gitlab::CryptoHelper.aes256_gcm_decrypt(token_to_decrypt, nonce: iv) else diff --git a/app/models/concerns/transactions.rb b/app/models/concerns/transactions.rb index a186ebc8475..1c9bd8274f5 100644 --- a/app/models/concerns/transactions.rb +++ b/app/models/concerns/transactions.rb @@ -8,7 +8,7 @@ module Transactions # transaction. Handles special cases when running inside a test environment, # where tests may be wrapped in transactions def inside_transaction? - base = Rails.env.test? ? @open_transactions_baseline.to_i : 0 + base = Rails.env.test? ? open_transactions_baseline.to_i : 0 connection.open_transactions > base end @@ -24,5 +24,15 @@ module Transactions def reset_open_transactions_baseline @open_transactions_baseline = 0 end + + def open_transactions_baseline + return unless Rails.env.test? + + if @open_transactions_baseline.nil? + return self == ApplicationRecord ? nil : superclass.open_transactions_baseline + end + + @open_transactions_baseline + end end end |