summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/analytics/instance_statistics.rb9
-rw-r--r--app/models/analytics/usage_trends/measurement.rb (renamed from app/models/analytics/instance_statistics/measurement.rb)6
-rw-r--r--app/models/bulk_imports/entity.rb5
-rw-r--r--app/models/ci/build.rb5
-rw-r--r--app/models/clusters/agent_token.rb4
-rw-r--r--app/models/concerns/project_features_compatibility.rb8
-rw-r--r--app/models/custom_emoji.rb2
-rw-r--r--app/models/group.rb26
-rw-r--r--app/models/iteration.rb28
-rw-r--r--app/models/iterations/cadence.rb14
-rw-r--r--app/models/list.rb1
-rw-r--r--app/models/merge_request.rb17
-rw-r--r--app/models/namespace.rb8
-rw-r--r--app/models/notification_setting.rb3
-rw-r--r--app/models/packages/nuget.rb2
-rw-r--r--app/models/packages/package.rb14
-rw-r--r--app/models/packages/rubygems.rb10
-rw-r--r--app/models/packages/rubygems/metadatum.rb1
-rw-r--r--app/models/pages/lookup_path.rb2
-rw-r--r--app/models/project.rb7
-rw-r--r--app/models/project_feature.rb22
-rw-r--r--app/models/project_services/jira_service.rb74
-rw-r--r--app/models/project_services/mattermost_service.rb2
-rw-r--r--app/models/project_services/slack_mattermost/notifier.rb24
-rw-r--r--app/models/project_services/slack_service.rb25
-rw-r--r--app/models/repository.rb14
-rw-r--r--app/models/snippet.rb6
-rw-r--r--app/models/user.rb1
28 files changed, 239 insertions, 101 deletions
diff --git a/app/models/analytics/instance_statistics.rb b/app/models/analytics/instance_statistics.rb
deleted file mode 100644
index df7b26e4fa6..00000000000
--- a/app/models/analytics/instance_statistics.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module Analytics
- module InstanceStatistics
- def self.table_name_prefix
- 'analytics_instance_statistics_'
- end
- end
-end
diff --git a/app/models/analytics/instance_statistics/measurement.rb b/app/models/analytics/usage_trends/measurement.rb
index c8b76e005ef..40d7c7fae2e 100644
--- a/app/models/analytics/instance_statistics/measurement.rb
+++ b/app/models/analytics/usage_trends/measurement.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
module Analytics
- module InstanceStatistics
+ module UsageTrends
class Measurement < ApplicationRecord
+ self.table_name = 'analytics_instance_statistics_measurements'
+
EXPERIMENTAL_IDENTIFIERS = %i[pipelines_succeeded pipelines_failed pipelines_canceled pipelines_skipped].freeze
enum identifier: {
@@ -58,4 +60,4 @@ module Analytics
end
end
-Analytics::InstanceStatistics::Measurement.prepend_if_ee('EE::Analytics::InstanceStatistics::Measurement')
+Analytics::UsageTrends::Measurement.prepend_if_ee('EE::Analytics::UsageTrends::Measurement')
diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb
index 16224fde502..ae1e3693809 100644
--- a/app/models/bulk_imports/entity.rb
+++ b/app/models/bulk_imports/entity.rb
@@ -37,8 +37,9 @@ class BulkImports::Entity < ApplicationRecord
validates :project, absence: true, if: :group
validates :group, absence: true, if: :project
- validates :source_type, :source_full_path, :destination_name,
- :destination_namespace, presence: true
+ validates :source_type, :source_full_path, :destination_name, presence: true
+ validates :destination_namespace, exclusion: [nil], if: :group
+ validates :destination_namespace, presence: true, if: :project
validate :validate_parent_is_a_group, if: :parent
validate :validate_imported_entity_type
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index db151126caf..d072bced639 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -564,7 +564,10 @@ module Ci
end
def features
- { trace_sections: true }
+ {
+ trace_sections: true,
+ failure_reasons: self.class.failure_reasons.keys
+ }
end
def merge_request
diff --git a/app/models/clusters/agent_token.rb b/app/models/clusters/agent_token.rb
index b260822f784..8266f40133d 100644
--- a/app/models/clusters/agent_token.rb
+++ b/app/models/clusters/agent_token.rb
@@ -7,9 +7,11 @@ module Clusters
self.table_name = 'cluster_agent_tokens'
- belongs_to :agent, class_name: 'Clusters::Agent'
+ belongs_to :agent, class_name: 'Clusters::Agent', optional: false
belongs_to :created_by_user, class_name: 'User', optional: true
before_save :ensure_token
+
+ validates :description, length: { maximum: 1024 }
end
end
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 07bec07e556..a06bfdf5825 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -34,6 +34,10 @@ module ProjectFeaturesCompatibility
write_feature_attribute_boolean(:snippets_access_level, value)
end
+ def security_and_compliance_enabled=(value)
+ write_feature_attribute_boolean(:security_and_compliance_access_level, value)
+ end
+
def repository_access_level=(value)
write_feature_attribute_string(:repository_access_level, value)
end
@@ -78,6 +82,10 @@ module ProjectFeaturesCompatibility
write_feature_attribute_string(:operations_access_level, value)
end
+ def security_and_compliance_access_level=(value)
+ write_feature_attribute_string(:security_and_compliance_access_level, value)
+ end
+
private
def write_feature_attribute_boolean(field, value)
diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index f4c914c6a3a..aea48a5ec20 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -6,6 +6,7 @@ class CustomEmoji < ApplicationRecord
belongs_to :namespace, inverse_of: :custom_emoji
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
+ belongs_to :creator, class_name: "User", inverse_of: :created_custom_emoji
# For now only external emoji are supported. See https://gitlab.com/gitlab-org/gitlab/-/issues/230467
validates :external, inclusion: { in: [true] }
@@ -15,6 +16,7 @@ class CustomEmoji < ApplicationRecord
validate :valid_emoji_name
validates :group, presence: true
+ validates :creator, presence: true
validates :name,
uniqueness: { scope: [:namespace_id, :name] },
presence: true,
diff --git a/app/models/group.rb b/app/models/group.rb
index 1eaa4499eb5..afcf3fe69e6 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -34,6 +34,7 @@ class Group < Namespace
has_many :milestones
has_many :iterations
+ has_many :iterations_cadences, class_name: 'Iterations::Cadence'
has_many :services
has_many :shared_group_links, foreign_key: :shared_with_group_id, class_name: 'GroupGroupLink'
has_many :shared_with_group_links, foreign_key: :shared_group_id, class_name: 'GroupGroupLink'
@@ -364,13 +365,28 @@ class Group < Namespace
# rubocop: enable CodeReuse/ServiceClass
# rubocop: disable CodeReuse/ServiceClass
- def refresh_members_authorized_projects(blocking: true, priority: UserProjectAccessChangedService::HIGH_PRIORITY)
+ def refresh_members_authorized_projects(
+ blocking: true,
+ priority: UserProjectAccessChangedService::HIGH_PRIORITY,
+ direct_members_only: false
+ )
+
+ user_ids = if direct_members_only
+ users_ids_of_direct_members
+ else
+ user_ids_for_project_authorizations
+ end
+
UserProjectAccessChangedService
- .new(user_ids_for_project_authorizations)
+ .new(user_ids)
.execute(blocking: blocking, priority: priority)
end
# rubocop: enable CodeReuse/ServiceClass
+ def users_ids_of_direct_members
+ direct_members.pluck(:user_id)
+ end
+
def user_ids_for_project_authorizations
members_with_parents.pluck(:user_id)
end
@@ -381,6 +397,12 @@ class Group < Namespace
end
end
+ def direct_members
+ GroupMember.active_without_invites_and_requests
+ .non_minimal_access
+ .where(source_id: id)
+ end
+
def members_with_parents
# Avoids an unnecessary SELECT when the group has no parents
source_ids =
diff --git a/app/models/iteration.rb b/app/models/iteration.rb
index 7a35bb1cd1f..012a062712f 100644
--- a/app/models/iteration.rb
+++ b/app/models/iteration.rb
@@ -16,6 +16,7 @@ class Iteration < ApplicationRecord
belongs_to :project
belongs_to :group
+ belongs_to :iterations_cadence, class_name: 'Iterations::Cadence', foreign_key: :iterations_cadence_id, inverse_of: :iterations
has_internal_id :iid, scope: :project
has_internal_id :iid, scope: :group
@@ -26,6 +27,9 @@ class Iteration < ApplicationRecord
validate :dates_do_not_overlap, if: :start_or_due_dates_changed?
validate :future_date, if: :start_or_due_dates_changed?, unless: :skip_future_date_validation
validate :no_project, unless: :skip_project_validation
+ validate :validate_group
+
+ before_create :set_iterations_cadence
scope :upcoming, -> { with_state(:upcoming) }
scope :started, -> { with_state(:started) }
@@ -135,6 +139,30 @@ class Iteration < ApplicationRecord
errors.add(:project_id, s_("is not allowed. We do not currently support project-level iterations"))
end
+
+ # TODO: this method should be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296099
+ def set_iterations_cadence
+ return if iterations_cadence
+ # For now we support only group iterations
+ # issue to clarify project iterations: https://gitlab.com/gitlab-org/gitlab/-/issues/299864
+ return unless group
+
+ self.iterations_cadence = group.iterations_cadences.first || create_default_cadence
+ end
+
+ def create_default_cadence
+ cadence_title = "#{group.name} Iterations"
+
+ Iterations::Cadence.create!(group: group, title: cadence_title, start_date: start_date)
+ end
+
+ # TODO: remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296100
+ def validate_group
+ return unless iterations_cadence
+ return if iterations_cadence.group_id == group_id
+
+ errors.add(:group, s_('is not valid. The iteration group has to match the iteration cadence group.'))
+ end
end
Iteration.prepend_if_ee('EE::Iteration')
diff --git a/app/models/iterations/cadence.rb b/app/models/iterations/cadence.rb
new file mode 100644
index 00000000000..4f8e148d18f
--- /dev/null
+++ b/app/models/iterations/cadence.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class Iterations::Cadence < ApplicationRecord
+ self.table_name = 'iterations_cadences'
+
+ belongs_to :group
+ has_many :iterations, foreign_key: :iterations_cadence_id, inverse_of: :iterations_cadence
+
+ validates :title, presence: true
+ validates :start_date, presence: true
+ validates :group_id, presence: true
+ validates :active, presence: true
+ validates :automatic, presence: true
+end
diff --git a/app/models/list.rb b/app/models/list.rb
index 49834af3dfb..5bd00a1d7ef 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -14,6 +14,7 @@ class List < ApplicationRecord
validates :label_id, uniqueness: { scope: :board_id }, if: :label?
scope :preload_associated_models, -> { preload(:board, label: :priorities) }
+ scope :without_types, ->(list_types) { where.not(list_type: list_types) }
alias_method :preferences, :list_user_preferences
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 1374e8a814a..8d558098d94 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -317,6 +317,8 @@ class MergeRequest < ApplicationRecord
scope :preload_author, -> { preload(:author) }
scope :preload_approved_by_users, -> { preload(:approved_by_users) }
scope :preload_metrics, -> (relation) { preload(metrics: relation) }
+ scope :preload_project_and_latest_diff, -> { preload(:source_project, :latest_merge_request_diff) }
+ scope :preload_latest_diff_comment, -> { preload(latest_merge_request_diff: :merge_request_diff_commits) }
scope :with_web_entity_associations, -> { preload(:author, :target_project) }
scope :with_auto_merge_enabled, -> do
@@ -374,8 +376,7 @@ class MergeRequest < ApplicationRecord
alias_attribute :auto_merge_enabled, :merge_when_pipeline_succeeds
alias_method :issuing_parent, :target_project
- delegate :active?, :builds_with_coverage, to: :head_pipeline, prefix: true, allow_nil: true
- delegate :success?, :active?, to: :actual_head_pipeline, prefix: true, allow_nil: true
+ delegate :builds_with_coverage, to: :head_pipeline, prefix: true, allow_nil: true
RebaseLockTimeout = Class.new(StandardError)
@@ -435,6 +436,18 @@ class MergeRequest < ApplicationRecord
target_project.latest_pipeline(target_branch, sha)
end
+ def head_pipeline_active?
+ !!head_pipeline&.active?
+ end
+
+ def actual_head_pipeline_active?
+ !!actual_head_pipeline&.active?
+ end
+
+ def actual_head_pipeline_success?
+ !!actual_head_pipeline&.success?
+ end
+
# Pattern used to extract `!123` merge request references from text
#
# This pattern supports cross-project references.
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 3342fb1fce9..21e8a6fec74 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -164,6 +164,10 @@ class Namespace < ApplicationRecord
name = host.delete_suffix(gitlab_host)
Namespace.where(parent_id: nil).by_path(name)
end
+
+ def top_most
+ where(parent_id: nil)
+ end
end
def package_settings
@@ -400,6 +404,10 @@ class Namespace < ApplicationRecord
!has_parent?
end
+ def recent?
+ created_at >= 90.days.ago
+ end
+
private
def all_projects_with_pages
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 82e39e4f207..72813b17501 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -49,7 +49,8 @@ class NotificationSetting < ApplicationRecord
:failed_pipeline,
:fixed_pipeline,
:success_pipeline,
- :moved_project
+ :moved_project,
+ :merge_when_pipeline_succeeds
].freeze
def self.email_events(source = nil)
diff --git a/app/models/packages/nuget.rb b/app/models/packages/nuget.rb
index 42c167e9b7f..f152eedb8fc 100644
--- a/app/models/packages/nuget.rb
+++ b/app/models/packages/nuget.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module Packages
module Nuget
+ TEMPORARY_PACKAGE_NAME = 'NuGet.Temporary.Package'
+
def self.table_name_prefix
'packages_nuget_'
end
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 391540634be..b0e7ff09982 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -40,11 +40,11 @@ class Packages::Package < ApplicationRecord
validate :unique_debian_package_name, if: :debian_package?
validate :valid_conan_package_recipe, if: :conan?
- validate :valid_npm_package_name, if: :npm?
validate :valid_composer_global_name, if: :composer?
validate :package_already_taken, if: :npm?
validates :name, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :name, format: { with: Gitlab::Regex.generic_package_name_regex }, if: :generic?
+ validates :name, format: { with: Gitlab::Regex.npm_package_name_regex }, if: :npm?
validates :name, format: { with: Gitlab::Regex.nuget_package_name_regex }, if: :nuget?
validates :name, format: { with: Gitlab::Regex.debian_package_name_regex }, if: :debian_package?
validates :name, inclusion: { in: %w[incoming] }, if: :debian_incoming?
@@ -98,12 +98,12 @@ class Packages::Package < ApplicationRecord
end
scope :preload_composer, -> { preload(:composer_metadatum) }
- scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
+ scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }
scope :has_version, -> { where.not(version: nil) }
scope :processed, -> do
where.not(package_type: :nuget).or(
- where.not(name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME)
+ where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME)
)
end
scope :preload_files, -> { preload(:package_files) }
@@ -247,14 +247,6 @@ class Packages::Package < ApplicationRecord
end
end
- def valid_npm_package_name
- return unless project&.root_namespace
-
- unless name =~ %r{\A@#{project.root_namespace.path}/[^/]+\z}
- errors.add(:name, 'is not valid')
- end
- end
-
def package_already_taken
return unless project
diff --git a/app/models/packages/rubygems.rb b/app/models/packages/rubygems.rb
new file mode 100644
index 00000000000..1aa6b16f47e
--- /dev/null
+++ b/app/models/packages/rubygems.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+module Packages
+ module Rubygems
+ TEMPORARY_PACKAGE_NAME = 'Gem.Temporary.Package'
+
+ def self.table_name_prefix
+ 'packages_rubygems_'
+ end
+ end
+end
diff --git a/app/models/packages/rubygems/metadatum.rb b/app/models/packages/rubygems/metadatum.rb
index 42db1f3defc..d4e5feb7c98 100644
--- a/app/models/packages/rubygems/metadatum.rb
+++ b/app/models/packages/rubygems/metadatum.rb
@@ -3,7 +3,6 @@
module Packages
module Rubygems
class Metadatum < ApplicationRecord
- self.table_name = 'packages_rubygems_metadata'
self.primary_key = :package_id
belongs_to :package, -> { where(package_type: :rubygems) }, inverse_of: :rubygems_metadatum
diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb
index c6781f8f6e3..46c347e5cf5 100644
--- a/app/models/pages/lookup_path.rb
+++ b/app/models/pages/lookup_path.rb
@@ -52,7 +52,7 @@ module Pages
def zip_source
return unless deployment&.file
- return if deployment.file.file_storage? && !Feature.enabled?(:pages_serve_with_zip_file_protocol, project)
+ return if deployment.file.file_storage? && !Feature.enabled?(:pages_serve_with_zip_file_protocol, project, default_enabled: true)
return if deployment.migrated? && !Feature.enabled?(:pages_serve_from_migrated_zip, project)
diff --git a/app/models/project.rb b/app/models/project.rb
index 2b9b7dcf733..08e3203a81e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -392,7 +392,8 @@ class Project < ApplicationRecord
:merge_requests_access_level, :forking_access_level, :issues_access_level,
:wiki_access_level, :snippets_access_level, :builds_access_level,
:repository_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
- :operations_enabled?, :operations_access_level, to: :project_feature, allow_nil: true
+ :operations_enabled?, :operations_access_level, :security_and_compliance_access_level,
+ to: :project_feature, allow_nil: true
delegate :show_default_award_emojis, :show_default_award_emojis=,
:show_default_award_emojis?,
to: :project_setting, allow_nil: true
@@ -2532,6 +2533,10 @@ class Project < ApplicationRecord
Projects::GitGarbageCollectWorker
end
+ def inherited_issuable_templates_enabled?
+ Feature.enabled?(:inherited_issuable_templates, self, default_enabled: :yaml)
+ end
+
private
def find_service(services, name)
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 7b204cfb1c0..3f86a1ce979 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -3,7 +3,8 @@
class ProjectFeature < ApplicationRecord
include Featurable
- FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations).freeze
+ FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations security_and_compliance).freeze
+ EXPORTABLE_FEATURES = (FEATURES - [:security_and_compliance]).freeze
set_available_features(FEATURES)
@@ -37,16 +38,17 @@ class ProjectFeature < ApplicationRecord
validate :repository_children_level
validate :allowed_access_levels
- default_value_for :builds_access_level, value: ENABLED, allows_nil: false
- default_value_for :issues_access_level, value: ENABLED, allows_nil: false
- default_value_for :forking_access_level, value: ENABLED, allows_nil: false
- default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false
- default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
- default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
- default_value_for :repository_access_level, value: ENABLED, allows_nil: false
- default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
+ default_value_for :builds_access_level, value: ENABLED, allows_nil: false
+ default_value_for :issues_access_level, value: ENABLED, allows_nil: false
+ default_value_for :forking_access_level, value: ENABLED, allows_nil: false
+ default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false
+ default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
+ default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
+ default_value_for :repository_access_level, value: ENABLED, allows_nil: false
+ default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
default_value_for :metrics_dashboard_access_level, value: PRIVATE, allows_nil: false
- default_value_for :operations_access_level, value: ENABLED, allows_nil: false
+ default_value_for :operations_access_level, value: ENABLED, allows_nil: false
+ default_value_for :security_and_compliance_access_level, value: PRIVATE, allows_nil: false
default_value_for(:pages_access_level, allows_nil: false) do |feature|
if ::Gitlab::Pages.access_control_is_forced?
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 5857d86f921..12f7bac23e4 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -124,15 +124,11 @@ class JiraService < IssueTrackerService
end
def fields
- transition_id_help_path = help_page_path('user/project/integrations/jira', anchor: 'obtaining-a-transition-id')
- transition_id_help_link_start = '<a href="%{transition_id_help_path}" target="_blank" rel="noopener noreferrer">'.html_safe % { transition_id_help_path: transition_id_help_path }
-
[
{ type: 'text', name: 'url', title: s_('JiraService|Web URL'), placeholder: 'https://jira.example.com', required: true },
{ type: 'text', name: 'api_url', title: s_('JiraService|Jira API URL'), placeholder: s_('JiraService|If different from Web URL') },
{ type: 'text', name: 'username', title: s_('JiraService|Username or Email'), placeholder: s_('JiraService|Use a username for server version and an email for cloud version'), required: true },
- { type: 'password', name: 'password', title: s_('JiraService|Password or API token'), placeholder: s_('JiraService|Use a password for server version and an API token for cloud version'), required: true },
- { type: 'text', name: 'jira_issue_transition_id', title: s_('JiraService|Jira workflow transition IDs'), placeholder: s_('JiraService|For example, 12, 24'), help: s_('JiraService|Set transition IDs for Jira workflow transitions. %{link_start}Learn more%{link_end}'.html_safe % { link_start: transition_id_help_link_start, link_end: '</a>'.html_safe }) }
+ { type: 'password', name: 'password', title: s_('JiraService|Password or API token'), placeholder: s_('JiraService|Use a password for server version and an API token for cloud version'), required: true }
]
end
@@ -159,17 +155,19 @@ class JiraService < IssueTrackerService
# support any events.
end
- def find_issue(issue_key, rendered_fields: false)
- options = {}
- options = options.merge(expand: 'renderedFields') if rendered_fields
+ def find_issue(issue_key, rendered_fields: false, transitions: false)
+ expands = []
+ expands << 'renderedFields' if rendered_fields
+ expands << 'transitions' if transitions
+ options = { expand: expands.join(',') } if expands.any?
- jira_request { client.Issue.find(issue_key, options) }
+ jira_request { client.Issue.find(issue_key, options || {}) }
end
def close_issue(entity, external_issue, current_user)
- issue = find_issue(external_issue.iid)
+ issue = find_issue(external_issue.iid, transitions: automatic_issue_transitions?)
- return if issue.nil? || has_resolution?(issue) || !jira_issue_transition_id.present?
+ return if issue.nil? || has_resolution?(issue)
commit_id = case entity
when Commit then entity.id
@@ -260,24 +258,52 @@ class JiraService < IssueTrackerService
end
end
+ def automatic_issue_transitions?
+ jira_issue_transition_id.blank?
+ end
+
# jira_issue_transition_id can have multiple values split by , or ;
# the issue is transitioned at the order given by the user
# if any transition fails it will log the error message and stop the transition sequence
def transition_issue(issue)
- jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).each do |transition_id|
- issue.transitions.build.save!(transition: { id: transition_id })
- rescue => error
- log_error(
- "Issue transition failed",
- error: {
- exception_class: error.class.name,
- exception_message: error.message,
- exception_backtrace: Gitlab::BacktraceCleaner.clean_backtrace(error.backtrace)
- },
- client_url: client_url
- )
- return false
+ return transition_issue_to_done(issue) if automatic_issue_transitions?
+
+ jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).all? do |transition_id|
+ transition_issue_to_id(issue, transition_id)
+ end
+ end
+
+ def transition_issue_to_id(issue, transition_id)
+ issue.transitions.build.save!(
+ transition: { id: transition_id }
+ )
+
+ true
+ rescue => error
+ log_error(
+ "Issue transition failed",
+ error: {
+ exception_class: error.class.name,
+ exception_message: error.message,
+ exception_backtrace: Gitlab::BacktraceCleaner.clean_backtrace(error.backtrace)
+ },
+ client_url: client_url
+ )
+
+ false
+ end
+
+ def transition_issue_to_done(issue)
+ transitions = issue.transitions rescue []
+
+ transition = transitions.find do |transition|
+ status = transition&.to&.statusCategory
+ status && status['key'] == 'done'
end
+
+ return false unless transition
+
+ transition_issue_to_id(issue, transition.id)
end
def log_usage(action, user)
diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb
index c1055db78e5..9cff979fcf2 100644
--- a/app/models/project_services/mattermost_service.rb
+++ b/app/models/project_services/mattermost_service.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class MattermostService < ChatNotificationService
- include ::SlackService::Notifier
+ include SlackMattermost::Notifier
def title
'Mattermost notifications'
diff --git a/app/models/project_services/slack_mattermost/notifier.rb b/app/models/project_services/slack_mattermost/notifier.rb
new file mode 100644
index 00000000000..1a78cea5933
--- /dev/null
+++ b/app/models/project_services/slack_mattermost/notifier.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module SlackMattermost
+ module Notifier
+ private
+
+ def notify(message, opts)
+ # See https://gitlab.com/gitlab-org/slack-notifier/#custom-http-client
+ notifier = Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
+ notifier.ping(
+ message.pretext,
+ attachments: message.attachments,
+ fallback: message.fallback
+ )
+ end
+
+ class HTTPClient
+ def self.post(uri, params = {})
+ params.delete(:http_options) # these are internal to the client and we do not want them
+ Gitlab::HTTP.post(uri, body: params)
+ end
+ end
+ end
+end
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 79245e84238..abe799bdff5 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class SlackService < ChatNotificationService
+ include SlackMattermost::Notifier
+
prop_accessor EVENT_CHANNEL['alert']
def title
@@ -35,27 +37,4 @@ class SlackService < ChatNotificationService
super
end
-
- module Notifier
- private
-
- def notify(message, opts)
- # See https://gitlab.com/gitlab-org/slack-notifier/#custom-http-client
- notifier = Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
- notifier.ping(
- message.pretext,
- attachments: message.attachments,
- fallback: message.fallback
- )
- end
-
- class HTTPClient
- def self.post(uri, params = {})
- params.delete(:http_options) # these are internal to the client and we do not want them
- Gitlab::HTTP.post(uri, body: params)
- end
- end
- end
-
- include Notifier
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 06a13194e1a..84ca8f0c12a 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -43,7 +43,7 @@ class Repository
changelog license_blob license_key gitignore
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref merged_branch_names
- has_visible_content? issue_template_names_by_category merge_request_template_names_by_category
+ has_visible_content? issue_template_names_hash merge_request_template_names_hash
user_defined_metrics_dashboard_paths xcode_project? has_ambiguous_refs?).freeze
# Methods that use cache_method but only memoize the value
@@ -60,8 +60,8 @@ class Repository
gitignore: :gitignore,
gitlab_ci: :gitlab_ci_yml,
avatar: :avatar,
- issue_template: :issue_template_names_by_category,
- merge_request_template: :merge_request_template_names_by_category,
+ issue_template: :issue_template_names_hash,
+ merge_request_template: :merge_request_template_names_hash,
metrics_dashboard: :user_defined_metrics_dashboard_paths,
xcode_config: :xcode_project?
}.freeze
@@ -573,15 +573,15 @@ class Repository
cache_method :avatar
# store issue_template_names as hash
- def issue_template_names_by_category
+ def issue_template_names_hash
Gitlab::Template::IssueTemplate.repository_template_names(project)
end
- cache_method :issue_template_names_by_category, fallback: {}
+ cache_method :issue_template_names_hash, fallback: {}
- def merge_request_template_names_by_category
+ def merge_request_template_names_hash
Gitlab::Template::MergeRequestTemplate.repository_template_names(project)
end
- cache_method :merge_request_template_names_by_category, fallback: {}
+ cache_method :merge_request_template_names_hash, fallback: {}
def user_defined_metrics_dashboard_paths
Gitlab::Metrics::Dashboard::RepoDashboardFinder.list_dashboards(project)
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index ab8782ed87f..b68e166af48 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -216,8 +216,10 @@ class Snippet < ApplicationRecord
def blobs
return [] unless repository_exists?
- branch = default_branch
- list_files(branch).map { |file| Blob.lazy(repository, branch, file) }
+ files = list_files(default_branch)
+ items = files.map { |file| [default_branch, file] }
+
+ repository.blobs_at(items).compact
end
def hook_attrs
diff --git a/app/models/user.rb b/app/models/user.rb
index 1f8b680c7e5..d91fc3ebce4 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -179,6 +179,7 @@ class User < ApplicationRecord
has_many :merge_request_reviewers, inverse_of: :reviewer
has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue
has_many :assigned_merge_requests, class_name: "MergeRequest", through: :merge_request_assignees, source: :merge_request
+ has_many :created_custom_emoji, class_name: 'CustomEmoji', inverse_of: :creator
has_many :bulk_imports