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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-07-31 15:07:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-07-31 15:07:02 +0300
commit74ecf758e30be848144df1672b5080a29fafbc0a (patch)
tree0bc719293526a2384b9db6638d42d49f1f5c9c9c
parent26d2324ac136c7a28235789ff9a0b2974b5f7c7b (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/CODEOWNERS2
-rw-r--r--.gitlab/issue_templates/Feature Proposal - lean.md7
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/controllers/projects/blob_controller.rb26
-rw-r--r--app/controllers/projects/performance_monitoring/dashboards_controller.rb114
-rw-r--r--app/controllers/search_controller.rb2
-rw-r--r--app/helpers/environments_helper.rb1
-rw-r--r--app/models/concerns/issuable_link.rb2
-rw-r--r--app/models/concerns/linkable_item.rb40
-rw-r--r--app/models/issue.rb24
-rw-r--r--app/models/issue_link.rb9
-rw-r--r--app/models/work_items/parent_link.rb11
-rw-r--r--app/models/work_items/related_work_item_link.rb21
-rw-r--r--app/services/issues/move_service.rb4
-rw-r--r--app/services/metrics/dashboard/clone_dashboard_service.rb170
-rw-r--r--app/services/metrics/dashboard/dynamic_embed_service.rb78
-rw-r--r--app/services/projects/prometheus/alerts/notify_service.rb41
-rw-r--r--app/services/search_service.rb4
-rw-r--r--config/routes/project.rb8
-rw-r--r--db/docs/issue_links.yml1
-rw-r--r--db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb9
-rw-r--r--db/post_migrate/20230724212040_add_temporary_indexes_for_orphaned_approval_rules.rb27
-rw-r--r--db/schema_migrations/202307241853211
-rw-r--r--db/schema_migrations/202307242120401
-rw-r--r--db/structure.sql5
-rw-r--r--doc/architecture/blueprints/remote_development/index.md34
-rw-r--r--doc/user/profile/notifications.md9
-rw-r--r--lib/api/entities/commit_status.rb1
-rw-r--r--lib/gitlab/alert_management/payload.rb17
-rw-r--r--lib/gitlab/alert_management/payload/managed_prometheus.rb46
-rw-r--r--lib/gitlab/metrics/dashboard/service_selector.rb1
-rw-r--r--lib/gitlab/metrics/global_search_slis.rb3
-rw-r--r--lib/gitlab/quick_actions/relate_actions.rb2
-rw-r--r--locale/gitlab.pot30
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock4
-rw-r--r--spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb287
-rw-r--r--spec/factories/work_items/related_work_item_links.rb8
-rw-r--r--spec/frontend/ide/components/ide_review_spec.js12
-rw-r--r--spec/lib/gitlab/alert_management/payload/managed_prometheus_spec.rb153
-rw-r--r--spec/lib/gitlab/alert_management/payload_spec.rb12
-rw-r--r--spec/lib/gitlab/metrics/dashboard/finder_spec.rb14
-rw-r--r--spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb27
-rw-r--r--spec/lib/gitlab/metrics/global_search_slis_spec.rb3
-rw-r--r--spec/mailers/emails/projects_spec.rb35
-rw-r--r--spec/models/issue_link_spec.rb51
-rw-r--r--spec/models/work_items/parent_link_spec.rb44
-rw-r--r--spec/models/work_items/related_work_item_link_spec.rb27
-rw-r--r--spec/requests/api/commit_statuses_spec.rb1
-rw-r--r--spec/requests/projects/blob_spec.rb87
-rw-r--r--spec/services/alert_management/process_prometheus_alert_service_spec.rb16
-rw-r--r--spec/services/metrics/dashboard/clone_dashboard_service_spec.rb190
-rw-r--r--spec/services/metrics/dashboard/dynamic_embed_service_spec.rb158
-rw-r--r--spec/services/projects/prometheus/alerts/notify_service_spec.rb85
-rw-r--r--spec/services/search_service_spec.rb6
-rw-r--r--spec/support/helpers/prometheus_helpers.rb22
-rw-r--r--spec/support/shared_examples/models/concerns/linkable_items_shared_examples.rb82
-rw-r--r--spec/support/shared_examples/models/issuable_link_shared_examples.rb7
62 files changed, 418 insertions, 1678 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index e1fc280bd1c..81b69b0e210 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -1452,7 +1452,7 @@ ee/lib/ee/api/entities/project.rb
/app/workers/releases/create_evidence_worker.rb
/app/workers/releases/manage_evidence_worker.rb
-[Fulfillment::Utilization] @sheldonled @aalakkad @kpalchyk
+^[Fulfillment::Utilization] @sheldonled @aalakkad @kpalchyk
/ee/app/assets/javascripts/usage_quotas/components/
/ee/app/assets/javascripts/usage_quotas/seats/
/ee/app/assets/javascripts/usage_quotas/storage/
diff --git a/.gitlab/issue_templates/Feature Proposal - lean.md b/.gitlab/issue_templates/Feature Proposal - lean.md
index 7b7402e4d18..c82907a2401 100644
--- a/.gitlab/issue_templates/Feature Proposal - lean.md
+++ b/.gitlab/issue_templates/Feature Proposal - lean.md
@@ -47,6 +47,13 @@ Create tracking issue using the Snowplow event tracking template. See https://gi
-->
+### Does this feature require an audit event?
+
+<!--- Checkout these docs to know more
+https://docs.gitlab.com/ee/development/audit_event_guide/#what-are-audit-events
+https://docs.gitlab.com/ee/administration/audit_events.html
+--->
+
<!-- Label reminders
Use the following resources to find the appropriate labels:
- Use only one tier label choosing the lowest tier this is intended for
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 175bca2ceaf..bb0bf7e2371 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-911b7a1827241f74f6df9b87ae7d27b83da69d64
+2a6efda45f56010b4cb83773434c277444fd53ce
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index c07a0b69110..7c71d1e9f4c 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-v16.3.0-rc3
+v16.3.0-rc4
diff --git a/Gemfile b/Gemfile
index 970cec209fc..e660896b6cd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -205,7 +205,7 @@ gem 'asciidoctor', '~> 2.0.18'
gem 'asciidoctor-include-ext', '~> 0.4.0', require: false
gem 'asciidoctor-plantuml', '~> 0.0.16'
gem 'asciidoctor-kroki', '~> 0.8.0', require: false
-gem 'rouge', '~> 4.1.2'
+gem 'rouge', '~> 4.1.3'
gem 'truncato', '~> 0.7.12'
gem 'nokogiri', '~> 1.15', '>= 1.15.3'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 4545017edf8..5481b316f75 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -520,7 +520,7 @@
{"name":"rexml","version":"3.2.5","platform":"ruby","checksum":"a33c3bf95fda7983ec7f05054f3a985af41dbc25a0339843bd2479e93cabb123"},
{"name":"rinku","version":"2.0.0","platform":"ruby","checksum":"3e695aaf9f24baba3af45823b5c427b58a624582132f18482320e2737f9f8a85"},
{"name":"rotp","version":"6.2.0","platform":"ruby","checksum":"239a2eefba6f1bd4157b2c735d0f975598e0ef94823eea2f35d103d2e5cc0787"},
-{"name":"rouge","version":"4.1.2","platform":"ruby","checksum":"3b4ca60e4ac6e36be2deb0359cba04278ba15bdd2b1fbbb66bbc19cae517d55f"},
+{"name":"rouge","version":"4.1.3","platform":"ruby","checksum":"9c8663db26e05e52b3b0286daacae73ebb361c1bd31d7febd8c57087faa0b9a5"},
{"name":"rqrcode","version":"0.7.0","platform":"ruby","checksum":"8b3a5cba9cc199ba2d781a7c767cb55679f29a3621aa0506a799cec3760d16a1"},
{"name":"rqrcode-rails3","version":"0.1.7","platform":"ruby","checksum":"6f0582f26485123e5ed6f2a8a2871f00d86d353e0f58c8429a5a13212bcf48c4"},
{"name":"rspec","version":"3.12.0","platform":"ruby","checksum":"ccc41799a43509dc0be84070e3f0410ac95cbd480ae7b6c245543eb64162399c"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 16c7c04a34e..cb96cb5c07c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1340,7 +1340,7 @@ GEM
rexml (3.2.5)
rinku (2.0.0)
rotp (6.2.0)
- rouge (4.1.2)
+ rouge (4.1.3)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -1972,7 +1972,7 @@ DEPENDENCIES
responders (~> 3.0)
retriable (~> 3.1.2)
rexml (~> 3.2.5)
- rouge (~> 4.1.2)
+ rouge (~> 4.1.3)
rqrcode-rails3 (~> 0.1.7)
rspec-benchmark (~> 0.6.0)
rspec-parameterized (~> 1.0)
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 388135726a0..56e4b22ded2 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -274,8 +274,6 @@ class Projects::BlobController < Projects::ApplicationController
@last_commit = @repository.last_commit_for_path(@commit.id, @blob.path, literal_pathspec: true)
@code_navigation_path = Gitlab::CodeNavigationPath.new(@project, @blob.commit_id).full_json_path_for(@blob.path)
- allow_lfs_direct_download
-
render 'show'
end
@@ -320,30 +318,6 @@ class Projects::BlobController < Projects::ApplicationController
current_user&.id
end
- def allow_lfs_direct_download
- return unless directly_downloading_lfs_object? && content_security_policy_enabled?
- return unless (lfs_object = @project.lfs_objects.find_by_oid(@blob.lfs_oid))
-
- request.content_security_policy.directives['connect-src'] ||= []
- request.content_security_policy.directives['connect-src'] << lfs_src(lfs_object)
- end
-
- def directly_downloading_lfs_object?
- Gitlab.config.lfs.enabled &&
- !Gitlab.config.lfs.object_store.proxy_download &&
- @blob&.stored_externally?
- end
-
- def content_security_policy_enabled?
- Gitlab.config.gitlab.content_security_policy.enabled
- end
-
- def lfs_src(lfs_object)
- file = lfs_object.file
- file = file.cdn_enabled_url(request.remote_ip) if file.respond_to?(:cdn_enabled_url)
- file.url
- end
-
alias_method :tracking_project_source, :project
def tracking_namespace_source
diff --git a/app/controllers/projects/performance_monitoring/dashboards_controller.rb b/app/controllers/projects/performance_monitoring/dashboards_controller.rb
deleted file mode 100644
index 1255ec1dde2..00000000000
--- a/app/controllers/projects/performance_monitoring/dashboards_controller.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-# frozen_string_literal: true
-
-module Projects
- module PerformanceMonitoring
- class DashboardsController < ::Projects::ApplicationController
- include BlobHelper
-
- before_action :check_repository_available!
- before_action :validate_required_params!
-
- rescue_from ActionController::ParameterMissing do |exception|
- respond_error(http_status: :bad_request, message: _('Request parameter %{param} is missing.') % { param: exception.param })
- end
-
- feature_category :metrics
- urgency :low
-
- def create
- return not_found if Feature.enabled?(:remove_monitor_metrics)
-
- result = ::Metrics::Dashboard::CloneDashboardService.new(project, current_user, dashboard_params).execute
-
- if result[:status] == :success
- respond_success(result)
- else
- respond_error(result)
- end
- end
-
- def update
- return not_found if Feature.enabled?(:remove_monitor_metrics)
-
- result = ::Metrics::Dashboard::UpdateDashboardService.new(project, current_user, dashboard_params.merge(file_content_params)).execute
-
- if result[:status] == :success
- respond_update_success(result)
- else
- respond_error(result)
- end
- end
-
- private
-
- def respond_success(result)
- set_web_ide_link_notice(result.dig(:dashboard, :path))
- respond_to do |format|
- format.json { render status: result.delete(:http_status), json: result }
- end
- end
-
- def respond_error(result)
- respond_to do |format|
- format.json { render json: { error: result[:message] }, status: result[:http_status] }
- end
- end
-
- def set_web_ide_link_notice(new_dashboard_path)
- web_ide_link_start = "<a href=\"#{ide_edit_path(project, redirect_safe_branch_name, new_dashboard_path)}\">"
- message = _("Your dashboard has been copied. You can %{web_ide_link_start}edit it here%{web_ide_link_end}.") % { web_ide_link_start: web_ide_link_start, web_ide_link_end: "</a>" }
- flash[:notice] = message.html_safe
- end
-
- def respond_update_success(result)
- set_web_ide_link_update_notice(result.dig(:dashboard, :path))
- respond_to do |format|
- format.json { render status: result.delete(:http_status), json: result }
- end
- end
-
- def set_web_ide_link_update_notice(new_dashboard_path)
- web_ide_link_start = "<a href=\"#{ide_edit_path(project, redirect_safe_branch_name, new_dashboard_path)}\">"
- message = _("Your dashboard has been updated. You can %{web_ide_link_start}edit it here%{web_ide_link_end}.") % { web_ide_link_start: web_ide_link_start, web_ide_link_end: "</a>" }
- flash[:notice] = message.html_safe
- end
-
- def validate_required_params!
- params.require(%i[branch file_name dashboard commit_message])
- end
-
- def redirect_safe_branch_name
- repository.find_branch(params[:branch]).name
- end
-
- def dashboard_params
- params.permit(%i[branch file_name dashboard commit_message]).to_h
- end
-
- def file_content_params
- params.permit(
- file_content: [
- :dashboard,
- panel_groups: [
- :group,
- :priority,
- panels: [
- :type,
- :title,
- :y_label,
- :weight,
- metrics: [
- :id,
- :unit,
- :label,
- :query,
- :query_range
- ]
- ]
- ]
- ]
- )
- end
- end
- end
-end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 2d6b51a49fc..af62ff8313d 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -252,7 +252,7 @@ class SearchController < ApplicationController
end
def search_type
- 'basic'
+ search_service.search_type
end
end
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 219dcf29563..cd768ba8a7b 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -54,7 +54,6 @@ module EnvironmentsHelper
{
'settings_path' => edit_project_settings_integration_path(project, 'prometheus'),
'clusters_path' => project_clusters_path(project),
- 'dashboards_endpoint' => project_performance_monitoring_dashboards_path(project, format: :json),
'default_branch' => project.default_branch,
'project_path' => project_path(project),
'tags_path' => project_tags_path(project),
diff --git a/app/models/concerns/issuable_link.rb b/app/models/concerns/issuable_link.rb
index 7f29083d6c6..5cb5f410a77 100644
--- a/app/models/concerns/issuable_link.rb
+++ b/app/models/concerns/issuable_link.rb
@@ -53,7 +53,7 @@ module IssuableLink
return unless source && target
if self.class.base_class.find_by(source: target, target: source)
- errors.add(:source, "is already related to this #{self.class.issuable_type}")
+ errors.add(:source, "is already related to this #{self.class.issuable_type.to_s.humanize(capitalize: false)}")
end
end
end
diff --git a/app/models/concerns/linkable_item.rb b/app/models/concerns/linkable_item.rb
new file mode 100644
index 00000000000..ce45549aca0
--- /dev/null
+++ b/app/models/concerns/linkable_item.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+# == LinkableItem concern
+#
+# Contains common functionality shared between related issue links and related work item links
+#
+# Used by IssueLink, WorkItems::RelatedWorkItemLink
+#
+module LinkableItem
+ extend ActiveSupport::Concern
+ include FromUnion
+ include IssuableLink
+
+ included do
+ validate :check_existing_parent_link
+
+ scope :for_source, ->(item) { where(source_id: item.id) }
+ scope :for_target, ->(item) { where(target_id: item.id) }
+ scope :for_items, ->(source, target) do
+ where(source: source, target: target).or(where(source: target, target: source))
+ end
+
+ private
+
+ def check_existing_parent_link
+ return unless source && target
+
+ existing_relation = WorkItems::ParentLink.for_parents([source, target]).for_children([source, target])
+ return if existing_relation.none?
+
+ errors.add(
+ :source,
+ format(
+ _('is a parent or child of this %{item}'),
+ item: self.class.issuable_type.to_s.humanize(capitalize: false)
+ )
+ )
+ end
+ end
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 6e48dcab9ed..2b280f62ee6 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -542,18 +542,18 @@ class Issue < ApplicationRecord
end
def related_issues(current_user, preload: nil)
- related_issues = ::Issue
- .select(['issues.*', 'issue_links.id AS issue_link_id',
- 'issue_links.link_type as issue_link_type_value',
- 'issue_links.target_id as issue_link_source_id',
- 'issue_links.created_at as issue_link_created_at',
- 'issue_links.updated_at as issue_link_updated_at'])
- .joins("INNER JOIN issue_links ON
- (issue_links.source_id = issues.id AND issue_links.target_id = #{id})
- OR
- (issue_links.target_id = issues.id AND issue_links.source_id = #{id})")
- .preload(preload)
- .reorder('issue_link_id')
+ related_issues = self.class
+ .select(['issues.*', 'issue_links.id AS issue_link_id',
+ 'issue_links.link_type as issue_link_type_value',
+ 'issue_links.target_id as issue_link_source_id',
+ 'issue_links.created_at as issue_link_created_at',
+ 'issue_links.updated_at as issue_link_updated_at'])
+ .joins("INNER JOIN issue_links ON
+ (issue_links.source_id = issues.id AND issue_links.target_id = #{id})
+ OR
+ (issue_links.target_id = issues.id AND issue_links.source_id = #{id})")
+ .preload(preload)
+ .reorder('issue_link_id')
related_issues = yield related_issues if block_given?
diff --git a/app/models/issue_link.rb b/app/models/issue_link.rb
index af55a5dec91..1c596ad0341 100644
--- a/app/models/issue_link.rb
+++ b/app/models/issue_link.rb
@@ -1,18 +1,11 @@
# frozen_string_literal: true
class IssueLink < ApplicationRecord
- include FromUnion
- include IssuableLink
+ include LinkableItem
belongs_to :source, class_name: 'Issue'
belongs_to :target, class_name: 'Issue'
- scope :for_source_issue, ->(issue) { where(source_id: issue.id) }
- scope :for_target_issue, ->(issue) { where(target_id: issue.id) }
- scope :for_issues, ->(source, target) do
- where(source: source, target: target).or(where(source: target, target: source))
- end
-
class << self
def issuable_type
:issue
diff --git a/app/models/work_items/parent_link.rb b/app/models/work_items/parent_link.rb
index 5dff9e8e8d5..d9e3690b6fc 100644
--- a/app/models/work_items/parent_link.rb
+++ b/app/models/work_items/parent_link.rb
@@ -19,8 +19,10 @@ module WorkItems
validate :validate_same_project
validate :validate_max_children
validate :validate_confidentiality
+ validate :check_existing_related_link
scope :for_parents, ->(parent_ids) { where(work_item_parent_id: parent_ids) }
+ scope :for_children, ->(children_ids) { where(work_item: children_ids) }
class << self
def has_public_children?(parent_id)
@@ -109,5 +111,14 @@ module WorkItems
errors.add :work_item, _('is already present in ancestors')
end
end
+
+ def check_existing_related_link
+ return unless work_item && work_item_parent
+
+ existing_link = WorkItems::RelatedWorkItemLink.for_items(work_item, work_item_parent)
+ return if existing_link.none?
+
+ errors.add(:work_item, _('cannot assign a linked work item as a parent'))
+ end
end
end
diff --git a/app/models/work_items/related_work_item_link.rb b/app/models/work_items/related_work_item_link.rb
new file mode 100644
index 00000000000..1da776fa3bd
--- /dev/null
+++ b/app/models/work_items/related_work_item_link.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module WorkItems
+ class RelatedWorkItemLink < ApplicationRecord
+ include LinkableItem
+
+ self.table_name = 'issue_links'
+
+ belongs_to :source, class_name: 'WorkItem'
+ belongs_to :target, class_name: 'WorkItem'
+
+ class << self
+ extend ::Gitlab::Utils::Override
+
+ override :issuable_type
+ def issuable_type
+ :work_item
+ end
+ end
+ end
+end
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index c1599ceef6e..e26e3d0835b 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -123,10 +123,10 @@ module Issues
end
def rewrite_related_issues
- source_issue_links = IssueLink.for_source_issue(original_entity)
+ source_issue_links = IssueLink.for_source(original_entity)
source_issue_links.update_all(source_id: new_entity.id)
- target_issue_links = IssueLink.for_target_issue(original_entity)
+ target_issue_links = IssueLink.for_target(original_entity)
target_issue_links.update_all(target_id: new_entity.id)
end
diff --git a/app/services/metrics/dashboard/clone_dashboard_service.rb b/app/services/metrics/dashboard/clone_dashboard_service.rb
deleted file mode 100644
index b5b46b1280b..00000000000
--- a/app/services/metrics/dashboard/clone_dashboard_service.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-# frozen_string_literal: true
-
-# Copies system dashboard definition in .yml file into designated
-# .yml file inside `.gitlab/dashboards`
-module Metrics
- module Dashboard
- class CloneDashboardService < ::BaseService
- include Stepable
- include Gitlab::Utils::StrongMemoize
-
- ALLOWED_FILE_TYPE = '.yml'
- USER_DASHBOARDS_DIR = ::Gitlab::Metrics::Dashboard::RepoDashboardFinder::DASHBOARD_ROOT
- SEQUENCES = {
- ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH => [
- ::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
- ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter
- ].freeze
- }.freeze
-
- steps :check_push_authorized,
- :check_branch_name,
- :check_file_type,
- :check_dashboard_template,
- :create_file,
- :refresh_repository_method_caches
-
- def execute
- execute_steps
- end
-
- private
-
- def check_push_authorized(result)
- return error(_('You are not allowed to push into this branch. Create another branch or open a merge request.'), :forbidden) unless push_authorized?
-
- success(result)
- end
-
- def check_branch_name(result)
- return error(_('There was an error creating the dashboard, branch name is invalid.'), :bad_request) unless valid_branch_name?
- return error(_('There was an error creating the dashboard, branch named: %{branch} already exists.') % { branch: params[:branch] }, :bad_request) unless new_or_default_branch?
-
- success(result)
- end
-
- def check_file_type(result)
- return error(_('The file name should have a .yml extension'), :bad_request) unless target_file_type_valid?
-
- success(result)
- end
-
- # Only allow out of the box metrics dashboards to be cloned. This can be
- # changed to allow cloning of any metrics dashboard, if desired.
- # However, only metrics dashboards should be allowed. If any file is
- # allowed to be cloned, this will become a security risk.
- def check_dashboard_template(result)
- return error(_('Not found.'), :not_found) unless dashboard_service&.out_of_the_box_dashboard?
-
- success(result)
- end
-
- def create_file(result)
- create_file_response = ::Files::CreateService.new(project, current_user, dashboard_attrs).execute
-
- if create_file_response[:status] == :success
- success(result.merge(create_file_response))
- else
- wrap_error(create_file_response)
- end
- end
-
- def refresh_repository_method_caches(result)
- repository.refresh_method_caches([:metrics_dashboard])
-
- success(result.merge(http_status: :created, dashboard: dashboard_details))
- end
-
- def dashboard_service
- strong_memoize(:dashboard_service) do
- Gitlab::Metrics::Dashboard::ServiceSelector.call(dashboard_service_options)
- end
- end
-
- def dashboard_attrs
- {
- commit_message: params[:commit_message],
- file_path: new_dashboard_path,
- file_content: new_dashboard_content,
- encoding: 'text',
- branch_name: branch,
- start_branch: repository.branch_exists?(branch) ? branch : project.default_branch
- }
- end
-
- def dashboard_details
- {
- path: new_dashboard_path,
- display_name: ::Metrics::Dashboard::CustomDashboardService.name_for_path(new_dashboard_path),
- default: false,
- system_dashboard: false
- }
- end
-
- def push_authorized?
- Gitlab::UserAccess.new(current_user, container: project).can_push_to_branch?(branch)
- end
-
- def dashboard_template
- @dashboard_template ||= params[:dashboard]
- end
-
- def branch
- @branch ||= params[:branch]
- end
-
- def new_or_default_branch?
- !repository.branch_exists?(params[:branch]) || project.default_branch == params[:branch]
- end
-
- def valid_branch_name?
- Gitlab::GitRefValidator.validate(params[:branch])
- end
-
- def new_dashboard_path
- @new_dashboard_path ||= File.join(USER_DASHBOARDS_DIR, file_name)
- end
-
- def file_name
- @file_name ||= File.basename(params[:file_name])
- end
-
- def target_file_type_valid?
- File.extname(params[:file_name]) == ALLOWED_FILE_TYPE
- end
-
- def wrap_error(result)
- if result[:message] == 'A file with this name already exists'
- error(_("A file with '%{file_name}' already exists in %{branch} branch") % { file_name: file_name, branch: branch }, :bad_request)
- else
- result
- end
- end
-
- def new_dashboard_content
- ::Gitlab::Metrics::Dashboard::Processor
- .new(project, raw_dashboard, sequence, {})
- .process.deep_stringify_keys.to_yaml
- end
-
- def repository
- @repository ||= project.repository
- end
-
- def raw_dashboard
- dashboard_service.new(project, current_user, dashboard_service_options).raw_dashboard
- end
-
- def dashboard_service_options
- {
- embedded: false,
- dashboard_path: dashboard_template
- }
- end
-
- def sequence
- SEQUENCES[dashboard_template] || []
- end
- end
- end
-end
diff --git a/app/services/metrics/dashboard/dynamic_embed_service.rb b/app/services/metrics/dashboard/dynamic_embed_service.rb
deleted file mode 100644
index a94538668c1..00000000000
--- a/app/services/metrics/dashboard/dynamic_embed_service.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: true
-
-# Responsible for returning a filtered project dashboard
-# containing only the request-provided metrics. The result
-# is then cached for future requests. Metrics are identified
-# based on a combination of identifiers for now, but the ideal
-# would be similar to the approach in DefaultEmbedService, but
-# a single unique identifier is not currently available across
-# all metric types (custom, project-defined, cluster, or system).
-#
-# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
-module Metrics
- module Dashboard
- class DynamicEmbedService < ::Metrics::Dashboard::BaseEmbedService
- include Gitlab::Utils::StrongMemoize
-
- class << self
- # Determines whether the provided params are sufficient
- # to uniquely identify a panel from a yml-defined dashboard.
- #
- # See https://docs.gitlab.com/ee/operations/metrics/dashboards/index.html
- # for additional info on defining custom dashboards.
- def valid_params?(params)
- [
- embedded?(params[:embedded]),
- params[:group].present?,
- params[:title].present?,
- params[:y_label]
- ].all?
- end
- end
-
- # Returns a new dashboard with only the matching
- # metrics from the system dashboard, stripped of groups.
- # @return [Hash]
- def get_raw_dashboard
- not_found! if panels.empty?
-
- { 'panel_groups' => [{ 'panels' => panels }] }
- end
-
- private
-
- def panels
- strong_memoize(:panels) do
- not_found! unless base_dashboard
- not_found! unless groups = base_dashboard['panel_groups']
- not_found! unless matching_group = find_group(groups)
- not_found! unless all_panels = matching_group['panels']
-
- find_panels(all_panels)
- end
- end
-
- def base_dashboard
- strong_memoize(:base_dashboard) do
- Gitlab::Metrics::Dashboard::Finder.find_raw(project, dashboard_path: dashboard_path)
- end
- end
-
- def find_group(groups)
- groups.find do |candidate_group|
- candidate_group['group'] == group
- end
- end
-
- def find_panels(all_panels)
- all_panels.select do |panel|
- panel['title'] == title && panel['y_label'] == y_label
- end
- end
-
- def not_found!
- panels_not_found!(identifiers)
- end
- end
- end
-end
diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb
index 22a882c4648..8f1f78beb5b 100644
--- a/app/services/projects/prometheus/alerts/notify_service.rb
+++ b/app/services/projects/prometheus/alerts/notify_service.rb
@@ -80,8 +80,7 @@ module Projects
def valid_alert_manager_token?(token, integration)
valid_for_alerts_endpoint?(token, integration) ||
- valid_for_manual?(token) ||
- valid_for_cluster?(token)
+ valid_for_manual?(token)
end
def valid_for_manual?(token)
@@ -109,44 +108,6 @@ module Projects
compare_token(token, integration.token)
end
- def valid_for_cluster?(token)
- cluster_integration = find_cluster_integration(project)
- return false unless cluster_integration
-
- cluster_integration_token = cluster_integration.alert_manager_token
-
- if token
- compare_token(token, cluster_integration_token)
- else
- cluster_integration_token.nil?
- end
- end
-
- def find_cluster_integration(project)
- alert_id = gitlab_alert_id
- return unless alert_id
-
- alert = find_alert(project, alert_id)
- return unless alert
-
- cluster = alert.environment.deployment_platform&.cluster
- return unless cluster&.enabled?
- return unless cluster.integration_prometheus_available?
-
- cluster.integration_prometheus
- end
-
- def find_alert(project, metric)
- Projects::Prometheus::AlertsFinder
- .new(project: project, metric: metric)
- .execute
- .first
- end
-
- def gitlab_alert_id
- alerts&.first&.dig('labels', 'gitlab_alert_id')
- end
-
def compare_token(expected, actual)
return unless expected && actual
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 433e9b0da6d..3d413ed9f7b 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -44,6 +44,10 @@ class SearchService
project.blank? && group.blank?
end
+ def search_type
+ 'basic'
+ end
+
def show_snippets?
strong_memoize(:show_snippets) do
params[:snippets] == 'true'
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 5831d5d331d..b514830e438 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -337,14 +337,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
- namespace :performance_monitoring do
- resources :dashboards, only: [:create] do
- collection do
- put '/:file_name', to: 'dashboards#update', constraints: { file_name: /.+\.yml/ }
- end
- end
- end
-
resources :alert_management, only: [:index] do
member do
get 'details(/*page)', to: 'alert_management#details', as: 'details'
diff --git a/db/docs/issue_links.yml b/db/docs/issue_links.yml
index ed21e9c177b..810e5d940c8 100644
--- a/db/docs/issue_links.yml
+++ b/db/docs/issue_links.yml
@@ -2,6 +2,7 @@
table_name: issue_links
classes:
- IssueLink
+- WorkItems::RelatedWorkItemLink
feature_categories:
- team_planning
description: Links two issues by relationship type, which can be related or blocking
diff --git a/db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb b/db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb
new file mode 100644
index 00000000000..f1adb65324b
--- /dev/null
+++ b/db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class PmAffectedPackagesAddVersionsAttribute < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :pm_affected_packages, :versions, :jsonb, default: [], null: false
+ end
+end
diff --git a/db/post_migrate/20230724212040_add_temporary_indexes_for_orphaned_approval_rules.rb b/db/post_migrate/20230724212040_add_temporary_indexes_for_orphaned_approval_rules.rb
new file mode 100644
index 00000000000..69e5f7d48ac
--- /dev/null
+++ b/db/post_migrate/20230724212040_add_temporary_indexes_for_orphaned_approval_rules.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class AddTemporaryIndexesForOrphanedApprovalRules < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ LICENSE_SCANNING = 2
+ SCAN_FINDING = 4
+
+ TMP_PROJECT_INDEX_NAME = 'tmp_idx_orphaned_approval_project_rules'
+ TMP_MR_INDEX_NAME = 'tmp_idx_orphaned_approval_merge_request_rules'
+
+ def up
+ add_concurrent_index('approval_project_rules', :id, where: query_condition, name: TMP_PROJECT_INDEX_NAME)
+ add_concurrent_index('approval_merge_request_rules', :id, where: query_condition, name: TMP_MR_INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name('approval_project_rules', TMP_PROJECT_INDEX_NAME)
+ remove_concurrent_index_by_name('approval_merge_request_rules', TMP_MR_INDEX_NAME)
+ end
+
+ private
+
+ def query_condition
+ "report_type IN (#{LICENSE_SCANNING}, #{SCAN_FINDING}) AND security_orchestration_policy_configuration_id IS NULL"
+ end
+end
diff --git a/db/schema_migrations/20230724185321 b/db/schema_migrations/20230724185321
new file mode 100644
index 00000000000..b5ffbeb5734
--- /dev/null
+++ b/db/schema_migrations/20230724185321
@@ -0,0 +1 @@
+73dc6e44071a82ff0e6237eaf8619af20944ecb959de803779372138d63ee194 \ No newline at end of file
diff --git a/db/schema_migrations/20230724212040 b/db/schema_migrations/20230724212040
new file mode 100644
index 00000000000..f404e1e6d09
--- /dev/null
+++ b/db/schema_migrations/20230724212040
@@ -0,0 +1 @@
+ccd668aefe3e5f1a138be29a6084bd59d058c75ad268eeebba4db9bbf0f6ad7e \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 6a3aa2b4801..ba104d2623d 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -20431,6 +20431,7 @@ CREATE TABLE pm_affected_packages (
affected_range text NOT NULL,
fixed_versions text[] DEFAULT '{}'::text[],
overridden_advisory_fields jsonb DEFAULT '{}'::jsonb NOT NULL,
+ versions jsonb DEFAULT '[]'::jsonb NOT NULL,
CONSTRAINT check_5dd528a2be CHECK ((char_length(package_name) <= 256)),
CONSTRAINT check_80dea16c7b CHECK ((char_length(affected_range) <= 512)),
CONSTRAINT check_d1d4646298 CHECK ((char_length(solution) <= 2048)),
@@ -33828,6 +33829,10 @@ CREATE INDEX tmp_idx_for_feedback_comment_processing ON vulnerability_feedback U
CREATE INDEX tmp_idx_for_vulnerability_feedback_migration ON vulnerability_feedback USING btree (id) WHERE ((migrated_to_state_transition = false) AND (feedback_type = 0));
+CREATE INDEX tmp_idx_orphaned_approval_merge_request_rules ON approval_merge_request_rules USING btree (id) WHERE ((report_type = ANY (ARRAY[2, 4])) AND (security_orchestration_policy_configuration_id IS NULL));
+
+CREATE INDEX tmp_idx_orphaned_approval_project_rules ON approval_project_rules USING btree (id) WHERE ((report_type = ANY (ARRAY[2, 4])) AND (security_orchestration_policy_configuration_id IS NULL));
+
CREATE INDEX tmp_idx_packages_on_project_id_when_npm_not_pending_destruction ON packages_packages USING btree (project_id) WHERE ((package_type = 2) AND (status <> 4));
CREATE INDEX tmp_idx_vuln_reads_where_dismissal_reason_null ON vulnerability_reads USING btree (id) WHERE ((state = 2) AND (dismissal_reason IS NULL));
diff --git a/doc/architecture/blueprints/remote_development/index.md b/doc/architecture/blueprints/remote_development/index.md
index ce55f23f828..c7d1ec29add 100644
--- a/doc/architecture/blueprints/remote_development/index.md
+++ b/doc/architecture/blueprints/remote_development/index.md
@@ -483,6 +483,40 @@ RestartRequested : User has requested a workspace restart.\n**desired_state** wi
RestartRequested -left-> Running : status=Running
```
+## Injecting environment variables and files into a workspace
+
+Like CI, there is a need to inject environment variables and files into a workspace. These environment variables and files will be frozen in time during workspace creation to ensure the same values are injected into the workspace every time it starts/restarts. Thus, a new database table, on the lines of `ci_job_variables` will be required. This table will contain the following columns -
+
+- `key` - To store the name of the environment variable or the file.
+- `encrypted_value` - To store the encrypted value of the environment variable or the file.
+- `encrypted_value_iv` - To store the initialization vector used for encryption.
+- `workspace_id` - To reference the workspace the environment variable or the file is to be injected into.
+- `variable_type` - To store whether this data is to be injected as an environment variable or a file.
+
+To perform the encryption, a secret key would be required. This would be uniquely generated for each workpsace upon creation.
+Having a unique secret key which is used for encrypting the corresponding workspace's environment variable and file data in the workspace, improves the security profile.
+
+Because of the nature of reconciliation loop between Agent and Rails, it is not scalable to decrypt these values at Rails side for each request.
+Instead, the `key`, `encrypted_value` and `encrypted_value_iv` of each environment variable of the workspace are sent to the Agent along with the workspace's `secret_key`
+for the Agent to decrypt them in place.
+
+To optimize this further, the data about the environment variables and files along with the secret key will only be sent when required i.e.
+
+- When new workspace creation request has been received from the user and an Agent initiates a Partial Reonciliation request
+- When an Agent initiates a Full Reconciliation request
+
+When a workspace is created from a project, it will inherit all the variables from the group/subgroup/project hierarchy which are defined under
+[`Settings > CI/CD > Variables`](../../../ci/variables/index.md#define-a-cicd-variable-in-the-ui). This aspect will be generalized to allow for defining `Variables`
+which will be inherited in both CI/CD and Workspaces.
+
+A user will also be able to define, at a user level, environment variables and files to be injected into each workspace created by them.
+
+When a new workspace is created, a new personal access token associated to the user who created the workspace will be generated.
+This personal access token will be tied to the lifecycle of the workspace and will be injected into the workspace as an environment variable or a file
+to allow for cloning private projects and supporting transparent Git operations from within the workspace out-of-the-box among other things.
+
+More details about the implementation details can be found in this [epic](https://gitlab.com/groups/gitlab-org/-/epics/10882).
+
## Workspace user traffic authentication and authorization
We need to only allow certain users to access workspaces. Currently, we are restricting this to the creator/owner of the workspace. After the workspace is created, it needs to be exposed to the network so that the user can connect to it.
diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md
index f378b0ae301..c064626dda7 100644
--- a/doc/user/profile/notifications.md
+++ b/doc/user/profile/notifications.md
@@ -199,6 +199,8 @@ Users are notified of the following events:
| Two-factor authentication disabled | User | Security email, always sent. |
| User added to group | User | Sent when user is added to group. |
| User added to project | User | Sent when user is added to project. |
+| Group access expired | Group members | Sent when user's access to a group expires in seven days. _[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12704) in GitLab 16.3._ |
+| Project access expired | Project members | Sent when user's access to a project expires in seven days. _[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12704) in GitLab 16.3._ |
## Notifications on issues, merge requests, and epics
@@ -331,6 +333,13 @@ The participants are:
- Authors of comments on the design.
- Anyone that is [mentioned](../discussions/index.md#mentions) in a comment on the design.
+## Notifications on group or project access expiration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12704) in GitLab 16.3.
+
+GitLab sends an email notification if a user's access to a group or project expires in seven days.
+This reminds group or project members to extend their access duration if they want to.
+
## Opt out of all GitLab emails
If you no longer wish to receive any email notifications:
diff --git a/lib/api/entities/commit_status.rb b/lib/api/entities/commit_status.rb
index df6a41895ff..14ec3ba461b 100644
--- a/lib/api/entities/commit_status.rb
+++ b/lib/api/entities/commit_status.rb
@@ -18,6 +18,7 @@ module API
expose :finished_at, documentation: { type: 'dateTime', example: '2016-01-21T08:40:25.832Z' }
expose :allow_failure, documentation: { type: 'boolean', example: false }
expose :coverage, documentation: { type: 'number', format: 'float', example: 98.29 }
+ expose :pipeline_id, documentation: { type: 'integer', example: 101 }
expose :author, using: Entities::UserBasic
end
diff --git a/lib/gitlab/alert_management/payload.rb b/lib/gitlab/alert_management/payload.rb
index de34a0f5d47..c6244124022 100644
--- a/lib/gitlab/alert_management/payload.rb
+++ b/lib/gitlab/alert_management/payload.rb
@@ -18,31 +18,20 @@ module Gitlab
# @param monitoring_tool [String]
# @param integration [AlertManagement::HttpIntegration]
def parse(project, payload, monitoring_tool: nil, integration: nil)
- payload_class = payload_class_for(
- monitoring_tool: monitoring_tool || payload&.dig('monitoring_tool'),
- payload: payload
- )
+ payload_class = payload_class_for(monitoring_tool: monitoring_tool || payload&.dig('monitoring_tool'))
payload_class.new(project: project, payload: payload, integration: integration)
end
private
- def payload_class_for(monitoring_tool:, payload:)
+ def payload_class_for(monitoring_tool:)
if monitoring_tool == MONITORING_TOOLS[:prometheus]
- if gitlab_managed_prometheus?(payload)
- ::Gitlab::AlertManagement::Payload::ManagedPrometheus
- else
- ::Gitlab::AlertManagement::Payload::Prometheus
- end
+ ::Gitlab::AlertManagement::Payload::Prometheus
else
::Gitlab::AlertManagement::Payload::Generic
end
end
-
- def gitlab_managed_prometheus?(payload)
- payload&.dig('labels', 'gitlab_alert_id').present?
- end
end
end
end
diff --git a/lib/gitlab/alert_management/payload/managed_prometheus.rb b/lib/gitlab/alert_management/payload/managed_prometheus.rb
deleted file mode 100644
index 4ed21108d3e..00000000000
--- a/lib/gitlab/alert_management/payload/managed_prometheus.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-# Attribute mapping for alerts via prometheus alerting integration,
-# and for which payload includes gitlab-controlled attributes.
-module Gitlab
- module AlertManagement
- module Payload
- class ManagedPrometheus < ::Gitlab::AlertManagement::Payload::Prometheus
- attribute :gitlab_prometheus_alert_id,
- paths: %w(labels gitlab_prometheus_alert_id),
- type: :integer
- attribute :metric_id,
- paths: %w(labels gitlab_alert_id),
- type: :integer
-
- def gitlab_alert
- strong_memoize(:gitlab_alert) do
- next unless metric_id || gitlab_prometheus_alert_id
-
- alerts = Projects::Prometheus::AlertsFinder
- .new(project: project, metric: metric_id, id: gitlab_prometheus_alert_id)
- .execute
-
- next if alerts.blank? || alerts.size > 1
-
- alerts.first
- end
- end
-
- def full_query
- gitlab_alert&.full_query || super
- end
-
- def environment
- gitlab_alert&.environment || super
- end
-
- private
-
- def plain_gitlab_fingerprint
- [metric_id, starts_at_raw].join('/')
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/metrics/dashboard/service_selector.rb b/lib/gitlab/metrics/dashboard/service_selector.rb
index 632f6ce6160..d192fc04e89 100644
--- a/lib/gitlab/metrics/dashboard/service_selector.rb
+++ b/lib/gitlab/metrics/dashboard/service_selector.rb
@@ -15,7 +15,6 @@ module Gitlab
SERVICES = [
::Metrics::Dashboard::GitlabAlertEmbedService,
::Metrics::Dashboard::CustomMetricEmbedService,
- ::Metrics::Dashboard::DynamicEmbedService,
::Metrics::Dashboard::DefaultEmbedService,
::Metrics::Dashboard::SystemDashboardService,
::Metrics::Dashboard::CustomDashboardService
diff --git a/lib/gitlab/metrics/global_search_slis.rb b/lib/gitlab/metrics/global_search_slis.rb
index c361d755a12..530bebd72ab 100644
--- a/lib/gitlab/metrics/global_search_slis.rb
+++ b/lib/gitlab/metrics/global_search_slis.rb
@@ -11,6 +11,7 @@ module Gitlab
BASIC_CODE_TARGET_S = 27.538
ADVANCED_CONTENT_TARGET_S = 2.452
ADVANCED_CODE_TARGET_S = 15.52
+ ZOEKT_TARGET_S = 15.52
def initialize_slis!
Gitlab::Metrics::Sli::Apdex.initialize_sli(:global_search, possible_labels)
@@ -42,6 +43,8 @@ module Gitlab
ADVANCED_CONTENT_TARGET_S
elsif search_type == 'advanced' && code_search?(search_scope)
ADVANCED_CODE_TARGET_S
+ elsif search_type == 'zoekt' && code_search?(search_scope)
+ ZOEKT_TARGET_S
end
end
diff --git a/lib/gitlab/quick_actions/relate_actions.rb b/lib/gitlab/quick_actions/relate_actions.rb
index 058c1e7e9bf..294ddd985de 100644
--- a/lib/gitlab/quick_actions/relate_actions.rb
+++ b/lib/gitlab/quick_actions/relate_actions.rb
@@ -36,7 +36,7 @@ module Gitlab
extract_references(issue_param, :issue).first
end
command :unlink do |issue|
- link = IssueLink.for_issues(quick_action_target, issue).first
+ link = IssueLink.for_items(quick_action_target, issue).first
if link
call_link_service(IssueLinks::DestroyService.new(link, current_user))
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a860f00e658..96fb9d44511 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1795,9 +1795,6 @@ msgstr ""
msgid "A file was not found."
msgstr ""
-msgid "A file with '%{file_name}' already exists in %{branch} branch"
-msgstr ""
-
msgid "A file with this name already exists."
msgstr ""
@@ -4258,6 +4255,12 @@ msgstr ""
msgid "AdvancedSearch|You are using outdated code search mappings. To improve code search quality, we recommend you use %{reindexing_link_start}zero-downtime reindexing%{link_end} or %{recreate_link_start}re-create your index%{link_end}."
msgstr ""
+msgid "AdvancedSearch|You have %{count} pending %{migrations_link_start}advanced search migrations%{link_end} that are obsolete. These migrations might affect your search experience. To resolve the issue, you must %{recreate_link_start}recreate your index%{link_end}."
+msgstr ""
+
+msgid "AdvancedSearch|You have pending obsolete migrations"
+msgstr ""
+
msgid "After a successful password update you will be redirected to login screen."
msgstr ""
@@ -39390,9 +39393,6 @@ msgstr ""
msgid "Request details"
msgstr ""
-msgid "Request parameter %{param} is missing."
-msgstr ""
-
msgid "Request review from"
msgstr ""
@@ -47153,12 +47153,6 @@ msgstr ""
msgid "There was a problem updating the keep latest artifacts setting."
msgstr ""
-msgid "There was an error creating the dashboard, branch name is invalid."
-msgstr ""
-
-msgid "There was an error creating the dashboard, branch named: %{branch} already exists."
-msgstr ""
-
msgid "There was an error creating the issue"
msgstr ""
@@ -54002,12 +53996,6 @@ msgstr ""
msgid "Your current password is required to register a two-factor authenticator app."
msgstr ""
-msgid "Your dashboard has been copied. You can %{web_ide_link_start}edit it here%{web_ide_link_end}."
-msgstr ""
-
-msgid "Your dashboard has been updated. You can %{web_ide_link_start}edit it here%{web_ide_link_end}."
-msgstr ""
-
msgid "Your deployment services will be broken, you will need to manually fix the services after renaming."
msgstr ""
@@ -54432,6 +54420,9 @@ msgstr ""
msgid "can't reference a branch that does not exist"
msgstr ""
+msgid "cannot assign a linked work item as a parent"
+msgstr ""
+
msgid "cannot assign a non-confidential work item to a confidential parent. Make the work item confidential and try again."
msgstr ""
@@ -55196,6 +55187,9 @@ msgstr ""
msgid "is"
msgstr ""
+msgid "is a parent or child of this %{item}"
+msgstr ""
+
msgid "is already associated to a GitLab Issue. New issue will not be associated."
msgstr ""
diff --git a/qa/Gemfile b/qa/Gemfile
index 1aa0785620a..887d7939fd3 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -24,7 +24,7 @@ gem 'rotp', '~> 6.2.2'
gem 'parallel', '~> 1.23'
gem 'rainbow', '~> 3.1.1'
gem 'rspec-parameterized', '~> 1.0.0'
-gem 'octokit', '~> 6.1.1'
+gem 'octokit', '~> 7.0.0'
gem "faraday-retry", "~> 2.2"
gem 'zeitwerk', '~> 2.6', '>= 2.6.8'
gem 'influxdb-client', '~> 2.9'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 4a29d81d5b9..d61eda6ff1e 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -213,7 +213,7 @@ GEM
nokogiri (1.15.3)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
- octokit (6.1.1)
+ octokit (7.0.0)
faraday (>= 1, < 3)
sawyer (~> 0.9)
oj (3.13.23)
@@ -354,7 +354,7 @@ DEPENDENCIES
influxdb-client (~> 2.9)
knapsack (~> 4.0)
nokogiri (~> 1.15, >= 1.15.3)
- octokit (~> 6.1.1)
+ octokit (~> 7.0.0)
parallel (~> 1.23)
parallel_tests (~> 4.2, >= 4.2.1)
pry-byebug (~> 3.10.1)
diff --git a/spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb b/spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb
deleted file mode 100644
index 02407e31756..00000000000
--- a/spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb
+++ /dev/null
@@ -1,287 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::PerformanceMonitoring::DashboardsController, feature_category: :metrics do
- let_it_be(:user) { create(:user) }
- let_it_be(:namespace) { create(:namespace) }
-
- let_it_be(:project) { create(:project, :repository, namespace: namespace) }
- let(:repository) { project.repository }
- let(:branch) { double(name: branch_name) }
- let(:commit_message) { 'test' }
- let(:branch_name) { "#{Time.current.to_i}_dashboard_new_branch" }
- let(:dashboard) { 'config/prometheus/common_metrics.yml' }
- let(:file_name) { 'custom_dashboard.yml' }
- let(:params) do
- {
- namespace_id: namespace,
- project_id: project,
- dashboard: dashboard,
- file_name: file_name,
- commit_message: commit_message,
- branch: branch_name,
- format: :json
- }
- end
-
- before do
- stub_feature_flags(remove_monitor_metrics: false)
- end
-
- describe 'POST #create' do
- context 'authenticated user' do
- before do
- sign_in(user)
- end
-
- context 'project with repository feature' do
- context 'with rights to push to the repository' do
- before do
- project.add_maintainer(user)
- end
-
- context 'valid parameters' do
- it 'delegates cloning to ::Metrics::Dashboard::CloneDashboardService' do
- allow(controller).to receive(:repository).and_return(repository)
- allow(repository).to receive(:find_branch).and_return(branch)
- dashboard_attrs = {
- dashboard: dashboard,
- file_name: file_name,
- commit_message: commit_message,
- branch: branch_name
- }
-
- service_instance = instance_double(::Metrics::Dashboard::CloneDashboardService)
- expect(::Metrics::Dashboard::CloneDashboardService).to receive(:new).with(project, user, dashboard_attrs).and_return(service_instance)
- expect(service_instance).to receive(:execute).and_return(status: :success, http_status: :created, dashboard: { path: 'dashboard/path' })
-
- post :create, params: params
- end
-
- context 'request format json' do
- it 'returns services response' do
- allow(::Metrics::Dashboard::CloneDashboardService).to receive(:new).and_return(double(execute: { status: :success, dashboard: { path: ".gitlab/dashboards/#{file_name}" }, http_status: :created }))
- allow(controller).to receive(:repository).and_return(repository)
- allow(repository).to receive(:find_branch).and_return(branch)
-
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :created
- expect(controller).to set_flash[:notice].to eq("Your dashboard has been copied. You can <a href=\"/-/ide/project/#{project.full_path}/edit/#{branch_name}/-/.gitlab/dashboards/#{file_name}\">edit it here</a>.")
- expect(json_response).to eq('status' => 'success', 'dashboard' => { 'path' => ".gitlab/dashboards/#{file_name}" })
- end
-
- context 'Metrics::Dashboard::CloneDashboardService failure' do
- it 'returns json with failure message', :aggregate_failures do
- allow(::Metrics::Dashboard::CloneDashboardService).to receive(:new).and_return(double(execute: { status: :error, message: 'something went wrong', http_status: :bad_request }))
-
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :bad_request
- expect(json_response).to eq('error' => 'something went wrong')
- end
- end
-
- %w(commit_message file_name dashboard).each do |param|
- context "param #{param} is missing" do
- let(param.to_s) { nil }
-
- it 'responds with bad request status and error message', :aggregate_failures do
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :bad_request
- expect(json_response).to eq('error' => "Request parameter #{param} is missing.")
- end
- end
- end
-
- context "param branch_name is missing" do
- let(:branch_name) { nil }
-
- it 'responds with bad request status and error message', :aggregate_failures do
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :bad_request
- expect(json_response).to eq('error' => "Request parameter branch is missing.")
- end
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :not_found
- end
- end
- end
- end
- end
-
- context 'without rights to push to repository' do
- before do
- project.add_guest(user)
- end
-
- it 'responds with :forbidden status code' do
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :forbidden
- end
- end
- end
-
- context 'project without repository feature' do
- let_it_be(:project) { create(:project, namespace: namespace) }
-
- it 'responds with :not_found status code' do
- post :create, params: params
-
- expect(response).to have_gitlab_http_status :not_found
- end
- end
- end
- end
-
- describe 'PUT #update' do
- context 'authenticated user' do
- before do
- sign_in(user)
- end
-
- let(:file_content) do
- {
- "dashboard" => "Dashboard Title",
- "panel_groups" => [{
- "group" => "Group Title",
- "panels" => [{
- "type" => "area-chart",
- "title" => "Chart Title",
- "y_label" => "Y-Axis",
- "metrics" => [{
- "id" => "metric_of_ages",
- "unit" => "count",
- "label" => "Metric of Ages",
- "query_range" => "http_requests_total"
- }]
- }]
- }]
- }
- end
-
- let(:params) do
- {
- namespace_id: namespace,
- project_id: project,
- dashboard: dashboard,
- file_name: file_name,
- file_content: file_content,
- commit_message: commit_message,
- branch: branch_name,
- format: :json
- }
- end
-
- context 'project with repository feature' do
- context 'with rights to push to the repository' do
- before do
- project.add_maintainer(user)
- end
-
- context 'valid parameters' do
- context 'request format json' do
- let(:update_dashboard_service_params) { params.except(:namespace_id, :project_id, :format) }
-
- let(:update_dashboard_service_results) do
- {
- status: :success,
- http_status: :created,
- dashboard: {
- path: ".gitlab/dashboards/custom_dashboard.yml",
- display_name: "custom_dashboard.yml",
- default: false,
- system_dashboard: false
- }
- }
- end
-
- let(:update_dashboard_service) { instance_double(::Metrics::Dashboard::UpdateDashboardService, execute: update_dashboard_service_results) }
-
- it 'returns path to new file' do
- allow(controller).to receive(:repository).and_return(repository)
- allow(repository).to receive(:find_branch).and_return(branch)
- allow(::Metrics::Dashboard::UpdateDashboardService).to receive(:new).with(project, user, update_dashboard_service_params).and_return(update_dashboard_service)
-
- put :update, params: params
-
- expect(response).to have_gitlab_http_status :created
- expect(controller).to set_flash[:notice].to eq("Your dashboard has been updated. You can <a href=\"/-/ide/project/#{project.full_path}/edit/#{branch_name}/-/.gitlab/dashboards/#{file_name}\">edit it here</a>.")
- expect(json_response).to eq('status' => 'success', 'dashboard' => { 'default' => false, 'display_name' => "custom_dashboard.yml", 'path' => ".gitlab/dashboards/#{file_name}", 'system_dashboard' => false })
- end
-
- context 'UpdateDashboardService failure' do
- it 'returns json with failure message' do
- allow(::Metrics::Dashboard::UpdateDashboardService).to receive(:new).and_return(double(execute: { status: :error, message: 'something went wrong', http_status: :bad_request }))
-
- put :update, params: params
-
- expect(response).to have_gitlab_http_status :bad_request
- expect(json_response).to eq('error' => 'something went wrong')
- end
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- put :update, params: params
-
- expect(response).to have_gitlab_http_status :not_found
- end
- end
- end
- end
-
- context 'missing branch' do
- let(:branch_name) { nil }
-
- it 'raises responds with :bad_request status code and error message' do
- put :update, params: params
-
- expect(response).to have_gitlab_http_status :bad_request
- expect(json_response).to eq('error' => "Request parameter branch is missing.")
- end
- end
- end
-
- context 'without rights to push to repository' do
- before do
- project.add_guest(user)
- end
-
- it 'responds with :forbidden status code' do
- put :update, params: params
-
- expect(response).to have_gitlab_http_status :forbidden
- end
- end
- end
-
- context 'project without repository feature' do
- let_it_be(:project) { create(:project, namespace: namespace) }
-
- it 'responds with :not_found status code' do
- put :update, params: params
-
- expect(response).to have_gitlab_http_status :not_found
- end
- end
- end
- end
-end
diff --git a/spec/factories/work_items/related_work_item_links.rb b/spec/factories/work_items/related_work_item_links.rb
new file mode 100644
index 00000000000..327323af803
--- /dev/null
+++ b/spec/factories/work_items/related_work_item_links.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :work_item_link, class: 'WorkItems::RelatedWorkItemLink' do
+ source factory: :work_item
+ target factory: :work_item
+ end
+end
diff --git a/spec/frontend/ide/components/ide_review_spec.js b/spec/frontend/ide/components/ide_review_spec.js
index 7ae8cfac935..59b87144e70 100644
--- a/spec/frontend/ide/components/ide_review_spec.js
+++ b/spec/frontend/ide/components/ide_review_spec.js
@@ -14,6 +14,7 @@ Vue.use(Vuex);
describe('IDE review mode', () => {
let wrapper;
let store;
+ let dispatch;
beforeEach(() => {
store = createStore();
@@ -25,27 +26,28 @@ describe('IDE review mode', () => {
loading: false,
});
+ dispatch = jest.spyOn(store, 'dispatch');
+
wrapper = mount(keepAlive(IdeReview), {
store,
});
});
+ const findEditorModeDropdown = () => wrapper.findComponent(EditorModeDropdown);
+
it('renders list of files', () => {
expect(wrapper.text()).toContain('fileName');
});
describe('activated', () => {
- let inititializeSpy;
-
beforeEach(async () => {
- inititializeSpy = jest.spyOn(wrapper.findComponent(IdeReview).vm, 'initialize');
store.state.viewer = 'editor';
await wrapper.vm.reactivate();
});
it('re initializes the component', () => {
- expect(inititializeSpy).toHaveBeenCalled();
+ expect(dispatch).toHaveBeenCalledWith('updateViewer', 'diff');
});
it('updates viewer to "diff" by default', () => {
@@ -81,7 +83,7 @@ describe('IDE review mode', () => {
});
it('renders edit dropdown', () => {
- expect(wrapper.findComponent(EditorModeDropdown).exists()).toBe(true);
+ expect(findEditorModeDropdown().exists()).toBe(true);
});
it('renders merge request link & IID', async () => {
diff --git a/spec/lib/gitlab/alert_management/payload/managed_prometheus_spec.rb b/spec/lib/gitlab/alert_management/payload/managed_prometheus_spec.rb
deleted file mode 100644
index d7184c89933..00000000000
--- a/spec/lib/gitlab/alert_management/payload/managed_prometheus_spec.rb
+++ /dev/null
@@ -1,153 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::AlertManagement::Payload::ManagedPrometheus do
- let_it_be(:project) { create(:project) }
-
- let(:raw_payload) { {} }
-
- let(:parsed_payload) { described_class.new(project: project, payload: raw_payload) }
-
- it_behaves_like 'subclass has expected api'
-
- shared_context 'with gitlab alert' do
- let_it_be(:gitlab_alert) { create(:prometheus_alert, project: project) }
- let(:metric_id) { gitlab_alert.prometheus_metric_id.to_s }
- let(:alert_id) { gitlab_alert.id.to_s }
- end
-
- describe '#metric_id' do
- subject { parsed_payload.metric_id }
-
- it { is_expected.to be_nil }
-
- context 'with gitlab_alert_id' do
- let(:raw_payload) { { 'labels' => { 'gitlab_alert_id' => '12' } } }
-
- it { is_expected.to eq(12) }
- end
- end
-
- describe '#gitlab_prometheus_alert_id' do
- subject { parsed_payload.gitlab_prometheus_alert_id }
-
- it { is_expected.to be_nil }
-
- context 'with gitlab_alert_id' do
- let(:raw_payload) { { 'labels' => { 'gitlab_prometheus_alert_id' => '12' } } }
-
- it { is_expected.to eq(12) }
- end
- end
-
- describe '#gitlab_alert' do
- subject { parsed_payload.gitlab_alert }
-
- context 'without alert info in payload' do
- it { is_expected.to be_nil }
- end
-
- context 'with metric id in payload' do
- let(:raw_payload) { { 'labels' => { 'gitlab_alert_id' => metric_id } } }
- let(:metric_id) { '-1' }
-
- context 'without matching alert' do
- it { is_expected.to be_nil }
- end
-
- context 'with matching alert' do
- include_context 'with gitlab alert'
-
- it { is_expected.to eq(gitlab_alert) }
-
- context 'when unclear which alert applies' do
- # With multiple alerts for different environments,
- # we can't be sure which prometheus alert the payload
- # belongs to
- let_it_be(:another_alert) do
- create(:prometheus_alert,
- prometheus_metric: gitlab_alert.prometheus_metric,
- project: project)
- end
-
- it { is_expected.to be_nil }
- end
- end
- end
-
- context 'with alert id' do
- # gitlab_prometheus_alert_id is a stronger identifier,
- # but was added after gitlab_alert_id; we won't
- # see it without gitlab_alert_id also present
- let(:raw_payload) do
- {
- 'labels' => {
- 'gitlab_alert_id' => metric_id,
- 'gitlab_prometheus_alert_id' => alert_id
- }
- }
- end
-
- context 'without matching alert' do
- let(:alert_id) { '-1' }
- let(:metric_id) { '-1' }
-
- it { is_expected.to be_nil }
- end
-
- context 'with matching alerts' do
- include_context 'with gitlab alert'
-
- it { is_expected.to eq(gitlab_alert) }
- end
- end
- end
-
- describe '#full_query' do
- subject { parsed_payload.full_query }
-
- it { is_expected.to be_nil }
-
- context 'with gitlab alert' do
- include_context 'with gitlab alert'
-
- let(:raw_payload) { { 'labels' => { 'gitlab_alert_id' => metric_id } } }
-
- it { is_expected.to eq(gitlab_alert.full_query) }
- end
-
- context 'with sufficient fallback info' do
- let(:raw_payload) { { 'generatorURL' => 'http://localhost:9090/graph?g0.expr=vector%281%29' } }
-
- it { is_expected.to eq('vector(1)') }
- end
- end
-
- describe '#environment' do
- subject { parsed_payload.environment }
-
- context 'with gitlab alert' do
- include_context 'with gitlab alert'
-
- let(:raw_payload) { { 'labels' => { 'gitlab_alert_id' => metric_id } } }
-
- it { is_expected.to eq(gitlab_alert.environment) }
- end
-
- context 'with sufficient fallback info' do
- let_it_be(:environment) { create(:environment, project: project, name: 'production') }
-
- let(:raw_payload) do
- {
- 'labels' => {
- 'gitlab_alert_id' => '-1',
- 'gitlab_environment_name' => 'production'
- }
- }
- end
-
- it { is_expected.to eq(environment) }
- end
- end
-end
diff --git a/spec/lib/gitlab/alert_management/payload_spec.rb b/spec/lib/gitlab/alert_management/payload_spec.rb
index efde7ed3772..fe14e6ae53c 100644
--- a/spec/lib/gitlab/alert_management/payload_spec.rb
+++ b/spec/lib/gitlab/alert_management/payload_spec.rb
@@ -19,12 +19,6 @@ RSpec.describe Gitlab::AlertManagement::Payload do
let(:payload) { { 'monitoring_tool' => 'Prometheus' } }
it { is_expected.to be_a Gitlab::AlertManagement::Payload::Prometheus }
-
- context 'with gitlab-managed attributes' do
- let(:payload) { { 'monitoring_tool' => 'Prometheus', 'labels' => { 'gitlab_alert_id' => '12' } } }
-
- it { is_expected.to be_a Gitlab::AlertManagement::Payload::ManagedPrometheus }
- end
end
context 'with the payload specifying an unknown tool' do
@@ -43,12 +37,6 @@ RSpec.describe Gitlab::AlertManagement::Payload do
context 'with an externally managed prometheus payload' do
it { is_expected.to be_a Gitlab::AlertManagement::Payload::Prometheus }
end
-
- context 'with a self-managed prometheus payload' do
- let(:payload) { { 'labels' => { 'gitlab_alert_id' => '14' } } }
-
- it { is_expected.to be_a Gitlab::AlertManagement::Payload::ManagedPrometheus }
- end
end
context 'as an unknown tool' do
diff --git a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
index 991f8eff684..a8769098dd5 100644
--- a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
@@ -80,20 +80,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store
end
context 'as a project-defined panel' do
- let(:dashboard_path) { '.gitlab/dashboard/test.yml' }
- let(:params) do
- {
- environment: environment,
- embedded: true,
- dashboard_path: dashboard_path,
- group: 'Group A',
- title: 'Super Chart A1',
- y_label: 'y_label'
- }
- end
-
- it_behaves_like 'misconfigured dashboard service response', :not_found
-
context 'when the metric exists' do
let(:project) { project_with_dashboard(dashboard_path) }
diff --git a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
index cbf8a40d4fc..82f3b1bfce9 100644
--- a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
@@ -35,33 +35,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::ServiceSelector do
it { is_expected.to be Metrics::Dashboard::DefaultEmbedService }
end
- context 'when all the chart identifiers are provided' do
- let(:arguments) do
- {
- embedded: true,
- dashboard_path: '.gitlab/dashboards/test.yml',
- group: 'Important Metrics',
- title: 'Total Requests',
- y_label: 'req/sec'
- }
- end
-
- it { is_expected.to be Metrics::Dashboard::DynamicEmbedService }
- end
-
- context 'when all chart params expect dashboard_path are provided' do
- let(:arguments) do
- {
- embedded: true,
- group: 'Important Metrics',
- title: 'Total Requests',
- y_label: 'req/sec'
- }
- end
-
- it { is_expected.to be Metrics::Dashboard::DynamicEmbedService }
- end
-
context 'with a system dashboard and "custom" group' do
let(:arguments) do
{
diff --git a/spec/lib/gitlab/metrics/global_search_slis_spec.rb b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
index 5248cd08770..68793db6e41 100644
--- a/spec/lib/gitlab/metrics/global_search_slis_spec.rb
+++ b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
+RSpec.describe Gitlab::Metrics::GlobalSearchSlis, feature_category: :global_search do
using RSpec::Parameterized::TableSyntax
describe '#initialize_slis!' do
@@ -92,6 +92,7 @@ RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
'basic' | true | 27.538
'advanced' | false | 2.452
'advanced' | true | 15.52
+ 'zoekt' | true | 15.52
end
with_them do
diff --git a/spec/mailers/emails/projects_spec.rb b/spec/mailers/emails/projects_spec.rb
index 1f0f09f7ca2..9518672939b 100644
--- a/spec/mailers/emails/projects_spec.rb
+++ b/spec/mailers/emails/projects_spec.rb
@@ -124,41 +124,6 @@ RSpec.describe Emails::Projects do
end
end
- context 'with gitlab alerting rule' do
- let_it_be(:prometheus_alert) { create(:prometheus_alert, project: project) }
- let_it_be(:environment) { prometheus_alert.environment }
-
- let(:alert) { create(:alert_management_alert, :prometheus, :from_payload, payload: payload, project: project) }
- let(:title) { "#{prometheus_alert.title} #{prometheus_alert.computed_operator} #{prometheus_alert.threshold}" }
-
- before do
- payload['labels'] = {
- 'gitlab_alert_id' => prometheus_alert.prometheus_metric_id,
- 'alertname' => prometheus_alert.title
- }
- end
-
- it_behaves_like 'an email sent from GitLab'
- it_behaves_like 'it should not have Gmail Actions links'
- it_behaves_like 'a user cannot unsubscribe through footer link'
- it_behaves_like 'shows the incident issues url'
-
- it 'has expected subject' do
- is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{title} for 5 minutes")
- end
-
- it 'has expected content' do
- is_expected.to have_body_text('An alert has been triggered')
- is_expected.to have_body_text(project.full_path)
- is_expected.to have_body_text(alert.details_url)
- is_expected.to have_body_text('Environment:')
- is_expected.to have_body_text(environment.name)
- is_expected.to have_body_text('Metric:')
- is_expected.to have_body_text(prometheus_alert.full_query)
- is_expected.not_to have_body_text('Description:')
- end
- end
-
context 'resolved' do
let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project) }
diff --git a/spec/models/issue_link_spec.rb b/spec/models/issue_link_spec.rb
index d69a3f2954c..7b06bec7b31 100644
--- a/spec/models/issue_link_spec.rb
+++ b/spec/models/issue_link_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-RSpec.describe IssueLink do
+RSpec.describe IssueLink, feature_category: :portfolio_management do
+ let_it_be(:project) { create(:project) }
+
it_behaves_like 'issuable link' do
let_it_be_with_reload(:issuable_link) { create(:issue_link) }
let_it_be(:issuable) { create(:issue) }
@@ -14,46 +16,11 @@ RSpec.describe IssueLink do
it { expect(described_class.issuable_type).to eq(:issue) }
end
- describe 'Scopes' do
- let_it_be(:issue1) { create(:issue) }
- let_it_be(:issue2) { create(:issue) }
-
- describe '.for_source_issue' do
- it 'includes linked issues for source issue' do
- source_issue = create(:issue)
- issue_link_1 = create(:issue_link, source: source_issue, target: issue1)
- issue_link_2 = create(:issue_link, source: source_issue, target: issue2)
-
- result = described_class.for_source_issue(source_issue)
-
- expect(result).to contain_exactly(issue_link_1, issue_link_2)
- end
- end
-
- describe '.for_target_issue' do
- it 'includes linked issues for target issue' do
- target_issue = create(:issue)
- issue_link_1 = create(:issue_link, source: issue1, target: target_issue)
- issue_link_2 = create(:issue_link, source: issue2, target: target_issue)
-
- result = described_class.for_target_issue(target_issue)
-
- expect(result).to contain_exactly(issue_link_1, issue_link_2)
- end
- end
-
- describe '.for_issues' do
- let_it_be(:issue) { create(:issue) }
- let_it_be(:source_link) { create(:issue_link, source: issue, target: issue1) }
- let_it_be(:target_link) { create(:issue_link, source: issue2, target: issue) }
-
- it 'includes links when issue is source' do
- expect(described_class.for_issues(issue, issue1)).to contain_exactly(source_link)
- end
-
- it 'includes links when issue is target' do
- expect(described_class.for_issues(issue, issue2)).to contain_exactly(target_link)
- end
- end
+ it_behaves_like 'includes LinkableItem concern' do
+ let_it_be(:item) { create(:issue, project: project) }
+ let_it_be(:item1) { create(:issue, project: project) }
+ let_it_be(:item2) { create(:issue, project: project) }
+ let_it_be(:link_factory) { :issue_link }
+ let_it_be(:item_type) { 'issue' }
end
end
diff --git a/spec/models/work_items/parent_link_spec.rb b/spec/models/work_items/parent_link_spec.rb
index d7f87da1965..3fcfa856db4 100644
--- a/spec/models/work_items/parent_link_spec.rb
+++ b/spec/models/work_items/parent_link_spec.rb
@@ -18,9 +18,9 @@ RSpec.describe WorkItems::ParentLink, feature_category: :portfolio_management do
it { is_expected.to validate_uniqueness_of(:work_item) }
describe 'hierarchy' do
- let_it_be(:issue) { build(:work_item, project: project) }
+ let_it_be(:issue) { create(:work_item, project: project) }
let_it_be(:incident) { build(:work_item, :incident, project: project) }
- let_it_be(:task1) { build(:work_item, :task, project: project) }
+ let_it_be(:task1) { create(:work_item, :task, project: project) }
let_it_be(:task2) { build(:work_item, :task, project: project) }
it 'is valid if issue parent has task child' do
@@ -158,6 +158,38 @@ RSpec.describe WorkItems::ParentLink, feature_category: :portfolio_management do
end
end
end
+
+ context 'when parent is already linked' do
+ shared_examples 'invalid link' do |link_factory|
+ let_it_be(:parent_link) { build(:parent_link, work_item_parent: issue, work_item: task1) }
+ let(:error_msg) { 'cannot assign a linked work item as a parent' }
+
+ context 'when parent is the link target' do
+ before do
+ create(link_factory, source_id: task1.id, target_id: issue.id)
+ end
+
+ it do
+ expect(parent_link).not_to be_valid
+ expect(parent_link.errors[:work_item]).to include(error_msg)
+ end
+ end
+
+ context 'when parent is the link source' do
+ before do
+ create(link_factory, source_id: issue.id, target_id: task1.id)
+ end
+
+ it do
+ expect(parent_link).not_to be_valid
+ expect(parent_link.errors[:work_item]).to include(error_msg)
+ end
+ end
+ end
+
+ it_behaves_like 'invalid link', :work_item_link
+ it_behaves_like 'invalid link', :issue_link
+ end
end
end
@@ -178,6 +210,14 @@ RSpec.describe WorkItems::ParentLink, feature_category: :portfolio_management do
expect(result).to include(link1, link2)
end
end
+
+ describe 'for_children' do
+ it 'includes the correct records' do
+ result = described_class.for_children([task1.id, task2.id])
+
+ expect(result).to include(link1, link2)
+ end
+ end
end
context 'with confidential work items' do
diff --git a/spec/models/work_items/related_work_item_link_spec.rb b/spec/models/work_items/related_work_item_link_spec.rb
new file mode 100644
index 00000000000..612bdfd937f
--- /dev/null
+++ b/spec/models/work_items/related_work_item_link_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::RelatedWorkItemLink, type: :model, feature_category: :portfolio_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:work_item, :issue, project: project) }
+
+ it_behaves_like 'issuable link' do
+ let_it_be_with_reload(:issuable_link) { create(:work_item_link) }
+ let_it_be(:issuable) { issue }
+ let(:issuable_class) { 'WorkItem' }
+ let(:issuable_link_factory) { :work_item_link }
+ end
+
+ it_behaves_like 'includes LinkableItem concern' do
+ let_it_be(:item) { create(:work_item, project: project) }
+ let_it_be(:item1) { create(:work_item, project: project) }
+ let_it_be(:item2) { create(:work_item, project: project) }
+ let_it_be(:link_factory) { :work_item_link }
+ let_it_be(:item_type) { 'work item' }
+ end
+
+ describe '.issuable_type' do
+ it { expect(described_class.issuable_type).to eq(:work_item) }
+ end
+end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 7540e19e278..2f0e64cd4da 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -152,6 +152,7 @@ RSpec.describe API::CommitStatuses, feature_category: :continuous_integration do
expect(json_response['ref']).not_to be_empty
expect(json_response['target_url']).to be_nil
expect(json_response['description']).to be_nil
+ expect(json_response['pipeline_id']).not_to be_nil
if status == 'failed'
expect(CommitStatus.find(json_response['id'])).to be_api_failure
diff --git a/spec/requests/projects/blob_spec.rb b/spec/requests/projects/blob_spec.rb
deleted file mode 100644
index 7d62619e76a..00000000000
--- a/spec/requests/projects/blob_spec.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Blobs', feature_category: :source_code_management do
- let_it_be(:project) { create(:project, :public, :repository, lfs: true) }
-
- describe 'GET /:namespace_id/:project_id/-/blob/:id' do
- subject(:request) do
- get namespace_project_blob_path(namespace_id: project.namespace, project_id: project, id: id)
- end
-
- context 'with LFS file' do
- let(:id) { 'master/files/lfs/lfs_object.iso' }
- let(:object_store_host) { 'http://127.0.0.1:9000' }
- let(:connect_src) do
- csp = response.headers['Content-Security-Policy']
- csp.split('; ').find { |src| src.starts_with?('connect-src') }
- end
-
- let(:gitlab_config) do
- Gitlab.config.gitlab.deep_merge(
- 'content_security_policy' => {
- 'enabled' => content_security_policy_enabled
- }
- )
- end
-
- let(:lfs_config) do
- Gitlab.config.lfs.deep_merge(
- 'enabled' => lfs_enabled,
- 'object_store' => {
- 'remote_directory' => 'lfs-objects',
- 'enabled' => true,
- 'proxy_download' => proxy_download,
- 'connection' => {
- 'endpoint' => object_store_host,
- 'path_style' => true
- }
- }
- )
- end
-
- before do
- stub_config_setting(gitlab_config)
- stub_lfs_setting(lfs_config)
- stub_lfs_object_storage(proxy_download: proxy_download)
-
- request
- end
-
- describe 'directly downloading lfs file' do
- let(:lfs_enabled) { true }
- let(:proxy_download) { false }
- let(:content_security_policy_enabled) { true }
-
- it { expect(response).to have_gitlab_http_status(:success) }
-
- it { expect(connect_src).to include(object_store_host) }
-
- context 'when lfs is disabled' do
- let(:lfs_enabled) { false }
-
- it { expect(response).to have_gitlab_http_status(:success) }
-
- it { expect(connect_src).not_to include(object_store_host) }
- end
-
- context 'when content_security_policy is disabled' do
- let(:content_security_policy_enabled) { false }
-
- it { expect(response).to have_gitlab_http_status(:success) }
-
- it { expect(connect_src).not_to include(object_store_host) }
- end
-
- context 'when proxy download is enabled' do
- let(:proxy_download) { true }
-
- it { expect(response).to have_gitlab_http_status(:success) }
-
- it { expect(connect_src).not_to include(object_store_host) }
- end
- end
- end
- end
-end
diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
index eb5f3808021..9fe77bf2b17 100644
--- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb
+++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
@@ -66,22 +66,6 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService, feature_category:
expect(alert.environment).to eq(environment)
end
end
-
- context 'prometheus alert given' do
- let(:prometheus_alert) { create(:prometheus_alert, project: project) }
- let(:alert) { project.alert_management_alerts.last }
-
- before do
- payload['labels']['gitlab_alert_id'] = prometheus_alert.prometheus_metric_id
- end
-
- it 'sets the prometheus alert and environment' do
- execute
-
- expect(alert.prometheus_alert).to eq(prometheus_alert)
- expect(alert.environment).to eq(prometheus_alert.environment)
- end
- end
end
context 'when alert payload is invalid' do
diff --git a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
deleted file mode 100644
index 0818bdd8b9c..00000000000
--- a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
+++ /dev/null
@@ -1,190 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_store_caching, feature_category: :metrics do
- include MetricsDashboardHelpers
-
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:environment) { create(:environment, project: project) }
-
- describe '#execute' do
- subject(:service_call) { described_class.new(project, user, params).execute }
-
- let(:commit_message) { 'test' }
- let(:branch) { "dashboard_new_branch" }
- let(:dashboard) { 'config/prometheus/common_metrics.yml' }
- let(:file_name) { 'custom_dashboard.yml' }
- let(:file_content_hash) { YAML.safe_load(File.read(dashboard)) }
- let(:params) do
- {
- dashboard: dashboard,
- file_name: file_name,
- commit_message: commit_message,
- branch: branch
- }
- end
-
- context 'user does not have push right to repository' do
- it_behaves_like 'misconfigured dashboard service response with stepable', :forbidden, 'You are not allowed to push into this branch. Create another branch or open a merge request.'
- end
-
- context 'with rights to push to the repository' do
- before do
- project.add_maintainer(user)
- end
-
- context 'wrong target file extension' do
- let(:file_name) { 'custom_dashboard.txt' }
-
- it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'The file name should have a .yml extension'
- end
-
- context 'wrong source dashboard file' do
- let(:dashboard) { 'config/prometheus/common_metrics_123.yml' }
-
- it_behaves_like 'misconfigured dashboard service response with stepable', :not_found, 'Not found.'
- end
-
- context 'path traversal attack attempt' do
- let(:dashboard) { 'config/prometheus/../database.yml' }
-
- it_behaves_like 'misconfigured dashboard service response with stepable', :not_found, 'Not found.'
- end
-
- context 'path traversal attack attempt on target file' do
- let(:file_name) { '../../custom_dashboard.yml' }
- let(:dashboard_attrs) do
- {
- commit_message: commit_message,
- branch_name: branch,
- start_branch: project.default_branch,
- encoding: 'text',
- file_path: ".gitlab/dashboards/custom_dashboard.yml",
- file_content: file_content_hash.to_yaml
- }
- end
-
- it 'strips target file name to safe value', :aggregate_failures do
- allow(::Gitlab::Metrics::Dashboard::Processor).to receive(:new).and_return(double(process: file_content_hash))
- service_instance = instance_double(::Files::CreateService)
- expect(::Files::CreateService).to receive(:new).with(project, user, dashboard_attrs).and_return(service_instance)
- expect(service_instance).to receive(:execute).and_return(status: :success)
-
- service_call
- end
- end
-
- context 'valid parameters' do
- before do
- allow(::Gitlab::Metrics::Dashboard::Processor).to receive(:new).and_return(double(process: file_content_hash))
- end
-
- it_behaves_like 'valid dashboard cloning process', ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH,
- [
- ::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
- ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter
- ]
-
- context 'selected branch already exists' do
- let(:branch) { 'existing_branch' }
-
- before do
- project.repository.add_branch(user, branch, 'master')
- end
-
- it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'There was an error creating the dashboard, branch named: existing_branch already exists.'
-
- # temporary not available function for first iteration
- # follow up issue https://gitlab.com/gitlab-org/gitlab/issues/196237 which
- # require this feature
- # it 'pass correct params to Files::CreateService', :aggregate_failures do
- # project.repository.add_branch(user, branch, 'master')
- #
- # service_instance = instance_double(::Files::CreateService)
- # expect(::Files::CreateService).to receive(:new).with(project, user, dashboard_attrs).and_return(service_instance)
- # expect(service_instance).to receive(:execute).and_return(status: :success)
- #
- # service_call
- # end
- end
-
- context 'blank branch name' do
- let(:branch) { '' }
-
- it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'There was an error creating the dashboard, branch name is invalid.'
- end
-
- context 'dashboard file already exists' do
- let(:branch) { 'custom_dashboard' }
-
- before do
- Files::CreateService.new(
- project,
- user,
- commit_message: 'Create custom dashboard custom_dashboard.yml',
- branch_name: 'master',
- start_branch: 'master',
- file_path: ".gitlab/dashboards/custom_dashboard.yml",
- file_content: File.read('config/prometheus/common_metrics.yml')
- ).execute
- end
-
- it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, "A file with 'custom_dashboard.yml' already exists in custom_dashboard branch"
- end
-
- it 'extends dashboard template path to absolute url' do
- allow(::Files::CreateService).to receive(:new).and_return(double(execute: { status: :success }))
-
- expect_file_read(Rails.root.join('config/prometheus/common_metrics.yml'), content: '')
-
- service_call
- end
-
- context 'Files::CreateService success' do
- before do
- allow(::Files::CreateService).to receive(:new).and_return(double(execute: { status: :success }))
- end
-
- it 'clears dashboards cache' do
- expect(project.repository).to receive(:refresh_method_caches).with([:metrics_dashboard])
-
- service_call
- end
-
- it 'returns success', :aggregate_failures do
- result = service_call
- dashboard_details = {
- path: '.gitlab/dashboards/custom_dashboard.yml',
- display_name: 'custom_dashboard.yml',
- default: false,
- system_dashboard: false
- }
-
- expect(result[:status]).to be :success
- expect(result[:http_status]).to be :created
- expect(result[:dashboard]).to match dashboard_details
- end
- end
-
- context 'Files::CreateService fails' do
- before do
- allow(::Files::CreateService).to receive(:new).and_return(double(execute: { status: :error }))
- end
-
- it 'does NOT clear dashboards cache' do
- expect(project.repository).not_to receive(:refresh_method_caches)
-
- service_call
- end
-
- it 'returns error' do
- result = service_call
- expect(result[:status]).to be :error
- end
- end
- end
- end
- end
-end
diff --git a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
deleted file mode 100644
index 1643f552a70..00000000000
--- a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Metrics::Dashboard::DynamicEmbedService, :use_clean_rails_memory_store_caching,
- feature_category: :metrics do
- include MetricsDashboardHelpers
-
- let_it_be(:project) { build(:project) }
- let_it_be(:user) { create(:user) }
- let_it_be(:environment) { create(:environment, project: project) }
-
- before do
- project.add_maintainer(user) if user
- end
-
- let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
- let(:group) { 'Group A' }
- let(:title) { 'Super Chart A1' }
- let(:y_label) { 'y_label' }
-
- describe '.valid_params?' do
- let(:valid_params) do
- {
- embedded: true,
- dashboard_path: dashboard_path,
- group: group,
- title: title,
- y_label: y_label
- }
- end
-
- subject { described_class.valid_params?(params) }
-
- let(:params) { valid_params }
-
- it { is_expected.to be_truthy }
-
- context 'missing embedded' do
- let(:params) { valid_params.except(:embedded) }
-
- it { is_expected.to be_falsey }
- end
-
- context 'not embedded' do
- let(:params) { valid_params.merge(embedded: 'false') }
-
- it { is_expected.to be_falsey }
- end
-
- context 'undefined dashboard' do
- let(:params) { valid_params.except(:dashboard_path) }
-
- it { is_expected.to be_truthy }
- end
-
- context 'missing dashboard' do
- let(:dashboard) { '' }
-
- it { is_expected.to be_truthy }
- end
-
- context 'missing group' do
- let(:group) { '' }
-
- it { is_expected.to be_falsey }
- end
-
- context 'missing title' do
- let(:title) { '' }
-
- it { is_expected.to be_falsey }
- end
-
- context 'undefined y-axis label' do
- let(:params) { valid_params.except(:y_label) }
-
- it { is_expected.to be_falsey }
- end
- end
-
- describe '#get_dashboard' do
- let(:service_params) do
- [
- project,
- user,
- {
- environment: environment,
- dashboard_path: dashboard_path,
- group: group,
- title: title,
- y_label: y_label
- }
- ]
- end
-
- let(:service_call) { described_class.new(*service_params).get_dashboard }
-
- context 'when the dashboard does not exist' do
- it_behaves_like 'misconfigured dashboard service response', :not_found
- end
-
- context 'when the dashboard is exists' do
- let(:project) { project_with_dashboard(dashboard_path) }
-
- it_behaves_like 'valid embedded dashboard service response'
- it_behaves_like 'raises error for users with insufficient permissions'
-
- it 'caches the unprocessed dashboard for subsequent calls' do
- expect(YAML).to receive(:safe_load).once.and_call_original
-
- described_class.new(*service_params).get_dashboard
- described_class.new(*service_params).get_dashboard
- end
-
- context 'when the specified group is not present on the dashboard' do
- let(:group) { 'Group Not Found' }
-
- it_behaves_like 'misconfigured dashboard service response', :not_found
- end
-
- context 'when the specified title is not present on the dashboard' do
- let(:title) { 'Title Not Found' }
-
- it_behaves_like 'misconfigured dashboard service response', :not_found
- end
-
- context 'when the specified y-axis label is not present on the dashboard' do
- let(:y_label) { 'Y-Axis Not Found' }
-
- it_behaves_like 'misconfigured dashboard service response', :not_found
- end
- end
-
- shared_examples 'uses system dashboard' do
- it 'uses the overview dashboard' do
- expect(Gitlab::Metrics::Dashboard::Finder)
- .to receive(:find_raw)
- .with(project, dashboard_path: system_dashboard_path)
- .once
-
- service_call
- end
- end
-
- context 'when the dashboard is nil' do
- let(:dashboard_path) { nil }
-
- it_behaves_like 'uses system dashboard'
- end
-
- context 'when the dashboard is not present' do
- let(:dashboard_path) { '' }
-
- it_behaves_like 'uses system dashboard'
- end
- end
-end
diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
index cc1f83ddc2b..73932887cd9 100644
--- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb
+++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
@@ -17,91 +17,12 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :i
subject { service.execute(token_input) }
context 'with valid payload' do
- let_it_be(:alert_firing) { create(:prometheus_alert, project: project) }
- let_it_be(:alert_resolved) { create(:prometheus_alert, project: project) }
- let_it_be(:cluster, reload: true) { create(:cluster, :provided_by_user, projects: [project]) }
-
- let(:payload_raw) { prometheus_alert_payload(firing: [alert_firing], resolved: [alert_resolved]) }
+ let(:payload_raw) { prometheus_alert_payload(firing: ['Alert A'], resolved: ['Alert B']) }
let(:payload) { ActionController::Parameters.new(payload_raw).permit! }
let(:payload_alert_firing) { payload_raw['alerts'].first }
let(:token) { 'token' }
let(:source) { 'Prometheus' }
- context 'with environment specific clusters' do
- let(:prd_cluster) do
- cluster
- end
-
- let(:stg_cluster) do
- create(:cluster, :provided_by_user, projects: [project], enabled: true, environment_scope: 'stg/*')
- end
-
- let(:stg_environment) do
- create(:environment, project: project, name: 'stg/1')
- end
-
- let(:alert_firing) do
- create(:prometheus_alert, project: project, environment: stg_environment)
- end
-
- before do
- create(:clusters_integrations_prometheus, cluster: prd_cluster, alert_manager_token: token)
- create(:clusters_integrations_prometheus, cluster: stg_cluster, alert_manager_token: nil)
- end
-
- context 'without token' do
- let(:token_input) { nil }
-
- include_examples 'processes one firing and one resolved prometheus alerts'
- end
-
- context 'with token' do
- it_behaves_like 'alerts service responds with an error and takes no actions', :unauthorized
- end
- end
-
- context 'with project specific cluster using prometheus integration' do
- where(:cluster_enabled, :integration_enabled, :configured_token, :token_input, :result) do
- true | true | token | token | :success
- true | true | nil | nil | :success
- true | true | token | 'x' | :failure
- true | true | token | nil | :failure
- true | false | token | token | :failure
- false | true | token | token | :failure
- false | nil | nil | token | :failure
- end
-
- with_them do
- before do
- cluster.update!(enabled: cluster_enabled)
-
- unless integration_enabled.nil?
- create(
- :clusters_integrations_prometheus,
- cluster: cluster,
- enabled: integration_enabled,
- alert_manager_token: configured_token
- )
- end
- end
-
- case result = params[:result]
- when :success
- include_examples 'processes one firing and one resolved prometheus alerts'
- when :failure
- it_behaves_like 'alerts service responds with an error and takes no actions', :unauthorized
- else
- raise "invalid result: #{result.inspect}"
- end
- end
- end
-
- context 'without project specific cluster' do
- let_it_be(:cluster) { create(:cluster, enabled: true) }
-
- it_behaves_like 'alerts service responds with an error and takes no actions', :unauthorized
- end
-
context 'with manual prometheus installation' do
where(:alerting_setting, :configured_token, :token_input, :result) do
true | token | token | :success
@@ -230,7 +151,7 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :i
context 'with multiple firing alerts and resolving alerts' do
let(:payload_raw) do
- prometheus_alert_payload(firing: [alert_firing, alert_firing], resolved: [alert_resolved])
+ prometheus_alert_payload(firing: ['Alert A', 'Alert A'], resolved: ['Alert B'])
end
it 'processes Prometheus alerts' do
@@ -248,7 +169,7 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :i
context 'when payload exceeds max amount of processable alerts' do
# We are defining 2 alerts in payload_raw above
let(:max_alerts) { 1 }
- let(:fingerprint) { prometheus_alert_payload_fingerprint(alert_resolved) }
+ let(:fingerprint) { prometheus_alert_payload_fingerprint('Alert A') }
before do
stub_const("#{described_class}::PROCESS_MAX_ALERTS", max_alerts)
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index c937a93c6ef..4ba704532e6 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -89,6 +89,12 @@ RSpec.describe SearchService, feature_category: :global_search do
end
end
+ describe '#search_type' do
+ subject { described_class.new(user, search: valid_search).search_type }
+
+ it { is_expected.to eq('basic') }
+ end
+
describe '#show_snippets?' do
context 'when :snippets is \'true\'' do
it 'returns true' do
diff --git a/spec/support/helpers/prometheus_helpers.rb b/spec/support/helpers/prometheus_helpers.rb
index e1f5e6dee14..da80f6f08c2 100644
--- a/spec/support/helpers/prometheus_helpers.rb
+++ b/spec/support/helpers/prometheus_helpers.rb
@@ -240,12 +240,11 @@ module PrometheusHelpers
def prometheus_alert_payload(firing: [], resolved: [])
status = firing.any? ? 'firing' : 'resolved'
alerts = firing + resolved
- alert_name = alerts.first&.title || ''
- prometheus_metric_id = alerts.first&.prometheus_metric_id&.to_s
+ alert_name = alerts.first || ''
alerts_map = \
- firing.map { |alert| prometheus_map_alert_payload('firing', alert) } +
- resolved.map { |alert| prometheus_map_alert_payload('resolved', alert) }
+ firing.map { |title| prometheus_map_alert_payload('firing', title) } +
+ resolved.map { |title| prometheus_map_alert_payload('resolved', title) }
# See https://prometheus.io/docs/alerting/configuration/#%3Cwebhook_config%3E
{
@@ -257,9 +256,7 @@ module PrometheusHelpers
'alertname' => alert_name
},
'commonLabels' => {
- 'alertname' => alert_name,
- 'gitlab' => 'hook',
- 'gitlab_alert_id' => prometheus_metric_id
+ 'alertname' => alert_name
},
'commonAnnotations' => {},
'externalURL' => '',
@@ -267,22 +264,21 @@ module PrometheusHelpers
}
end
- def prometheus_alert_payload_fingerprint(prometheus_alert)
+ def prometheus_alert_payload_fingerprint(title)
# timestamp is hard-coded in #prometheus_map_alert_payload
- fingerprint = "#{prometheus_alert.prometheus_metric_id}/2018-09-24T08:57:31.095725221Z"
+ # sample fingerprint format comes from AlertManagement::Payload::Prometheus
+ fingerprint = ["2018-09-24T08:57:31.095725221Z", title].join('/')
Gitlab::AlertManagement::Fingerprint.generate(fingerprint)
end
private
- def prometheus_map_alert_payload(status, alert)
+ def prometheus_map_alert_payload(status, title)
{
'status' => status,
'labels' => {
- 'alertname' => alert.title,
- 'gitlab' => 'hook',
- 'gitlab_alert_id' => alert.prometheus_metric_id.to_s
+ 'alertname' => title
},
'annotations' => {},
'startsAt' => '2018-09-24T08:57:31.095725221Z',
diff --git a/spec/support/shared_examples/models/concerns/linkable_items_shared_examples.rb b/spec/support/shared_examples/models/concerns/linkable_items_shared_examples.rb
new file mode 100644
index 00000000000..efd27a051fe
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/linkable_items_shared_examples.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'includes LinkableItem concern' do
+ describe 'validation' do
+ let_it_be(:task) { create(:work_item, :task, project: project) }
+ let_it_be(:issue) { create(:work_item, :issue, project: project) }
+
+ subject(:link) { build(link_factory, source_id: source.id, target_id: target.id) }
+
+ describe '#check_existing_parent_link' do
+ shared_examples 'invalid due to existing link' do
+ it do
+ is_expected.to be_invalid
+ expect(link.errors.messages[:source]).to include("is a parent or child of this #{item_type}")
+ end
+ end
+
+ context 'without existing link parent' do
+ let(:source) { issue }
+ let(:target) { task }
+
+ it 'is valid' do
+ is_expected.to be_valid
+ expect(link.errors).to be_empty
+ end
+ end
+
+ context 'with existing link parent' do
+ let_it_be(:relationship) { create(:parent_link, work_item_parent: issue, work_item: task) }
+
+ it_behaves_like 'invalid due to existing link' do
+ let(:source) { issue }
+ let(:target) { task }
+ end
+
+ it_behaves_like 'invalid due to existing link' do
+ let(:source) { task }
+ let(:target) { issue }
+ end
+ end
+ end
+ end
+
+ describe 'Scopes' do
+ describe '.for_source' do
+ it 'includes linked items for source' do
+ source = item
+ link_1 = create(link_factory, source: source, target: item1)
+ link_2 = create(link_factory, source: source, target: item2)
+
+ result = described_class.for_source(source)
+
+ expect(result).to contain_exactly(link_1, link_2)
+ end
+ end
+
+ describe '.for_target' do
+ it 'includes linked items for target' do
+ target = item
+ link_1 = create(link_factory, source: item1, target: target)
+ link_2 = create(link_factory, source: item2, target: target)
+
+ result = described_class.for_target(target)
+
+ expect(result).to contain_exactly(link_1, link_2)
+ end
+ end
+
+ describe '.for_items' do
+ let_it_be(:source_link) { create(link_factory, source: item, target: item1) }
+ let_it_be(:target_link) { create(link_factory, source: item2, target: item) }
+
+ it 'includes links when item is source' do
+ expect(described_class.for_items(item, item1)).to contain_exactly(source_link)
+ end
+
+ it 'includes links when item is target' do
+ expect(described_class.for_items(item, item2)).to contain_exactly(target_link)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/issuable_link_shared_examples.rb b/spec/support/shared_examples/models/issuable_link_shared_examples.rb
index 42c7be5ddc3..af96b77edaf 100644
--- a/spec/support/shared_examples/models/issuable_link_shared_examples.rb
+++ b/spec/support/shared_examples/models/issuable_link_shared_examples.rb
@@ -7,8 +7,8 @@
# issuable_link_factory
RSpec.shared_examples 'issuable link' do
describe 'Associations' do
- it { is_expected.to belong_to(:source).class_name(issuable.class.name) }
- it { is_expected.to belong_to(:target).class_name(issuable.class.name) }
+ it { is_expected.to belong_to(:source).class_name(issuable_class) }
+ it { is_expected.to belong_to(:target).class_name(issuable_class) }
end
describe 'Validation' do
@@ -27,7 +27,8 @@ RSpec.shared_examples 'issuable link' do
issuable_link = create_issuable_link(subject.target, subject.source)
expect(issuable_link).to be_invalid
- expect(issuable_link.errors[:source]).to include("is already related to this #{issuable.class.name.downcase}")
+ expect(issuable_link.errors[:source])
+ .to include("is already related to this #{issuable.issuable_type.humanize(capitalize: false)}")
end
context 'when it relates to itself' do