Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
613868af23
commit
0045970352
|
|
@ -31,9 +31,7 @@ Rails/InverseOf:
|
|||
- 'app/models/jira_connect_subscription.rb'
|
||||
- 'app/models/members/group_member.rb'
|
||||
- 'app/models/members/project_member.rb'
|
||||
- 'app/models/merge_request.rb'
|
||||
- 'app/models/merge_request/metrics.rb'
|
||||
- 'app/models/merge_request_diff.rb'
|
||||
- 'app/models/namespace.rb'
|
||||
- 'app/models/notification_setting.rb'
|
||||
- 'app/models/packages/composer/cache_file.rb'
|
||||
|
|
@ -60,7 +58,6 @@ Rails/InverseOf:
|
|||
- 'ee/app/models/ee/clusters/agent.rb'
|
||||
- 'ee/app/models/ee/epic.rb'
|
||||
- 'ee/app/models/ee/group.rb'
|
||||
- 'ee/app/models/ee/merge_request.rb'
|
||||
- 'ee/app/models/ee/plan.rb'
|
||||
- 'ee/app/models/ee/project.rb'
|
||||
- 'ee/app/models/ee/service_desk_setting.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
11825b3ad89525b194cc4095e581eef843377cdb
|
||||
0b0e46fe69e9b94e3def2a9318188fcc0f3f00c6
|
||||
|
|
|
|||
|
|
@ -4,17 +4,6 @@
|
|||
box-shadow: inset 0 0 0 $gl-border-size-1 $red-500 if-important($important);
|
||||
}
|
||||
|
||||
.timezone-dropdown {
|
||||
.gl-dropdown-item-text-primary {
|
||||
@include gl-overflow-hidden;
|
||||
@include gl-text-overflow-ellipsis;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
@include gl-bg-gray-10;
|
||||
}
|
||||
|
|
@ -52,65 +41,17 @@ $scroll-top-gradient: linear-gradient(to bottom, $gradient-dark-gray 0%, $gradie
|
|||
$scroll-bottom-gradient: linear-gradient(to bottom, $gradient-gray 0%, $gradient-dark-gray 100%);
|
||||
$column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradient-gray 100%);
|
||||
|
||||
.schedule-shell {
|
||||
@include gl-relative;
|
||||
@include gl-h-full;
|
||||
@include gl-w-full;
|
||||
@include gl-overflow-x-auto;
|
||||
}
|
||||
|
||||
.timeline-section {
|
||||
@include gl-sticky;
|
||||
@include gl-top-0;
|
||||
z-index: 20;
|
||||
|
||||
.timeline-header-label,
|
||||
.timeline-header-item {
|
||||
@include gl-float-left;
|
||||
}
|
||||
|
||||
.timeline-header-label {
|
||||
@include gl-sticky;
|
||||
@include gl-top-0;
|
||||
@include gl-left-0;
|
||||
width: $details-cell-width;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.timeline-header-item {
|
||||
.item-sublabel .sublabel-value {
|
||||
color: var(--gray-700, $gray-700);
|
||||
@include gl-font-weight-normal;
|
||||
|
||||
&.label-dark {
|
||||
color: var(--gray-900, $gray-900);
|
||||
}
|
||||
|
||||
&.label-bold {
|
||||
@include gl-font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.item-sublabel {
|
||||
@include gl-relative;
|
||||
@include gl-display-flex;
|
||||
|
||||
.sublabel-value {
|
||||
@include gl-flex-grow-1;
|
||||
@include gl-flex-basis-0;
|
||||
|
||||
text-align: center;
|
||||
@include gl-font-base;
|
||||
}
|
||||
}
|
||||
|
||||
.current-day-indicator-header {
|
||||
@include gl-absolute;
|
||||
@include gl-bottom-0;
|
||||
height: $grid-size;
|
||||
width: $grid-size;
|
||||
background-color: var(--red-500, $red-500);
|
||||
@include gl-rounded-full;
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
|
||||
|
|
@ -137,35 +78,19 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
|
|||
|
||||
.details-cell,
|
||||
.timeline-cell {
|
||||
@include gl-float-left;
|
||||
height: $item-height;
|
||||
}
|
||||
|
||||
.details-cell {
|
||||
@include gl-sticky;
|
||||
@include gl-left-0;
|
||||
width: $details-cell-width;
|
||||
@include gl-font-base;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.timeline-cell {
|
||||
@include gl-relative;
|
||||
@include gl-bg-transparent;
|
||||
border-right: $border-style;
|
||||
|
||||
&:last-child {
|
||||
@include gl-border-r-0;
|
||||
}
|
||||
|
||||
.current-day-indicator {
|
||||
@include gl-absolute;
|
||||
top: -1px;
|
||||
width: $gl-spacing-scale-1;
|
||||
height: calc(100% + 1px);
|
||||
background-color: var(--red-500, $red-500);
|
||||
@include gl-pointer-events-none;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
module AlertManagement
|
||||
class HttpIntegrationsFinder
|
||||
def initialize(project, params)
|
||||
TYPE_IDENTIFIERS = ::AlertManagement::HttpIntegration.type_identifiers
|
||||
|
||||
def initialize(project, params = {})
|
||||
@project = project
|
||||
@params = params
|
||||
end
|
||||
|
|
@ -13,6 +15,7 @@ module AlertManagement
|
|||
filter_by_availability
|
||||
filter_by_endpoint_identifier
|
||||
filter_by_active
|
||||
filter_by_type
|
||||
|
||||
collection
|
||||
end
|
||||
|
|
@ -21,15 +24,13 @@ module AlertManagement
|
|||
|
||||
attr_reader :project, :params, :collection
|
||||
|
||||
# Overridden in EE
|
||||
def filter_by_availability
|
||||
return if multiple_alert_http_integrations?
|
||||
|
||||
first_id = project.alert_management_http_integrations
|
||||
.ordered_by_id
|
||||
.select(:id)
|
||||
.limit(1)
|
||||
|
||||
@collection = collection.id_in(first_id)
|
||||
# Re-find by id so subsequent filters don't expose unavailable records
|
||||
@collection = collection.id_in(collection
|
||||
.select('DISTINCT ON (type_identifier) id')
|
||||
.ordered_by_type_and_id
|
||||
.limit(TYPE_IDENTIFIERS.length))
|
||||
end
|
||||
|
||||
def filter_by_endpoint_identifier
|
||||
|
|
@ -44,9 +45,11 @@ module AlertManagement
|
|||
@collection = collection.active
|
||||
end
|
||||
|
||||
# Overridden in EE
|
||||
def multiple_alert_http_integrations?
|
||||
false
|
||||
def filter_by_type
|
||||
return unless params[:type_identifier]
|
||||
return unless TYPE_IDENTIFIERS.include?(params[:type_identifier])
|
||||
|
||||
@collection = collection.for_type(params[:type_identifier])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
module AlertManagement
|
||||
class HttpIntegration < ApplicationRecord
|
||||
include ::Gitlab::Routing
|
||||
|
||||
LEGACY_IDENTIFIER = 'legacy'
|
||||
DEFAULT_NAME_SLUG = 'http-endpoint'
|
||||
|
||||
belongs_to :project, inverse_of: :alert_management_http_integrations
|
||||
|
||||
|
|
@ -19,6 +19,7 @@ module AlertManagement
|
|||
validates :active, inclusion: { in: [true, false] }
|
||||
validates :token, presence: true, format: { with: /\A\h{32}\z/ }
|
||||
validates :name, presence: true, length: { maximum: 255 }
|
||||
validates :type_identifier, presence: true
|
||||
validates :endpoint_identifier, presence: true, length: { maximum: 255 }, format: { with: /\A[A-Za-z0-9]+\z/ }
|
||||
validates :endpoint_identifier, uniqueness: { scope: [:project_id, :active] }, if: :active?
|
||||
validates :payload_attribute_mapping, json_schema: { filename: 'http_integration_payload_attribute_mapping' }
|
||||
|
|
@ -29,15 +30,30 @@ module AlertManagement
|
|||
before_validation :ensure_payload_example_not_nil
|
||||
|
||||
scope :for_endpoint_identifier, ->(endpoint_identifier) { where(endpoint_identifier: endpoint_identifier) }
|
||||
scope :for_type, ->(type) { where(type_identifier: type) }
|
||||
scope :for_project, ->(project_ids) { where(project: project_ids) }
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :ordered_by_id, -> { order(:id) }
|
||||
scope :legacy, -> { for_endpoint_identifier(LEGACY_IDENTIFIER) }
|
||||
scope :ordered_by_type_and_id, -> { order(:type_identifier, :id) }
|
||||
|
||||
enum type_identifier: {
|
||||
http: 0,
|
||||
prometheus: 1
|
||||
}
|
||||
|
||||
def url
|
||||
return project_alerts_notify_url(project, format: :json) if legacy?
|
||||
if legacy?
|
||||
return project_alerts_notify_url(project, format: :json) if http?
|
||||
return notify_project_prometheus_alerts_url(project, format: :json) if prometheus?
|
||||
end
|
||||
|
||||
project_alert_http_integration_url(project, name_slug, endpoint_identifier, format: :json)
|
||||
end
|
||||
|
||||
def legacy?
|
||||
endpoint_identifier == LEGACY_IDENTIFIER
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.generate_token
|
||||
|
|
@ -45,11 +61,7 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def name_slug
|
||||
(name && Gitlab::Utils.slugify(name)) || DEFAULT_NAME_SLUG
|
||||
end
|
||||
|
||||
def legacy?
|
||||
endpoint_identifier == LEGACY_IDENTIFIER
|
||||
(name && Gitlab::Utils.slugify(name)) || "#{type_identifier}-endpoint"
|
||||
end
|
||||
|
||||
# Blank token assignment triggers token reset
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ class MergeRequest < ApplicationRecord
|
|||
dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
||||
has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
|
||||
has_many :pipelines_for_merge_request, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline'
|
||||
has_many :pipelines_for_merge_request, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline', inverse_of: :merge_request
|
||||
has_many :suggestions, through: :notes
|
||||
has_many :unresolved_notes, -> { unresolved }, as: :noteable, class_name: 'Note'
|
||||
has_many :unresolved_notes, -> { unresolved }, as: :noteable, class_name: 'Note', inverse_of: :noteable
|
||||
|
||||
has_many :merge_request_assignees
|
||||
has_many :assignees, class_name: "User", through: :merge_request_assignees
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class MergeRequestDiff < ApplicationRecord
|
|||
-> { order(:merge_request_diff_id, :relative_order) },
|
||||
inverse_of: :merge_request_diff
|
||||
|
||||
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }
|
||||
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }, inverse_of: :merge_request_diff
|
||||
|
||||
validates :base_commit_sha, :head_commit_sha, :start_commit_sha, sha: true
|
||||
validates :merge_request_id, uniqueness: { scope: :diff_type }, if: :merge_head?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AlertManagement
|
||||
module HttpIntegrations
|
||||
class BaseService < BaseProjectService
|
||||
# @param project [Project]
|
||||
# @param current_user [User]
|
||||
# @param params [Hash]
|
||||
def initialize(project, current_user, params)
|
||||
@response = nil
|
||||
|
||||
super(project: project, current_user: current_user, params: params.with_indifferent_access)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed?
|
||||
current_user&.can?(:admin_operations, project)
|
||||
end
|
||||
|
||||
def too_many_integrations?(integration)
|
||||
AlertManagement::HttpIntegration
|
||||
.for_project(integration.project_id)
|
||||
.for_type(integration.type_identifier)
|
||||
.id_not_in(integration.id)
|
||||
.any?
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.slice(*permitted_params_keys)
|
||||
end
|
||||
|
||||
# overriden in EE
|
||||
def permitted_params_keys
|
||||
%i[name active type_identifier]
|
||||
end
|
||||
|
||||
def error(message)
|
||||
ServiceResponse.error(message: message)
|
||||
end
|
||||
|
||||
def success(integration)
|
||||
ServiceResponse.success(payload: { integration: integration.reset })
|
||||
end
|
||||
|
||||
def error_multiple_integrations
|
||||
error(_('Multiple integrations of a single type are not supported for this project'))
|
||||
end
|
||||
|
||||
def error_on_save(integration)
|
||||
error(integration.errors.full_messages.to_sentence)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::AlertManagement::HttpIntegrations::BaseService.prepend_mod
|
||||
|
|
@ -2,68 +2,34 @@
|
|||
|
||||
module AlertManagement
|
||||
module HttpIntegrations
|
||||
class CreateService
|
||||
# @param project [Project]
|
||||
# @param current_user [User]
|
||||
# @param params [Hash]
|
||||
def initialize(project, current_user, params)
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
@params = params.with_indifferent_access
|
||||
end
|
||||
|
||||
class CreateService < BaseService
|
||||
def execute
|
||||
return error_no_permissions unless allowed?
|
||||
return error_multiple_integrations unless creation_allowed?
|
||||
|
||||
integration = project.alert_management_http_integrations.create(permitted_params)
|
||||
return error_in_create(integration) unless integration.valid?
|
||||
::AlertManagement::HttpIntegration.transaction do
|
||||
integration = project.alert_management_http_integrations.build(permitted_params)
|
||||
|
||||
success(integration)
|
||||
if integration.save
|
||||
@response = success(integration)
|
||||
|
||||
if too_many_integrations?(integration)
|
||||
@response = error_multiple_integrations
|
||||
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
else
|
||||
@response = error_on_save(integration)
|
||||
end
|
||||
end
|
||||
|
||||
@response
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project, :current_user, :params
|
||||
|
||||
def allowed?
|
||||
current_user&.can?(:admin_operations, project)
|
||||
end
|
||||
|
||||
def creation_allowed?
|
||||
project.alert_management_http_integrations.empty?
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.slice(*permitted_params_keys)
|
||||
end
|
||||
|
||||
# overriden in EE
|
||||
def permitted_params_keys
|
||||
%i[name active]
|
||||
end
|
||||
|
||||
def error(message)
|
||||
ServiceResponse.error(message: message)
|
||||
end
|
||||
|
||||
def success(integration)
|
||||
ServiceResponse.success(payload: { integration: integration })
|
||||
end
|
||||
|
||||
def error_no_permissions
|
||||
error(_('You have insufficient permissions to create an HTTP integration for this project'))
|
||||
end
|
||||
|
||||
def error_multiple_integrations
|
||||
error(_('Multiple HTTP integrations are not supported for this project'))
|
||||
end
|
||||
|
||||
def error_in_create(integration)
|
||||
error(integration.errors.full_messages.to_sentence)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::AlertManagement::HttpIntegrations::CreateService.prepend_mod_with('AlertManagement::HttpIntegrations::CreateService')
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module AlertManagement
|
|||
|
||||
def execute
|
||||
return error_no_permissions unless allowed?
|
||||
return error_legacy_prometheus unless destroy_allowed?
|
||||
|
||||
if integration.destroy
|
||||
success
|
||||
|
|
@ -28,6 +29,12 @@ module AlertManagement
|
|||
current_user&.can?(:admin_operations, integration)
|
||||
end
|
||||
|
||||
# Prevents downtime while migrating from Integrations::Prometheus.
|
||||
# Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/409734
|
||||
def destroy_allowed?
|
||||
!(integration.legacy? && integration.prometheus?)
|
||||
end
|
||||
|
||||
def error(message)
|
||||
ServiceResponse.error(message: message)
|
||||
end
|
||||
|
|
@ -39,6 +46,10 @@ module AlertManagement
|
|||
def error_no_permissions
|
||||
error(_('You have insufficient permissions to remove this HTTP integration'))
|
||||
end
|
||||
|
||||
def error_legacy_prometheus
|
||||
error(_('Legacy Prometheus integrations cannot currently be removed'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,51 +2,48 @@
|
|||
|
||||
module AlertManagement
|
||||
module HttpIntegrations
|
||||
class UpdateService
|
||||
class UpdateService < BaseService
|
||||
# @param integration [AlertManagement::HttpIntegration]
|
||||
# @param current_user [User]
|
||||
# @param params [Hash]
|
||||
def initialize(integration, current_user, params)
|
||||
@integration = integration
|
||||
@current_user = current_user
|
||||
@params = params.with_indifferent_access
|
||||
|
||||
super(integration.project, current_user, params)
|
||||
end
|
||||
|
||||
def execute
|
||||
return error_no_permissions unless allowed?
|
||||
|
||||
params[:token] = nil if params.delete(:regenerate_token)
|
||||
integration.transaction do
|
||||
if integration.update(permitted_params.merge(token_params))
|
||||
@response = success(integration)
|
||||
|
||||
if integration.update(permitted_params)
|
||||
success
|
||||
else
|
||||
error(integration.errors.full_messages.to_sentence)
|
||||
if type_update? && too_many_integrations?(integration)
|
||||
@response = error_multiple_integrations
|
||||
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
else
|
||||
@response = error_on_save(integration)
|
||||
end
|
||||
end
|
||||
|
||||
@response
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :integration, :current_user, :params
|
||||
attr_reader :integration
|
||||
|
||||
def allowed?
|
||||
current_user&.can?(:admin_operations, integration)
|
||||
def token_params
|
||||
return {} unless params[:regenerate_token]
|
||||
|
||||
{ token: nil }
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.slice(*permitted_params_keys)
|
||||
end
|
||||
|
||||
# overriden in EE
|
||||
def permitted_params_keys
|
||||
%i[name active token]
|
||||
end
|
||||
|
||||
def error(message)
|
||||
ServiceResponse.error(message: message)
|
||||
end
|
||||
|
||||
def success
|
||||
ServiceResponse.success(payload: { integration: integration.reset })
|
||||
def type_update?
|
||||
params[:type_identifier].present?
|
||||
end
|
||||
|
||||
def error_no_permissions
|
||||
|
|
@ -55,5 +52,3 @@ module AlertManagement
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
::AlertManagement::HttpIntegrations::UpdateService.prepend_mod_with('AlertManagement::HttpIntegrations::UpdateService')
|
||||
|
|
|
|||
|
|
@ -79,12 +79,18 @@ module Projects
|
|||
end
|
||||
|
||||
def valid_alert_manager_token?(token, integration)
|
||||
valid_for_manual?(token) ||
|
||||
valid_for_alerts_endpoint?(token, integration) ||
|
||||
valid_for_alerts_endpoint?(token, integration) ||
|
||||
valid_for_manual?(token) ||
|
||||
valid_for_cluster?(token)
|
||||
end
|
||||
|
||||
def valid_for_manual?(token)
|
||||
# If migration from Integrations::Prometheus to
|
||||
# AlertManagement::HttpIntegrations is complete,
|
||||
# we should use use the HttpIntegration as SSOT.
|
||||
# Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/409734
|
||||
return false if project.alert_management_http_integrations.legacy.prometheus.any?
|
||||
|
||||
prometheus = project.find_or_initialize_integration('prometheus')
|
||||
return false unless prometheus.manual_configuration?
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: npm_group_level_endpoints
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119073
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409476
|
||||
milestone: '16.0'
|
||||
type: development
|
||||
group: group::package registry
|
||||
default_enabled: false
|
||||
|
|
@ -3,7 +3,7 @@ key_path: redis_hll_counters.user_container_registry.i_container_registry_writes
|
|||
description: A monthly count of unique users that have executed write operations to the registry
|
||||
product_section: ops
|
||||
product_stage: package
|
||||
product_group: package
|
||||
product_group: container_registry
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.0"
|
||||
|
|
@ -22,7 +22,8 @@ options:
|
|||
- i_container_registry_delete_repository_user
|
||||
- i_container_registry_create_repository_user
|
||||
- i_container_registry_push_repository_user
|
||||
performance_indicator_type: []
|
||||
performance_indicator_type:
|
||||
- gmau
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTypeToHttpIntegrations < Gitlab::Database::Migration[2.1]
|
||||
def change
|
||||
add_column :alert_management_http_integrations, :type_identifier, :integer, default: 0, null: false, limit: 2
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
ad16293967c9751d138690328308944dd0930cd88e1afa16d825fbaf2cc8299c
|
||||
|
|
@ -11042,6 +11042,7 @@ CREATE TABLE alert_management_http_integrations (
|
|||
name text NOT NULL,
|
||||
payload_example jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
payload_attribute_mapping jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
type_identifier smallint DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT check_286943b636 CHECK ((char_length(encrypted_token_iv) <= 255)),
|
||||
CONSTRAINT check_392143ccf4 CHECK ((char_length(name) <= 255)),
|
||||
CONSTRAINT check_e270820180 CHECK ((char_length(endpoint_identifier) <= 255)),
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ different scopes:
|
|||
|
||||
- Use the instance-level prefix to make requests in the scope of the entire instance.
|
||||
- Use the project-level prefix to make requests in a single project's scope.
|
||||
- Use the group-level prefix to make requests in a group’s scope.
|
||||
|
||||
The examples in this document all use the project-level prefix.
|
||||
|
||||
|
|
@ -147,6 +148,22 @@ The examples in this document all use the project-level prefix.
|
|||
| --------- | ------ | -------- | ----------- |
|
||||
| `id` | string | yes | The project ID or full project path. |
|
||||
|
||||
### Group-level
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299834) in GitLab 16.0 [with a flag](../../administration/feature_flags.md) named `npm_group_level_endpoints`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `npm_group_level_endpoints`.
|
||||
The feature is not ready for production use.
|
||||
|
||||
```plaintext
|
||||
/groups/:id/-/packages/npm`
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ------ | -------- | ----------- |
|
||||
| `id` | string | yes | The group ID or full group path. |
|
||||
|
||||
## Metadata
|
||||
|
||||
Returns the metadata for a given package.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Users API **(FREE)**
|
||||
|
||||
This documentation has information on API calls, parameters and responses for the Users API.
|
||||
This documentation has information on API calls, parameters and responses for the Users API.
|
||||
|
||||
For information on user activities that update the user event timestamps, see [get user activities](#get-user-activities).
|
||||
|
||||
|
|
@ -875,7 +875,7 @@ Parameters:
|
|||
| :------------------------------- | :------- | :--------------------------------------------------------------------------- |
|
||||
| `view_diffs_file_by_file` | Yes | Flag indicating the user sees only one file diff per page. |
|
||||
| `show_whitespace_in_diffs` | Yes | Flag indicating the user sees whitespace changes in diffs. |
|
||||
| `pass_user_identities_to_ci_jwt` | Yes | Flag indicating the user passes their external identities as CI information. This attribute does not contain enough information to identify or authorize the user in an external system. The attribute is internal to GitLab, and must not be passed to third-party services. |
|
||||
| `pass_user_identities_to_ci_jwt` | Yes | Flag indicating the user passes their external identities as CI information. This attribute does not contain enough information to identify or authorize the user in an external system. The attribute is internal to GitLab, and must not be passed to third-party services. For more information and examples, see [Token Payload](../ci/secrets/id_token_authentication.md#token-payload). |
|
||||
|
||||
## User follow
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ The token also includes custom claims provided by GitLab:
|
|||
| `user_id` | Always | ID of the user executing the job. |
|
||||
| `user_login` | Always | Username of the user executing the job. |
|
||||
| `user_email` | Always | Email of the user executing the job. |
|
||||
| `user_identities` | User Preference setting | List of the user's external identities ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/387537) in GitLab 16.0). |
|
||||
| `pipeline_id` | Always | ID of the pipeline. |
|
||||
| `pipeline_source` | Always | [Pipeline source](../jobs/job_control.md#common-if-clauses-for-rules). |
|
||||
| `job_id` | Always | ID of the job. |
|
||||
|
|
@ -83,6 +84,10 @@ The token also includes custom claims provided by GitLab:
|
|||
"user_id": "1",
|
||||
"user_login": "sample-user",
|
||||
"user_email": "sample-user@example.com",
|
||||
"user_identities": [
|
||||
{"provider": "github", "extern_uid": "2435223452345"},
|
||||
{"provider": "bitbucket", "extern_uid": "john.smith"},
|
||||
],
|
||||
"pipeline_id": "574",
|
||||
"pipeline_source": "push",
|
||||
"job_id": "302",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ When you select a row in the compliance report, a drawer appears that provides:
|
|||
|
||||
### View the compliance violations report for a group
|
||||
|
||||
> Target branch search [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358414) in GitLab 16.0.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must be an administrator or have the Owner role for the group.
|
||||
|
|
@ -50,6 +52,12 @@ You can sort the compliance report on:
|
|||
- Type of violation.
|
||||
- Merge request title.
|
||||
|
||||
You can filter the compliance violations report on:
|
||||
|
||||
- Project.
|
||||
- Date range of merge.
|
||||
- Target branch.
|
||||
|
||||
Select a row to see details of the compliance violation.
|
||||
|
||||
#### Severity levels
|
||||
|
|
|
|||
|
|
@ -190,8 +190,7 @@ During provisioning:
|
|||
|
||||
- Both primary and secondary emails are considered when checking whether a GitLab user account exists.
|
||||
- Duplicate usernames are handled by adding suffix `1` when creating the user. For example, if `test_user` already
|
||||
exists, `test_user1` is used. If `test_user1` already exists, GitLab increments the suffix until an unused username
|
||||
is found.
|
||||
exists, `test_user1` is used. If `test_user1` already exists, GitLab increments the suffix to find an unused username. If no unused username is found after 4 tries, a random string is attached to the username.
|
||||
|
||||
On subsequent visits, new and existing users can access groups either:
|
||||
|
||||
|
|
|
|||
|
|
@ -119,15 +119,16 @@ Your package should now publish to the Package Registry when the pipeline runs.
|
|||
|
||||
If multiple packages have the same name and version, when you install a package, the most recently-published package is retrieved.
|
||||
|
||||
You can install a package from a GitLab project or instance:
|
||||
You can install a package from a GitLab project, group, or instance:
|
||||
|
||||
- **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace.
|
||||
- **Group-level**: Use when you have many npm packages in different projects in the same GitLab group.
|
||||
- **Project-level**: Use when you have few npm packages and they are not in the same GitLab group.
|
||||
|
||||
### Authenticate to the Package Registry
|
||||
|
||||
You must authenticate to the Package Registry to install a package from a private project.
|
||||
No authentication is needed if the project is public.
|
||||
You must authenticate to the Package Registry to install a package from a private project or a private group.
|
||||
No authentication is needed if the project or the group is public.
|
||||
|
||||
To authenticate with `npm`:
|
||||
|
||||
|
|
@ -145,7 +146,13 @@ If you're installing:
|
|||
npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
|
||||
```
|
||||
|
||||
From the project level:
|
||||
- From the group level:
|
||||
|
||||
```shell
|
||||
npm config set -- //your_domain_name/api/v4/groups/your_group_id/-/packages/npm/:_authToken=your_token
|
||||
```
|
||||
|
||||
- From the project level:
|
||||
|
||||
```shell
|
||||
npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token
|
||||
|
|
@ -154,6 +161,7 @@ If you're installing:
|
|||
In these examples:
|
||||
|
||||
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
|
||||
- Replace `your_group_id` with your group ID, found on the group's home page.
|
||||
- Replace `your_project_id` is your project ID, found on the project's home page.
|
||||
- Replace `your_token` with a deploy token, group access token, project access token, or personal access token.
|
||||
|
||||
|
|
@ -185,6 +193,32 @@ To install a package from the instance level, the package must have been publish
|
|||
npm install @scope/my-package
|
||||
```
|
||||
|
||||
### Install from the group level
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299834) in GitLab 16.0 [with a flag](../../../administration/feature_flags.md) named `npm_group_level_endpoints`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `npm_group_level_endpoints`.
|
||||
The feature is not ready for production use.
|
||||
|
||||
1. [Authenticate to the Package Registry](#authenticate-to-the-package-registry).
|
||||
|
||||
1. Set the registry
|
||||
|
||||
```shell
|
||||
npm config set @scope:registry=https://your_domain_name/api/v4/groups/your_group_id/-/packages/npm/
|
||||
```
|
||||
|
||||
- Replace `@scope` with the [root level group](#naming-convention) of the group you're installing to the package from.
|
||||
- Replace `your_domain_name` with your domain name, for example, `gitlab.com`.
|
||||
- Replace `your_group_id` is your group ID, found on the group's home page.
|
||||
|
||||
1. Install the package
|
||||
|
||||
```shell
|
||||
npm install @scope/my-package
|
||||
```
|
||||
|
||||
### Install from the project level
|
||||
|
||||
1. [Authenticate to the Package Registry](#authenticate-to-the-package-registry).
|
||||
|
|
|
|||
|
|
@ -182,6 +182,13 @@ NOTE:
|
|||
This feature is experimental, and choosing absolute times might break certain layouts.
|
||||
Open an issue if you notice that using absolute times breaks a layout.
|
||||
|
||||
## User identities in CI job JSON web tokens
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/387537) in GitLab 16.0. False by default.
|
||||
|
||||
You can select to include the list of your external identities in the JSON Web Token information that is generated for a CI job.
|
||||
For more information and examples, see [Token Payload](../../ci/secrets/id_token_authentication.md#token-payload).
|
||||
|
||||
## Integrations
|
||||
|
||||
Configure your preferences with third-party services which provide enhancements to your GitLab experience.
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@ module API
|
|||
mount ::API::Metrics::Dashboard::Annotations
|
||||
mount ::API::Metrics::UserStarredDashboards
|
||||
mount ::API::Namespaces
|
||||
mount ::API::NpmGroupPackages
|
||||
mount ::API::NpmInstancePackages
|
||||
mount ::API::NpmProjectPackages
|
||||
mount ::API::NugetGroupPackages
|
||||
|
|
|
|||
|
|
@ -11,16 +11,12 @@ module API
|
|||
package_name: API::NO_SLASH_URL_PART_REGEX
|
||||
}.freeze
|
||||
|
||||
def endpoint_scope
|
||||
params[:id].present? ? :project : :instance
|
||||
end
|
||||
|
||||
def project
|
||||
strong_memoize(:project) do
|
||||
case endpoint_scope
|
||||
when :project
|
||||
user_project(action: :read_package)
|
||||
when :instance
|
||||
when :instance, :group
|
||||
# Simulate the same behavior as #user_project by re-using #find_project!
|
||||
# but take care if the project_id is nil as #find_project! is not designed
|
||||
# to handle it.
|
||||
|
|
@ -39,6 +35,8 @@ module API
|
|||
::Packages::Npm::PackageFinder.new(package_name, project: project_or_nil)
|
||||
when :instance
|
||||
::Packages::Npm::PackageFinder.new(package_name, namespace: top_namespace_from(package_name))
|
||||
when :group
|
||||
::Packages::Npm::PackageFinder.new(package_name, namespace: group)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -57,6 +55,14 @@ module API
|
|||
case endpoint_scope
|
||||
when :project
|
||||
params[:id]
|
||||
when :group
|
||||
finder = ::Packages::Npm::PackageFinder.new(
|
||||
params[:package_name],
|
||||
namespace: group,
|
||||
last_of_each_version: false
|
||||
)
|
||||
|
||||
finder.last&.project_id
|
||||
when :instance
|
||||
package_name = params[:package_name]
|
||||
|
||||
|
|
@ -91,6 +97,13 @@ module API
|
|||
|
||||
Namespace.top_most.by_path(namespace_path)
|
||||
end
|
||||
|
||||
def group
|
||||
group = find_group(params[:id])
|
||||
not_found!('Group') unless can?(current_user, :read_group, group)
|
||||
group
|
||||
end
|
||||
strong_memoize_attr :group
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class NpmGroupPackages < ::API::Base
|
||||
helpers ::API::Helpers::Packages::Npm
|
||||
|
||||
feature_category :package_registry
|
||||
urgency :low
|
||||
|
||||
helpers do
|
||||
def endpoint_scope
|
||||
:group
|
||||
end
|
||||
end
|
||||
|
||||
after_validation do
|
||||
not_found! unless Feature.enabled?(:npm_group_level_endpoints, group)
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the group'
|
||||
end
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
namespace ':id/-/packages/npm' do
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
end
|
||||
namespace '-/package/*package_name' do
|
||||
get 'dist-tags', format: false do
|
||||
not_found!
|
||||
end
|
||||
|
||||
namespace 'dist-tags/:tag' do
|
||||
put format: false do
|
||||
not_found!
|
||||
end
|
||||
|
||||
delete format: false do
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post '-/npm/v1/security/audits/quick' do
|
||||
not_found!
|
||||
end
|
||||
|
||||
post '-/npm/v1/security/advisories/bulk' do
|
||||
not_found!
|
||||
end
|
||||
|
||||
include ::API::Concerns::Packages::NpmEndpoints
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10,6 +10,12 @@ module API
|
|||
render_api_error!(e.message, 400)
|
||||
end
|
||||
|
||||
helpers do
|
||||
def endpoint_scope
|
||||
:instance
|
||||
end
|
||||
end
|
||||
|
||||
namespace 'packages/npm' do
|
||||
include ::API::Concerns::Packages::NpmEndpoints
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ module API
|
|||
render_api_error!(e.message, 400)
|
||||
end
|
||||
|
||||
helpers do
|
||||
def endpoint_scope
|
||||
:project
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1239,7 +1239,7 @@ module API
|
|||
params do
|
||||
optional :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page'
|
||||
optional :show_whitespace_in_diffs, type: Boolean, desc: 'Flag indicating the user sees whitespace changes in diffs'
|
||||
optional :pass_user_identities_to_ci_jwt, type: Boolean, desc: 'Flag indicating the user passes their external identities as CI information'
|
||||
optional :pass_user_identities_to_ci_jwt, type: Boolean, desc: 'Flag indicating the user passes their external identities to a CI job as part of a JSON web token.'
|
||||
at_least_one_of :view_diffs_file_by_file, :show_whitespace_in_diffs, :pass_user_identities_to_ci_jwt
|
||||
end
|
||||
put "preferences", feature_category: :user_profile, urgency: :high do
|
||||
|
|
|
|||
|
|
@ -26475,6 +26475,9 @@ msgstr ""
|
|||
msgid "Leave zen mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Legacy Prometheus integrations cannot currently be removed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Legacy Web IDE"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -29161,15 +29164,15 @@ msgstr ""
|
|||
msgid "Multi-project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multiple HTTP integrations are not supported for this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multiple IP address ranges are supported. Does not affect access to the group's settings."
|
||||
msgstr ""
|
||||
|
||||
msgid "Multiple Prometheus integrations are not supported"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multiple integrations of a single type are not supported for this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multiple signatures"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -39,19 +39,20 @@ module QA
|
|||
end
|
||||
|
||||
def network
|
||||
shell "docker network inspect #{@network}"
|
||||
rescue CommandError
|
||||
'bridge'
|
||||
else
|
||||
@network
|
||||
network_exists?(@network) ? @network : 'bridge'
|
||||
end
|
||||
|
||||
def runner_network
|
||||
shell "docker network inspect #{@runner_network}"
|
||||
rescue CommandError
|
||||
network
|
||||
else
|
||||
@runner_network
|
||||
network_exists?(@runner_network) ? @runner_network : network
|
||||
end
|
||||
|
||||
def inspect_network(name)
|
||||
shell("docker network inspect #{name}", fail_on_exception: false, return_exit_status: true)
|
||||
end
|
||||
|
||||
def network_exists?(name)
|
||||
_, status = inspect_network(name)
|
||||
status == 0
|
||||
end
|
||||
|
||||
def pull
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ module QA
|
|||
|
||||
module_function
|
||||
|
||||
def shell(command, stdin_data: nil, fail_on_exception: true, stream_progress: true, mask_secrets: []) # rubocop:disable Metrics/CyclomaticComplexity
|
||||
def shell(command, stdin_data: nil, fail_on_exception: true, stream_progress: true, mask_secrets: [], return_exit_status: false) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||
cmd_string = Array(command).join(' ')
|
||||
cmd_output = ''
|
||||
exit_status = 0
|
||||
|
||||
QA::Runtime::Logger.info("Executing: `#{mask_secrets_on_string(cmd_string, mask_secrets).cyan}`")
|
||||
|
||||
|
|
@ -36,7 +37,9 @@ module QA
|
|||
# add newline after progress dots
|
||||
puts if print_progress_dots && !cmd_output.empty?
|
||||
|
||||
if wait.value.exited? && wait.value.exitstatus.nonzero? && fail_on_exception
|
||||
exit_status = wait.value.exitstatus if wait.value.exited?
|
||||
|
||||
if exit_status.nonzero? && fail_on_exception
|
||||
Runtime::Logger.error("Command output:\n#{cmd_output.strip}") unless cmd_output.empty?
|
||||
raise CommandError, "Command: `#{mask_secrets_on_string(cmd_string, mask_secrets)}` failed! ✘"
|
||||
end
|
||||
|
|
@ -44,7 +47,7 @@ module QA
|
|||
Runtime::Logger.debug("Command output:\n#{cmd_output.strip}") unless cmd_output.empty?
|
||||
end
|
||||
|
||||
cmd_output.strip
|
||||
return_exit_status ? [cmd_output.strip, exit_status] : cmd_output.strip
|
||||
end
|
||||
|
||||
def sql_to_docker_exec_cmd(sql, username, password, database, host, container)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ FactoryBot.define do
|
|||
endpoint_identifier { 'legacy' }
|
||||
end
|
||||
|
||||
trait :prometheus do
|
||||
type_identifier { :prometheus }
|
||||
end
|
||||
|
||||
initialize_with { new(**attributes) }
|
||||
|
||||
factory :alert_management_prometheus_integration, traits: [:prometheus]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AlertManagement::HttpIntegrationsFinder do
|
||||
RSpec.describe AlertManagement::HttpIntegrationsFinder, feature_category: :incident_management do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project ) }
|
||||
let_it_be(:extra_integration) { create(:alert_management_http_integration, project: project ) }
|
||||
let_it_be(:prometheus_integration) { create(:alert_management_prometheus_integration, :inactive, project: project ) }
|
||||
let_it_be(:extra_prometheus_integration) { create(:alert_management_prometheus_integration, project: project ) }
|
||||
let_it_be(:alt_project_integration) { create(:alert_management_http_integration) }
|
||||
|
||||
let(:params) { {} }
|
||||
|
|
@ -14,7 +16,7 @@ RSpec.describe AlertManagement::HttpIntegrationsFinder do
|
|||
subject(:execute) { described_class.new(project, params).execute }
|
||||
|
||||
context 'empty params' do
|
||||
it { is_expected.to contain_exactly(integration) }
|
||||
it { is_expected.to contain_exactly(integration, prometheus_integration) }
|
||||
end
|
||||
|
||||
context 'endpoint_identifier param given' do
|
||||
|
|
@ -37,7 +39,7 @@ RSpec.describe AlertManagement::HttpIntegrationsFinder do
|
|||
context 'but blank' do
|
||||
let(:params) { { endpoint_identifier: nil } }
|
||||
|
||||
it { is_expected.to contain_exactly(integration) }
|
||||
it { is_expected.to contain_exactly(integration, prometheus_integration) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -46,18 +48,34 @@ RSpec.describe AlertManagement::HttpIntegrationsFinder do
|
|||
|
||||
it { is_expected.to contain_exactly(integration) }
|
||||
|
||||
context 'when integration is disabled' do
|
||||
before do
|
||||
integration.update!(active: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
|
||||
context 'but blank' do
|
||||
let(:params) { { active: nil } }
|
||||
|
||||
it { is_expected.to contain_exactly(integration) }
|
||||
it { is_expected.to contain_exactly(integration, prometheus_integration) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'type_identifier param given' do
|
||||
let(:params) { { type_identifier: extra_integration.type_identifier } }
|
||||
|
||||
it { is_expected.to contain_exactly(integration) }
|
||||
|
||||
context 'matches an unavailable integration' do
|
||||
let(:params) { { type_identifier: extra_prometheus_integration.type_identifier } }
|
||||
|
||||
it { is_expected.to contain_exactly(prometheus_integration) }
|
||||
end
|
||||
|
||||
context 'but unknown' do
|
||||
let(:params) { { type_identifier: :unknown } }
|
||||
|
||||
it { is_expected.to contain_exactly(integration, prometheus_integration) }
|
||||
end
|
||||
|
||||
context 'but blank' do
|
||||
let(:params) { { type_identifier: nil } }
|
||||
|
||||
it { is_expected.to contain_exactly(integration, prometheus_integration) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe "GraphQL Pipeline details", '(JavaScript fixtures)', type: :request, feature_category: :pipeline_composition do
|
||||
include ApiHelpers
|
||||
include GraphqlHelpers
|
||||
include JavaScriptFixturesHelpers
|
||||
|
||||
let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures') }
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:admin) { project.first_owner }
|
||||
let_it_be(:commit) { create(:commit, project: project) }
|
||||
let_it_be(:pipeline) do
|
||||
create(:ci_pipeline, project: project, sha: commit.id, ref: 'master', user: admin, status: :success)
|
||||
end
|
||||
|
||||
let_it_be(:build_success) do
|
||||
create(:ci_build, :dependent, name: 'build_my_app', pipeline: pipeline, stage: 'build', status: :success)
|
||||
end
|
||||
|
||||
let_it_be(:build_test) { create(:ci_build, :dependent, name: 'test_my_app', pipeline: pipeline, stage: 'test') }
|
||||
let_it_be(:build_deploy_failed) do
|
||||
create(:ci_build, :dependent, name: 'deploy_my_app', status: :failed, pipeline: pipeline, stage: 'deploy')
|
||||
end
|
||||
|
||||
let_it_be(:bridge) { create(:ci_bridge, pipeline: pipeline) }
|
||||
|
||||
let(:pipeline_details_query_path) { 'app/graphql/queries/pipelines/get_pipeline_details.query.graphql' }
|
||||
|
||||
it "pipelines/pipeline_details.json" do
|
||||
query = get_graphql_query_as_string(pipeline_details_query_path, with_base_path: false)
|
||||
|
||||
post_graphql(query, current_user: admin, variables: { projectPath: project.full_path, iid: pipeline.iid })
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
|
|
@ -397,6 +397,44 @@ describe('URL utility', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('visitUrl', () => {
|
||||
let originalLocation;
|
||||
const mockUrl = 'http://example.com/page';
|
||||
|
||||
beforeAll(() => {
|
||||
originalLocation = window.location;
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
value: new URL(TEST_HOST),
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
window.location = originalLocation;
|
||||
});
|
||||
|
||||
it('navigates to a page', () => {
|
||||
urlUtils.visitUrl(mockUrl);
|
||||
|
||||
expect(window.location.href).toBe(mockUrl);
|
||||
});
|
||||
|
||||
it('navigates to a new page', () => {
|
||||
const otherWindow = {};
|
||||
|
||||
Object.defineProperty(window, 'open', {
|
||||
writable: true,
|
||||
value: jest.fn().mockReturnValue(otherWindow),
|
||||
});
|
||||
|
||||
urlUtils.visitUrl(mockUrl, true);
|
||||
|
||||
expect(otherWindow.opener).toBe(null);
|
||||
expect(otherWindow.location).toBe(mockUrl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateHistory', () => {
|
||||
const state = { key: 'prop' };
|
||||
const title = 'TITLE';
|
||||
|
|
|
|||
|
|
@ -1,471 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DAG visualization parsing utilities generateColumnsFromLayersList matches the snapshot 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"groups": Array [
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "4",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "6",
|
||||
"kind": "BUILD",
|
||||
"name": "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "8",
|
||||
"path": "/root/abcd-dag/-/jobs/1482/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1482",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "7",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
"size": 1,
|
||||
"stageName": "build",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "5",
|
||||
"label": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "9",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "11",
|
||||
"kind": "BUILD",
|
||||
"name": "build_b",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "13",
|
||||
"path": "/root/abcd-dag/-/jobs/1515/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1515",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "12",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "build_b",
|
||||
"size": 1,
|
||||
"stageName": "build",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "10",
|
||||
"label": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "14",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "16",
|
||||
"kind": "BUILD",
|
||||
"name": "build_c",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "18",
|
||||
"path": "/root/abcd-dag/-/jobs/1484/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1484",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "17",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "build_c",
|
||||
"size": 1,
|
||||
"stageName": "build",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "15",
|
||||
"label": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "19",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "21",
|
||||
"kind": "BUILD",
|
||||
"name": "build_d 1/3",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "23",
|
||||
"path": "/root/abcd-dag/-/jobs/1485/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1485",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "22",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "24",
|
||||
"kind": "BUILD",
|
||||
"name": "build_d 2/3",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "26",
|
||||
"path": "/root/abcd-dag/-/jobs/1486/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1486",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "25",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "27",
|
||||
"kind": "BUILD",
|
||||
"name": "build_d 3/3",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "29",
|
||||
"path": "/root/abcd-dag/-/jobs/1487/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1487",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "28",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "build_d",
|
||||
"size": 3,
|
||||
"stageName": "build",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "20",
|
||||
"label": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "57",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "59",
|
||||
"kind": "BUILD",
|
||||
"name": "test_c",
|
||||
"needs": Array [],
|
||||
"previousStageJobsOrNeeds": Array [],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": null,
|
||||
"detailsPath": "/root/kinder-pipe/-/pipelines/154",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "60",
|
||||
"label": null,
|
||||
"tooltip": null,
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "test_c",
|
||||
"size": 1,
|
||||
"stageName": "test",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "58",
|
||||
"label": null,
|
||||
},
|
||||
},
|
||||
],
|
||||
"id": "layer-0",
|
||||
"name": "",
|
||||
"status": Object {
|
||||
"action": null,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"groups": Array [
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "32",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "34",
|
||||
"kind": "BUILD",
|
||||
"name": "test_a",
|
||||
"needs": Array [
|
||||
"build_c",
|
||||
"build_b",
|
||||
"build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
],
|
||||
"previousStageJobsOrNeeds": Array [
|
||||
"build_c",
|
||||
"build_b",
|
||||
"build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "36",
|
||||
"path": "/root/abcd-dag/-/jobs/1514/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1514",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "35",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "test_a",
|
||||
"size": 1,
|
||||
"stageName": "test",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "33",
|
||||
"label": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "40",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "42",
|
||||
"kind": "BUILD",
|
||||
"name": "test_b 1/2",
|
||||
"needs": Array [
|
||||
"build_d 3/3",
|
||||
"build_d 2/3",
|
||||
"build_d 1/3",
|
||||
"build_b",
|
||||
"build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
],
|
||||
"previousStageJobsOrNeeds": Array [
|
||||
"build_d 3/3",
|
||||
"build_d 2/3",
|
||||
"build_d 1/3",
|
||||
"build_b",
|
||||
"build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "44",
|
||||
"path": "/root/abcd-dag/-/jobs/1489/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1489",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "43",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "67",
|
||||
"kind": "BUILD",
|
||||
"name": "test_b 2/2",
|
||||
"needs": Array [
|
||||
"build_d 3/3",
|
||||
"build_d 2/3",
|
||||
"build_d 1/3",
|
||||
"build_b",
|
||||
"build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
],
|
||||
"previousStageJobsOrNeeds": Array [
|
||||
"build_d 3/3",
|
||||
"build_d 2/3",
|
||||
"build_d 1/3",
|
||||
"build_b",
|
||||
"build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl",
|
||||
],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": Object {
|
||||
"__typename": "StatusAction",
|
||||
"buttonTitle": "Retry this job",
|
||||
"icon": "retry",
|
||||
"id": "51",
|
||||
"path": "/root/abcd-dag/-/jobs/1490/retry",
|
||||
"title": "Retry",
|
||||
},
|
||||
"detailsPath": "/root/abcd-dag/-/jobs/1490",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "50",
|
||||
"label": "passed",
|
||||
"tooltip": "passed",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "test_b",
|
||||
"size": 2,
|
||||
"stageName": "test",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "41",
|
||||
"label": "passed",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"__typename": "CiGroup",
|
||||
"id": "61",
|
||||
"jobs": Array [
|
||||
Object {
|
||||
"__typename": "CiJob",
|
||||
"id": "53",
|
||||
"kind": "BUILD",
|
||||
"name": "test_d",
|
||||
"needs": Array [
|
||||
"build_b",
|
||||
],
|
||||
"previousStageJobsOrNeeds": Array [
|
||||
"build_b",
|
||||
],
|
||||
"scheduledAt": null,
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"action": null,
|
||||
"detailsPath": "/root/abcd-dag/-/pipelines/153",
|
||||
"group": "success",
|
||||
"hasDetails": true,
|
||||
"icon": "status_success",
|
||||
"id": "64",
|
||||
"label": null,
|
||||
"tooltip": null,
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "test_d",
|
||||
"size": 1,
|
||||
"stageName": "test",
|
||||
"status": Object {
|
||||
"__typename": "DetailedStatus",
|
||||
"group": "success",
|
||||
"icon": "status_success",
|
||||
"id": "62",
|
||||
"label": null,
|
||||
},
|
||||
},
|
||||
],
|
||||
"id": "layer-1",
|
||||
"name": "",
|
||||
"status": Object {
|
||||
"action": null,
|
||||
},
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
|
||||
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
|
||||
|
|
@ -7,11 +8,8 @@ import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines
|
|||
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
|
||||
import { calculatePipelineLayersInfo } from '~/pipelines/components/graph/utils';
|
||||
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
|
||||
import {
|
||||
generateResponse,
|
||||
mockPipelineResponse,
|
||||
pipelineWithUpstreamDownstream,
|
||||
} from './mock_data';
|
||||
|
||||
import { generateResponse, pipelineWithUpstreamDownstream } from './mock_data';
|
||||
|
||||
describe('graph component', () => {
|
||||
let wrapper;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitl
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
|
||||
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
|
@ -26,7 +27,6 @@ import {
|
|||
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
|
||||
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
|
||||
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
|
||||
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
|
||||
import * as Api from '~/pipelines/components/graph_shared/api';
|
||||
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
|
||||
import * as parsingUtils from '~/pipelines/components/parsing_utils';
|
||||
|
|
@ -34,7 +34,7 @@ import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_head
|
|||
import * as sentryUtils from '~/pipelines/utils';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
import { mockRunningPipelineHeaderData } from '../mock_data';
|
||||
import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data';
|
||||
import { mapCallouts, mockCalloutsResponse } from './mock_data';
|
||||
|
||||
const defaultProvide = {
|
||||
graphqlResourceEtag: 'frog/amphibirama/etag/',
|
||||
|
|
@ -55,8 +55,6 @@ describe('Pipeline graph wrapper', () => {
|
|||
const findLinksLayer = () => wrapper.findComponent(LinksLayer);
|
||||
const findGraph = () => wrapper.findComponent(PipelineGraph);
|
||||
const findStageColumnTitle = () => wrapper.findByTestId('stage-column-title');
|
||||
const findAllStageColumnGroupsInColumn = () =>
|
||||
wrapper.findComponent(StageColumnComponent).findAll('[data-testid="stage-column-group"]');
|
||||
const findViewSelector = () => wrapper.findComponent(GraphViewSelector);
|
||||
const findViewSelectorToggle = () => findViewSelector().findComponent(GlToggle);
|
||||
const findViewSelectorTrip = () => findViewSelector().findComponent(GlAlert);
|
||||
|
|
@ -316,12 +314,10 @@ describe('Pipeline graph wrapper', () => {
|
|||
});
|
||||
|
||||
it('switches between views', async () => {
|
||||
const groupsInFirstColumn =
|
||||
mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes.length;
|
||||
expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn);
|
||||
expect(findStageColumnTitle().text()).toBe('build');
|
||||
expect(findStageColumnTitle().text()).toBe('deploy');
|
||||
|
||||
await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
|
||||
expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1);
|
||||
|
||||
expect(findStageColumnTitle().text()).toBe('');
|
||||
});
|
||||
|
||||
|
|
@ -507,9 +503,9 @@ describe('Pipeline graph wrapper', () => {
|
|||
});
|
||||
|
||||
describe('with metrics path', () => {
|
||||
const duration = 875;
|
||||
const numLinks = 7;
|
||||
const totalGroups = 8;
|
||||
const duration = 500;
|
||||
const numLinks = 3;
|
||||
const totalGroups = 7;
|
||||
const metricsData = {
|
||||
histograms: [
|
||||
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
|
||||
|
|
@ -559,9 +555,6 @@ describe('Pipeline graph wrapper', () => {
|
|||
createComponentWithApollo({
|
||||
provide: {
|
||||
metricsPath,
|
||||
glFeatures: {
|
||||
pipelineGraphLayersView: true,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
currentViewType: LAYER_VIEW,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
|
||||
|
|
@ -15,11 +16,8 @@ import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
|
|||
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
|
||||
import * as parsingUtils from '~/pipelines/components/parsing_utils';
|
||||
import { LOAD_FAILURE } from '~/pipelines/constants';
|
||||
import {
|
||||
mockPipelineResponse,
|
||||
pipelineWithUpstreamDownstream,
|
||||
wrappedPipelineReturn,
|
||||
} from './mock_data';
|
||||
|
||||
import { pipelineWithUpstreamDownstream, wrappedPipelineReturn } from './mock_data';
|
||||
|
||||
const processedPipeline = pipelineWithUpstreamDownstream(mockPipelineResponse);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,710 +5,6 @@ import {
|
|||
RETRY_ACTION_TITLE,
|
||||
} from '~/pipelines/components/graph/constants';
|
||||
|
||||
export const mockPipelineResponse = {
|
||||
data: {
|
||||
project: {
|
||||
__typename: 'Project',
|
||||
id: '1',
|
||||
pipeline: {
|
||||
__typename: 'Pipeline',
|
||||
id: 163,
|
||||
iid: '22',
|
||||
complete: true,
|
||||
usesNeeds: true,
|
||||
downstream: null,
|
||||
upstream: null,
|
||||
userPermissions: {
|
||||
__typename: 'PipelinePermissions',
|
||||
updatePipeline: true,
|
||||
},
|
||||
stages: {
|
||||
__typename: 'CiStageConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiStage',
|
||||
id: '2',
|
||||
name: 'build',
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '3',
|
||||
action: null,
|
||||
},
|
||||
groups: {
|
||||
__typename: 'CiGroupConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
id: '4',
|
||||
name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
size: 1,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '5',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '6',
|
||||
kind: BUILD_KIND,
|
||||
name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '7',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1482',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '8',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1482/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
name: 'build_b',
|
||||
id: '9',
|
||||
size: 1,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '10',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '11',
|
||||
name: 'build_b',
|
||||
kind: BUILD_KIND,
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '12',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1515',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '13',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1515/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
id: '14',
|
||||
name: 'build_c',
|
||||
size: 1,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '15',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '16',
|
||||
name: 'build_c',
|
||||
kind: BUILD_KIND,
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '17',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1484',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '18',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1484/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
id: '19',
|
||||
name: 'build_d',
|
||||
size: 3,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '20',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '21',
|
||||
kind: BUILD_KIND,
|
||||
name: 'build_d 1/3',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '22',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1485',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '23',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1485/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '24',
|
||||
kind: BUILD_KIND,
|
||||
name: 'build_d 2/3',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '25',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1486',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '26',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1486/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '27',
|
||||
kind: BUILD_KIND,
|
||||
name: 'build_d 3/3',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '28',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1487',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '29',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1487/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiStage',
|
||||
id: '30',
|
||||
name: 'test',
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '31',
|
||||
action: null,
|
||||
},
|
||||
groups: {
|
||||
__typename: 'CiGroupConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
id: '32',
|
||||
name: 'test_a',
|
||||
size: 1,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '33',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '34',
|
||||
kind: BUILD_KIND,
|
||||
name: 'test_a',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '35',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1514',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '36',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1514/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '37',
|
||||
name: 'build_c',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '38',
|
||||
name: 'build_b',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '39',
|
||||
name:
|
||||
'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
},
|
||||
],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '37',
|
||||
name: 'build_c',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '38',
|
||||
name: 'build_b',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '39',
|
||||
name:
|
||||
'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
id: '40',
|
||||
name: 'test_b',
|
||||
size: 2,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '41',
|
||||
label: 'passed',
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '42',
|
||||
kind: BUILD_KIND,
|
||||
name: 'test_b 1/2',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '43',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1489',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '44',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1489/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '45',
|
||||
name: 'build_d 3/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '46',
|
||||
name: 'build_d 2/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '47',
|
||||
name: 'build_d 1/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '48',
|
||||
name: 'build_b',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '49',
|
||||
name:
|
||||
'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
},
|
||||
],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '45',
|
||||
name: 'build_d 3/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '46',
|
||||
name: 'build_d 2/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '47',
|
||||
name: 'build_d 1/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '48',
|
||||
name: 'build_b',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '49',
|
||||
name:
|
||||
'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '67',
|
||||
kind: BUILD_KIND,
|
||||
name: 'test_b 2/2',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '50',
|
||||
icon: 'status_success',
|
||||
tooltip: 'passed',
|
||||
label: 'passed',
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/jobs/1490',
|
||||
group: 'success',
|
||||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: '51',
|
||||
buttonTitle: 'Retry this job',
|
||||
icon: 'retry',
|
||||
path: '/root/abcd-dag/-/jobs/1490/retry',
|
||||
title: 'Retry',
|
||||
},
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '52',
|
||||
name: 'build_d 3/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '53',
|
||||
name: 'build_d 2/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '54',
|
||||
name: 'build_d 1/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '55',
|
||||
name: 'build_b',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '56',
|
||||
name:
|
||||
'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
},
|
||||
],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '52',
|
||||
name: 'build_d 3/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '53',
|
||||
name: 'build_d 2/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '54',
|
||||
name: 'build_d 1/3',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '55',
|
||||
name: 'build_b',
|
||||
},
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '56',
|
||||
name:
|
||||
'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
name: 'test_c',
|
||||
id: '57',
|
||||
size: 1,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '58',
|
||||
label: null,
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '59',
|
||||
kind: BUILD_KIND,
|
||||
name: 'test_c',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '60',
|
||||
icon: 'status_success',
|
||||
tooltip: null,
|
||||
label: null,
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/kinder-pipe/-/pipelines/154',
|
||||
group: 'success',
|
||||
action: null,
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'CiGroup',
|
||||
id: '61',
|
||||
name: 'test_d',
|
||||
size: 1,
|
||||
status: {
|
||||
id: '62',
|
||||
__typename: 'DetailedStatus',
|
||||
label: null,
|
||||
group: 'success',
|
||||
icon: 'status_success',
|
||||
},
|
||||
jobs: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiJob',
|
||||
id: '53',
|
||||
kind: BUILD_KIND,
|
||||
name: 'test_d',
|
||||
scheduledAt: null,
|
||||
status: {
|
||||
__typename: 'DetailedStatus',
|
||||
id: '64',
|
||||
icon: 'status_success',
|
||||
tooltip: null,
|
||||
label: null,
|
||||
hasDetails: true,
|
||||
detailsPath: '/root/abcd-dag/-/pipelines/153',
|
||||
group: 'success',
|
||||
action: null,
|
||||
},
|
||||
needs: {
|
||||
__typename: 'CiBuildNeedConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '65',
|
||||
name: 'build_b',
|
||||
},
|
||||
],
|
||||
},
|
||||
previousStageJobsOrNeeds: {
|
||||
__typename: 'CiJobConnection',
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'CiBuildNeed',
|
||||
id: '65',
|
||||
name: 'build_b',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const downstream = {
|
||||
nodes: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
|
||||
import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
|
||||
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
|
||||
import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
|
||||
|
||||
import { generateResponse } from '../graph/mock_data';
|
||||
|
||||
describe('links layer component', () => {
|
||||
let wrapper;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
|
||||
import { createSankey } from '~/pipelines/components/dag/drawing_utils';
|
||||
import {
|
||||
makeLinksFromNodes,
|
||||
|
|
@ -14,7 +15,7 @@ import { createNodeDict } from '~/pipelines/utils';
|
|||
import { mockDownstreamPipelinesRest } from '../vue_merge_request_widget/mock_data';
|
||||
import { mockDownstreamPipelinesGraphql } from '../commit/mock_data';
|
||||
import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data';
|
||||
import { generateResponse, mockPipelineResponse } from './graph/mock_data';
|
||||
import { generateResponse } from './graph/mock_data';
|
||||
|
||||
describe('DAG visualization parsing utilities', () => {
|
||||
const nodeDict = createNodeDict(mockParsedGraphQLNodes);
|
||||
|
|
@ -152,14 +153,6 @@ describe('DAG visualization parsing utilities', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
Just as a fallback in case multiple functions change, so tests pass
|
||||
but the implementation moves away from case.
|
||||
*/
|
||||
it('matches the snapshot', () => {
|
||||
expect(columns).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,20 +17,9 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
|
|||
let_it_be(:project) { create(:project, :public, namespace: namespace) }
|
||||
let_it_be(:package) { create(:npm_package, project: project) }
|
||||
|
||||
describe '#endpoint_scope' do
|
||||
subject { object.endpoint_scope }
|
||||
|
||||
context 'when params includes an id' do
|
||||
let(:params) { { id: 42, package_name: 'foo' } }
|
||||
|
||||
it { is_expected.to eq(:project) }
|
||||
end
|
||||
|
||||
context 'when params does not include an id' do
|
||||
let(:params) { { package_name: 'foo' } }
|
||||
|
||||
it { is_expected.to eq(:instance) }
|
||||
end
|
||||
before do
|
||||
allow(object).to receive(:endpoint_scope).and_return(endpoint_scope)
|
||||
allow(object).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
describe '#finder_for_endpoint_scope' do
|
||||
|
|
@ -40,6 +29,7 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
|
|||
|
||||
context 'when called with project scope' do
|
||||
let(:params) { { id: project.id } }
|
||||
let(:endpoint_scope) { :project }
|
||||
|
||||
it 'returns a PackageFinder for project scope' do
|
||||
expect(::Packages::Npm::PackageFinder).to receive(:new).with(package_name, project: project)
|
||||
|
|
@ -50,6 +40,7 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
|
|||
|
||||
context 'when called with instance scope' do
|
||||
let(:params) { { package_name: package_name } }
|
||||
let(:endpoint_scope) { :instance }
|
||||
|
||||
it 'returns a PackageFinder for namespace scope' do
|
||||
expect(::Packages::Npm::PackageFinder).to receive(:new).with(package_name, namespace: group)
|
||||
|
|
@ -57,6 +48,17 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
|
|||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with group scope' do
|
||||
let(:params) { { id: group.id } }
|
||||
let(:endpoint_scope) { :group }
|
||||
|
||||
it 'returns a PackageFinder for group scope' do
|
||||
expect(::Packages::Npm::PackageFinder).to receive(:new).with(package_name, namespace: group)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#project_id_or_nil' do
|
||||
|
|
@ -64,11 +66,21 @@ RSpec.describe ::API::Helpers::Packages::Npm, feature_category: :package_registr
|
|||
|
||||
context 'when called with project scope' do
|
||||
let(:params) { { id: project.id } }
|
||||
let(:endpoint_scope) { :project }
|
||||
|
||||
it { is_expected.to eq(project.id) }
|
||||
end
|
||||
|
||||
context 'when called with namespace scope' do
|
||||
context 'when called with group scope' do
|
||||
let(:params) { { id: group.id, package_name: package.name } }
|
||||
let(:endpoint_scope) { :group }
|
||||
|
||||
it { is_expected.to eq(project.id) }
|
||||
end
|
||||
|
||||
context 'when called with instance scope' do
|
||||
let(:endpoint_scope) { :instance }
|
||||
|
||||
context 'when given an unscoped name' do
|
||||
let(:params) { { package_name: 'foo' } }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe AddTypeToHttpIntegrations, feature_category: :incident_management do
|
||||
let(:integrations) { table(:alert_management_http_integrations) }
|
||||
|
||||
it 'correctly migrates up and down' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(integrations.column_names).not_to include('type_identifier')
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
integrations.reset_column_information
|
||||
expect(integrations.column_names).to include('type_identifier')
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AlertManagement::HttpIntegration do
|
||||
RSpec.describe AlertManagement::HttpIntegration, feature_category: :incident_management do
|
||||
include ::Gitlab::Routing.url_helpers
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
|
@ -21,6 +21,7 @@ RSpec.describe AlertManagement::HttpIntegration do
|
|||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:project) }
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
it { is_expected.to validate_presence_of(:type_identifier) }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(255) }
|
||||
|
||||
context 'when active' do
|
||||
|
|
@ -86,6 +87,66 @@ RSpec.describe AlertManagement::HttpIntegration do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
let_it_be(:integration_1) { create(:alert_management_http_integration) }
|
||||
let_it_be(:integration_2) { create(:alert_management_http_integration, :inactive, project: project) }
|
||||
let_it_be(:integration_3) { create(:alert_management_http_integration, :prometheus, project: project) }
|
||||
let_it_be(:integration_4) { create(:alert_management_http_integration, :legacy, :inactive) }
|
||||
|
||||
describe '.for_endpoint_identifier' do
|
||||
let(:identifier) { integration_1.endpoint_identifier }
|
||||
|
||||
subject { described_class.for_endpoint_identifier(identifier) }
|
||||
|
||||
it { is_expected.to contain_exactly(integration_1) }
|
||||
end
|
||||
|
||||
describe '.for_type' do
|
||||
let(:type) { :prometheus }
|
||||
|
||||
subject { described_class.for_type(type) }
|
||||
|
||||
it { is_expected.to contain_exactly(integration_3) }
|
||||
end
|
||||
|
||||
describe '.for_project' do
|
||||
let(:project) { integration_2.project }
|
||||
|
||||
subject { described_class.for_project(project) }
|
||||
|
||||
it { is_expected.to contain_exactly(integration_2, integration_3) }
|
||||
|
||||
context 'with project_ids array' do
|
||||
let(:project) { [integration_1.project_id] }
|
||||
|
||||
it { is_expected.to contain_exactly(integration_1) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.active' do
|
||||
subject { described_class.active }
|
||||
|
||||
it { is_expected.to contain_exactly(integration_1, integration_3) }
|
||||
end
|
||||
|
||||
describe '.legacy' do
|
||||
subject { described_class.legacy }
|
||||
|
||||
it { is_expected.to contain_exactly(integration_4) }
|
||||
end
|
||||
|
||||
describe '.ordered_by_type_and_id' do
|
||||
before do
|
||||
# Rearrange cache by saving to avoid false-positives
|
||||
integration_2.touch
|
||||
end
|
||||
|
||||
subject { described_class.ordered_by_type_and_id }
|
||||
|
||||
it { is_expected.to eq([integration_1, integration_2, integration_4, integration_3]) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'before validation' do
|
||||
describe '#ensure_payload_example_not_nil' do
|
||||
subject(:integration) { build(:alert_management_http_integration, payload_example: payload_example) }
|
||||
|
|
@ -230,5 +291,33 @@ RSpec.describe AlertManagement::HttpIntegration do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a prometheus integration' do
|
||||
let(:integration) { build(:alert_management_http_integration, :prometheus) }
|
||||
|
||||
it do
|
||||
is_expected.to eq(
|
||||
project_alert_http_integration_url(
|
||||
integration.project,
|
||||
'datadog',
|
||||
integration.endpoint_identifier,
|
||||
format: :json
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
context 'for a legacy integration' do
|
||||
let(:integration) { build(:alert_management_http_integration, :prometheus, :legacy) }
|
||||
|
||||
it do
|
||||
is_expected.to eq(
|
||||
notify_project_prometheus_alerts_url(
|
||||
integration.project,
|
||||
format: :json
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,198 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::NpmGroupPackages, feature_category: :package_registry do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
include_context 'npm api setup'
|
||||
|
||||
describe 'GET /api/v4/groups/:id/-/packages/npm/*package_name' do
|
||||
let(:url) { api("/groups/#{group.id}/-/packages/npm/#{package_name}") }
|
||||
|
||||
it_behaves_like 'handling get metadata requests', scope: :group
|
||||
|
||||
context 'with a duplicate package name in another project' do
|
||||
subject { get(url) }
|
||||
|
||||
before do
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
||||
let_it_be(:project2) { create(:project, :public, namespace: namespace) }
|
||||
let_it_be(:package2) do
|
||||
create(:npm_package,
|
||||
project: project2,
|
||||
name: "@#{group.path}/scoped_package",
|
||||
version: '1.2.0')
|
||||
end
|
||||
|
||||
it 'includes all matching package versions in the response' do
|
||||
subject
|
||||
|
||||
expect(json_response['versions'].keys).to match_array([package.version, package2.version])
|
||||
end
|
||||
|
||||
context 'with the feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(npm_allow_packages_in_multiple_projects: false)
|
||||
end
|
||||
|
||||
it 'returns matching package versions from only one project' do
|
||||
subject
|
||||
|
||||
expect(json_response['versions'].keys).to match_array([package2.version])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mixed group and project visibilities' do
|
||||
subject { get(url, headers: headers) }
|
||||
|
||||
where(:auth, :group_visibility, :project_visibility, :user_role, :expected_status) do
|
||||
nil | :public | :public | nil | :ok
|
||||
nil | :public | :internal | nil | :not_found
|
||||
nil | :public | :private | nil | :not_found
|
||||
nil | :internal | :internal | nil | :not_found
|
||||
nil | :internal | :private | nil | :not_found
|
||||
nil | :private | :private | nil | :not_found
|
||||
|
||||
:oauth | :public | :public | :guest | :ok
|
||||
:oauth | :public | :internal | :guest | :ok
|
||||
:oauth | :public | :private | :guest | :forbidden
|
||||
:oauth | :internal | :internal | :guest | :ok
|
||||
:oauth | :internal | :private | :guest | :forbidden
|
||||
:oauth | :private | :private | :guest | :forbidden
|
||||
:oauth | :public | :public | :reporter | :ok
|
||||
:oauth | :public | :internal | :reporter | :ok
|
||||
:oauth | :public | :private | :reporter | :ok
|
||||
:oauth | :internal | :internal | :reporter | :ok
|
||||
:oauth | :internal | :private | :reporter | :ok
|
||||
:oauth | :private | :private | :reporter | :ok
|
||||
|
||||
:personal_access_token | :public | :public | :guest | :ok
|
||||
:personal_access_token | :public | :internal | :guest | :ok
|
||||
:personal_access_token | :public | :private | :guest | :forbidden
|
||||
:personal_access_token | :internal | :internal | :guest | :ok
|
||||
:personal_access_token | :internal | :private | :guest | :forbidden
|
||||
:personal_access_token | :private | :private | :guest | :forbidden
|
||||
:personal_access_token | :public | :public | :reporter | :ok
|
||||
:personal_access_token | :public | :internal | :reporter | :ok
|
||||
:personal_access_token | :public | :private | :reporter | :ok
|
||||
:personal_access_token | :internal | :internal | :reporter | :ok
|
||||
:personal_access_token | :internal | :private | :reporter | :ok
|
||||
:personal_access_token | :private | :private | :reporter | :ok
|
||||
|
||||
:job_token | :public | :public | :developer | :ok
|
||||
:job_token | :public | :internal | :developer | :ok
|
||||
:job_token | :public | :private | :developer | :ok
|
||||
:job_token | :internal | :internal | :developer | :ok
|
||||
:job_token | :internal | :private | :developer | :ok
|
||||
:job_token | :private | :private | :developer | :ok
|
||||
|
||||
:deploy_token | :public | :public | nil | :ok
|
||||
:deploy_token | :public | :internal | nil | :ok
|
||||
:deploy_token | :public | :private | nil | :ok
|
||||
:deploy_token | :internal | :internal | nil | :ok
|
||||
:deploy_token | :internal | :private | nil | :ok
|
||||
:deploy_token | :private | :private | nil | :ok
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:headers) do
|
||||
case auth
|
||||
when :oauth
|
||||
build_token_auth_header(token.plaintext_token)
|
||||
when :personal_access_token
|
||||
build_token_auth_header(personal_access_token.token)
|
||||
when :job_token
|
||||
build_token_auth_header(job.token)
|
||||
when :deploy_token
|
||||
build_token_auth_header(deploy_token.token)
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
project.update!(visibility: project_visibility.to_s)
|
||||
project.send("add_#{user_role}", user) if user_role
|
||||
group.update!(visibility: group_visibility.to_s)
|
||||
group.send("add_#{user_role}", user) if user_role
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', params[:expected_status]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is a reporter of project but is not a direct member of group' do
|
||||
subject { get(url, headers: headers) }
|
||||
|
||||
where(:group_visibility, :project_visibility, :expected_status) do
|
||||
:public | :public | :ok
|
||||
:public | :internal | :ok
|
||||
:public | :private | :ok
|
||||
:internal | :internal | :ok
|
||||
:internal | :private | :ok
|
||||
:private | :private | :ok
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:headers) { build_token_auth_header(personal_access_token.token) }
|
||||
|
||||
before do
|
||||
project.update!(visibility: project_visibility.to_s)
|
||||
project.add_reporter(user)
|
||||
|
||||
group.update!(visibility: group_visibility.to_s)
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', params[:expected_status]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/packages/npm/-/package/*package_name/dist-tags' do
|
||||
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags") }
|
||||
|
||||
subject { get(url) }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
|
||||
describe 'PUT /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
let(:tag_name) { 'test' }
|
||||
let(:headers) { build_token_auth_header(personal_access_token.token) }
|
||||
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
|
||||
|
||||
subject { put(url, headers: headers) }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
let(:tag_name) { 'test' }
|
||||
let(:headers) { build_token_auth_header(personal_access_token.token) }
|
||||
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
|
||||
|
||||
subject { delete(url, headers: headers) }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
|
||||
describe 'POST /api/v4/groups/:id/-/packages/npm/-/npm/v1/security/advisories/bulk' do
|
||||
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/npm/v1/security/advisories/bulk") }
|
||||
|
||||
subject { post(url) }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
|
||||
describe 'POST /api/v4/groups/:id/-/packages/npm/-/npm/v1/security/audits/quick' do
|
||||
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/npm/v1/security/audits/quick") }
|
||||
|
||||
subject { post(url) }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
end
|
||||
|
|
@ -38,12 +38,6 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService, feature_categor
|
|||
it_behaves_like 'error response', 'You have insufficient permissions to create an HTTP integration for this project'
|
||||
end
|
||||
|
||||
context 'when an integration already exists' do
|
||||
let_it_be(:existing_integration) { create(:alert_management_http_integration, project: project) }
|
||||
|
||||
it_behaves_like 'error response', 'Multiple HTTP integrations are not supported for this project'
|
||||
end
|
||||
|
||||
context 'when an error occurs during update' do
|
||||
it_behaves_like 'error response', "Name can't be blank"
|
||||
end
|
||||
|
|
@ -61,6 +55,38 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService, feature_categor
|
|||
expect(integration.token).to be_present
|
||||
expect(integration.endpoint_identifier).to be_present
|
||||
end
|
||||
|
||||
context 'with an existing HTTP integration' do
|
||||
let_it_be(:http_integration) { create(:alert_management_http_integration, project: project) }
|
||||
|
||||
it_behaves_like 'error response', 'Multiple integrations of a single type are not supported for this project'
|
||||
|
||||
context 'when creating a different type of integration' do
|
||||
let(:params) { { type_identifier: :prometheus, name: 'Prometheus' } }
|
||||
|
||||
it 'is successful' do
|
||||
expect(response).to be_success
|
||||
expect(response.payload[:integration]).to be_a(::AlertManagement::HttpIntegration)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an existing Prometheus integration' do
|
||||
let_it_be(:http_integration) { create(:alert_management_prometheus_integration, project: project) }
|
||||
|
||||
context 'when creating a different type of integration' do
|
||||
it 'is successful' do
|
||||
expect(response).to be_success
|
||||
expect(response.payload[:integration]).to be_a(::AlertManagement::HttpIntegration)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when creating the same time of integration' do
|
||||
let(:params) { { type_identifier: :prometheus, name: 'Prometheus' } }
|
||||
|
||||
it_behaves_like 'error response', 'Multiple integrations of a single type are not supported for this project'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,6 +47,13 @@ RSpec.describe AlertManagement::HttpIntegrations::DestroyService, feature_catego
|
|||
it_behaves_like 'error response', 'Name cannot be removed'
|
||||
end
|
||||
|
||||
context 'when destroying a legacy Prometheus integration' do
|
||||
let_it_be(:existing_integration) { create(:alert_management_prometheus_integration, :legacy, project: project) }
|
||||
let!(:integration) { existing_integration }
|
||||
|
||||
it_behaves_like 'error response', 'Legacy Prometheus integrations cannot currently be removed'
|
||||
end
|
||||
|
||||
it 'successfully returns the integration' do
|
||||
expect(response).to be_success
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :metrics do
|
||||
RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :incident_management do
|
||||
include PrometheusHelpers
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
@ -163,6 +163,24 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :m
|
|||
raise "invalid result: #{result.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with simultaneous manual configuration' do
|
||||
let_it_be(:integration) { create(:alert_management_prometheus_integration, :legacy, project: project) }
|
||||
let_it_be(:old_prometheus_integration) { create(:prometheus_integration, project: project) }
|
||||
let_it_be(:alerting_setting) { create(:project_alerting_setting, project: project, token: integration.token) }
|
||||
|
||||
subject { service.execute(integration.token, integration) }
|
||||
|
||||
it_behaves_like 'processes one firing and one resolved prometheus alerts'
|
||||
|
||||
context 'when HTTP integration is inactive' do
|
||||
before do
|
||||
integration.update!(active: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'alerts service responds with an error and takes no actions', :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'incident settings' do
|
||||
|
|
|
|||
|
|
@ -46,9 +46,8 @@ module JavaScriptFixturesHelpers
|
|||
#
|
||||
# query_path - file path to the GraphQL query, relative to `app/assets/javascripts`.
|
||||
# ee - boolean, when true `query_path` will be looked up in `/ee`.
|
||||
def get_graphql_query_as_string(query_path, ee: false)
|
||||
base = (ee ? 'ee/' : '') + 'app/assets/javascripts'
|
||||
|
||||
def get_graphql_query_as_string(query_path, ee: false, with_base_path: true)
|
||||
base = (ee ? 'ee/' : '') + (with_base_path ? 'app/assets/javascripts' : '')
|
||||
path = Rails.root / base / query_path
|
||||
queries = Gitlab::Graphql::Queries.find(path)
|
||||
if queries.length == 1
|
||||
|
|
|
|||
|
|
@ -259,8 +259,13 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
|
|||
before do
|
||||
project.send("add_#{user_role}", user) if user_role
|
||||
project.update!(visibility: visibility.to_s)
|
||||
|
||||
group.send("add_#{user_role}", user) if user_role && scope == :group
|
||||
group.update!(visibility: visibility.to_s) if scope == :group
|
||||
|
||||
package.update!(name: package_name) unless package_name == 'non-existing-package'
|
||||
if scope == :instance
|
||||
|
||||
if %i[instance group].include?(scope)
|
||||
allow_fetch_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
|
||||
else
|
||||
allow_fetch_cascade_application_setting(attribute: "npm_package_requests_forwarding", return_value: request_forward)
|
||||
|
|
@ -280,6 +285,8 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
|
|||
end
|
||||
end
|
||||
|
||||
status = :not_found if scope == :group && params[:package_name_type] == :non_existing && !params[:request_forward]
|
||||
|
||||
it_behaves_like example_name, status: status
|
||||
end
|
||||
end
|
||||
|
|
@ -300,6 +307,7 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
|
|||
let(:headers) { build_token_auth_header(personal_access_token.token) }
|
||||
|
||||
before do
|
||||
group.add_developer(user) if scope == :group
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue