Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-04 18:09:22 +00:00
parent da602b32e4
commit 7c221ba5ce
119 changed files with 1206 additions and 401 deletions

View file

@ -23,14 +23,10 @@ import {
} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { convertToSnakeCase } from '~/lib/utils/text_utility';
import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
import {
ALERTS_STATUS_TABS,
ALERTS_SEVERITY_LABELS,
trackAlertListViewsOptions,
} from '../constants';
import AlertStatus from './alert_status.vue';
import { ALERTS_STATUS_TABS, SEVERITY_LEVELS, trackAlertListViewsOptions } from '../constants';
const TH_TEST_ID = { 'data-testid': 'alert-management-severity-sort' };
@ -96,7 +92,7 @@ export default {
sortable: true,
},
],
severityLabels: ALERTS_SEVERITY_LABELS,
severityLabels: SEVERITY_LEVELS,
statusTabs: ALERTS_STATUS_TABS,
components: {
GlAlert,

View file

@ -1,12 +1,12 @@
import { s__ } from '~/locale';
export const ALERTS_SEVERITY_LABELS = {
CRITICAL: s__('AlertManagement|Critical'),
HIGH: s__('AlertManagement|High'),
MEDIUM: s__('AlertManagement|Medium'),
LOW: s__('AlertManagement|Low'),
INFO: s__('AlertManagement|Info'),
UNKNOWN: s__('AlertManagement|Unknown'),
export const SEVERITY_LEVELS = {
CRITICAL: s__('severity|Critical'),
HIGH: s__('severity|High'),
MEDIUM: s__('severity|Medium'),
LOW: s__('severity|Low'),
INFO: s__('severity|Info'),
UNKNOWN: s__('severity|Unknown'),
};
export const ALERTS_STATUS_TABS = [
@ -46,20 +46,3 @@ export const trackAlertListViewsOptions = {
category: 'Alert Management',
action: 'view_alerts_list',
};
/**
* Tracks snowplow event when user views alert details
*/
export const trackAlertsDetailsViewsOptions = {
category: 'Alert Management',
action: 'view_alert_details',
};
/**
* Tracks snowplow event when alert status is updated
*/
export const trackAlertStatusUpdateOptions = {
category: 'Alert Management',
action: 'update_alert_status',
label: 'Status',
};

View file

@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
import AlertManagementList from './components/alert_management_list_wrapper.vue';
Vue.use(VueApollo);
@ -59,6 +60,7 @@ export default () => {
populatingAlertsHelpUrl,
emptyAlertSvgPath,
alertManagementEnabled: parseBoolean(alertManagementEnabled),
trackAlertStatusUpdateOptions: PAGE_CONFIG.OPERATIONS.TRACK_ALERT_STATUS_UPDATE_OPTIONS,
userCanEnableAlertManagement: parseBoolean(userCanEnableAlertManagement),
},
apolloProvider,

View file

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/author.fragment.graphql"
#import "./author.fragment.graphql"
fragment AlertNote on Note {
id

View file

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/alert_note.fragment.graphql"
#import "../fragments/alert_note.fragment.graphql"
mutation updateAlertStatus($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) {
updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {

View file

@ -1,3 +1,3 @@
import AlertDetails from '~/alert_management/details';
import AlertDetails from '~/vue_shared/alert_details';
AlertDetails('#js-alert_details');

View file

@ -78,11 +78,7 @@ export default {
return `ci-badge-${escape(group.name)}`;
},
isFadedOut(jobName) {
return (
this.jobHovered &&
this.highlightedJobs.length > 1 &&
!this.highlightedJobs.includes(jobName)
);
return this.highlightedJobs.length > 1 && !this.highlightedJobs.includes(jobName);
},
},
};
@ -126,12 +122,9 @@ export default {
:class="{ 'gl-opacity-3': isFadedOut(group.name) }"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/>
<job-group-dropdown
v-else
:group="group"
:pipeline-id="pipelineId"
:class="{ 'gl-opacity-3': isFadedOut(group.name) }"
/>
<div v-else :class="{ 'gl-opacity-3': isFadedOut(group.name) }">
<job-group-dropdown :group="group" :pipeline-id="pipelineId" />
</div>
</div>
</template>
</main-graph-wrapper>

View file

@ -62,7 +62,7 @@ export default {
helpPath: this.codequalityHelpPath,
});
this.fetchReports(this.glFeatures.codequalityMrDiff);
this.fetchReports(this.glFeatures.codequalityBackendComparison);
},
methods: {
...mapActions(['fetchReports', 'setPaths']),

View file

@ -21,11 +21,11 @@ import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import alertQuery from '../graphql/queries/details.query.graphql';
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
import { ALERTS_SEVERITY_LABELS, trackAlertsDetailsViewsOptions } from '../constants';
import createIssueMutation from '../graphql/mutations/create_issue_from_alert.mutation.graphql';
import toggleSidebarStatusMutation from '../graphql/mutations/toggle_sidebar_status.mutation.graphql';
import alertQuery from '../graphql/queries/alert_details.query.graphql';
import sidebarStatusQuery from '../graphql/queries/alert_sidebar_status.query.graphql';
import { SEVERITY_LEVELS } from '../constants';
import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql';
import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql';
import SystemNote from './system_notes/system_note.vue';
import AlertSidebar from './alert_sidebar.vue';
import AlertMetrics from './alert_metrics.vue';
@ -44,7 +44,7 @@ export default {
directives: {
SafeHtml: GlSafeHtmlDirective,
},
severityLabels: ALERTS_SEVERITY_LABELS,
severityLabels: SEVERITY_LEVELS,
tabsConfig: [
{
id: 'overview',
@ -89,6 +89,9 @@ export default {
projectIssuesPath: {
default: '',
},
trackAlertsDetailsViewsOptions: {
default: null,
},
},
apollo: {
alert: {
@ -155,7 +158,9 @@ export default {
},
},
mounted() {
this.trackPageViews();
if (this.trackAlertsDetailsViewsOptions) {
this.trackPageViews();
}
toggleContainerClasses(containerEl, {
'issuable-bulk-update-sidebar': true,
'right-sidebar-expanded': true,
@ -217,7 +222,7 @@ export default {
return joinPaths(this.projectIssuesPath, issueId);
},
trackPageViews() {
const { category, action } = trackAlertsDetailsViewsOptions;
const { category, action } = this.trackAlertsDetailsViewsOptions;
Tracking.event(category, action);
},
},

View file

@ -1,5 +1,5 @@
<script>
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
import sidebarStatusQuery from '../graphql/queries/alert_sidebar_status.query.graphql';
import SidebarHeader from './sidebar/sidebar_header.vue';
import SidebarTodo from './sidebar/sidebar_todo.vue';
import SidebarStatus from './sidebar/sidebar_status.vue';

View file

@ -2,8 +2,7 @@
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import updateAlertStatusMutation from '~/graphql_shared/mutations/update_alert_status.mutation.graphql';
import { trackAlertStatusUpdateOptions } from '../constants';
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
export default {
i18n: {
@ -21,6 +20,11 @@ export default {
GlDropdown,
GlDropdownItem,
},
inject: {
trackAlertStatusUpdateOptions: {
default: null,
},
},
props: {
projectPath: {
type: String,
@ -58,7 +62,9 @@ export default {
},
})
.then((resp) => {
this.trackStatusUpdate(status);
if (this.trackAlertStatusUpdateOptions) {
this.trackStatusUpdate(status);
}
const errors = resp.data?.updateAlertStatus?.errors || [];
if (errors[0]) {
@ -81,7 +87,7 @@ export default {
});
},
trackStatusUpdate(status) {
const { category, action, label } = trackAlertStatusUpdateOptions;
const { category, action, label } = this.trackAlertStatusUpdateOptions;
Tracking.event(category, action, { label, property: status });
},
},

View file

@ -4,7 +4,7 @@ import { s__ } from '~/locale';
import Todo from '~/sidebar/components/todo_toggle/todo.vue';
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import createAlertTodoMutation from '../../graphql/mutations/alert_todo_create.mutation.graphql';
import alertQuery from '../../graphql/queries/details.query.graphql';
import alertQuery from '../../graphql/queries/alert_details.query.graphql';
export default {
i18n: {

View file

@ -0,0 +1,29 @@
import { s__ } from '~/locale';
export const SEVERITY_LEVELS = {
CRITICAL: s__('severity|Critical'),
HIGH: s__('severity|High'),
MEDIUM: s__('severity|Medium'),
LOW: s__('severity|Low'),
INFO: s__('severity|Info'),
UNKNOWN: s__('severity|Unknown'),
};
export const DEFAULT_PAGE = 'OPERATIONS';
/* eslint-disable @gitlab/require-i18n-strings */
export const PAGE_CONFIG = {
OPERATIONS: {
// Tracks snowplow event when user views alert details
TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: {
category: 'Alert Management',
action: 'view_alert_details',
},
// Tracks snowplow event when alert status is updated
TRACK_ALERT_STATUS_UPDATE_OPTIONS: {
category: 'Alert Management',
action: 'update_alert_status',
label: 'Status',
},
},
};

View file

@ -1,4 +1,4 @@
#import "../fragments/detail_item.fragment.graphql"
#import "../fragments/alert_detail_item.fragment.graphql"
mutation alertTodoCreate($projectPath: ID!, $iid: String!) {
alertTodoCreate(input: { iid: $iid, projectPath: $projectPath }) {

View file

@ -1,4 +1,4 @@
#import "../fragments/detail_item.fragment.graphql"
#import "../fragments/alert_detail_item.fragment.graphql"
query alertDetails($fullPath: ID!, $alertId: String) {
project(fullPath: $fullPath) {

View file

@ -4,14 +4,15 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import AlertDetails from './components/alert_details.vue';
import sidebarStatusQuery from './graphql/queries/sidebar_status.query.graphql';
import sidebarStatusQuery from './graphql/queries/alert_sidebar_status.query.graphql';
import createRouter from './router';
import { DEFAULT_PAGE, PAGE_CONFIG } from './constants';
Vue.use(VueApollo);
export default (selector) => {
const domEl = document.querySelector(selector);
const { alertId, projectPath, projectIssuesPath, projectId } = domEl.dataset;
const { alertId, projectPath, projectIssuesPath, projectId, page = DEFAULT_PAGE } = domEl.dataset;
const router = createRouter();
const resolvers = {
@ -48,18 +49,28 @@ export default (selector) => {
},
});
const provide = {
projectPath,
alertId,
projectIssuesPath,
projectId,
};
if (page === DEFAULT_PAGE) {
const { TRACK_ALERTS_DETAILS_VIEWS_OPTIONS, TRACK_ALERT_STATUS_UPDATE_OPTIONS } = PAGE_CONFIG[
page
];
provide.trackAlertsDetailsViewsOptions = TRACK_ALERTS_DETAILS_VIEWS_OPTIONS;
provide.trackAlertStatusUpdateOptions = TRACK_ALERT_STATUS_UPDATE_OPTIONS;
}
// eslint-disable-next-line no-new
new Vue({
el: selector,
components: {
AlertDetails,
},
provide: {
projectPath,
alertId,
projectIssuesPath,
projectId,
},
provide,
apolloProvider,
router,
render(createElement) {

View file

@ -16,7 +16,12 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
result = DependencyProxy::FindOrCreateManifestService.new(group, image, tag, token).execute
if result[:status] == :success
send_upload(result[:manifest].file)
response.headers['Docker-Content-Digest'] = result[:manifest].digest
response.headers['Content-Length'] = result[:manifest].size
response.headers['Docker-Distribution-Api-Version'] = DependencyProxy::DISTRIBUTION_API_VERSION
response.headers['Etag'] = "\"#{result[:manifest].digest}\""
send_upload(result[:manifest].file, send_params: { type: result[:manifest].content_type })
else
render status: result[:http_status], json: result[:message]
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Projects
module Ci
module PrometheusMetrics
class HistogramsController < Projects::ApplicationController
feature_category :pipeline_authoring
respond_to :json, only: [:create]
def create
result = ::Ci::PrometheusMetrics::ObserveHistogramsService.new(project, permitted_params).execute
render json: result.payload, status: result.http_status
end
private
def permitted_params
params.permit(histograms: [:name, :value])
end
end
end
end
end

View file

@ -41,7 +41,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
push_frontend_feature_flag(:remove_resolve_note, @project, default_enabled: true)
push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true)
push_frontend_feature_flag(:codequality_mr_diff, @project)
push_frontend_feature_flag(:codequality_backend_comparison, @project, default_enabled: :yaml)
push_frontend_feature_flag(:suggestions_custom_commit, @project)
push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml)
push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)

View file

@ -5,7 +5,7 @@ module Types
graphql_name 'MilestoneStateEnum'
description 'Current state of milestone'
value 'active', 'Milestone is currently active'
value 'closed', 'Milestone is closed'
value 'active', description: 'Milestone is currently active'
value 'closed', description: 'Milestone is closed'
end
end

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true
module DependencyProxy
URL_SUFFIX = '/dependency_proxy/containers'
DISTRIBUTION_API_VERSION = 'registry/2.0'
def self.table_name_prefix
'dependency_proxy_'

View file

@ -12,5 +12,10 @@ class DependencyProxy::Manifest < ApplicationRecord
mount_file_store_uploader DependencyProxy::FileUploader
scope :find_or_initialize_by_file_name, ->(file_name) { find_or_initialize_by(file_name: file_name) }
def self.find_or_initialize_by_file_name_or_digest(file_name:, digest:)
result = find_by(file_name: file_name) || find_by(digest: digest)
return result if result
new(file_name: file_name, digest: digest)
end
end

View file

@ -1503,7 +1503,7 @@ class MergeRequest < ApplicationRecord
end
def has_codequality_reports?
return false unless ::Gitlab::Ci::Features.display_quality_on_mr_diff?(project)
return false unless ::Gitlab::Ci::Features.display_codequality_backend_comparison?(project)
actual_head_pipeline&.has_reports?(Ci::JobArtifact.codequality_reports)
end

View file

@ -960,8 +960,8 @@ class User < ApplicationRecord
end
# rubocop: disable CodeReuse/ServiceClass
def refresh_authorized_projects
Users::RefreshAuthorizedProjectsService.new(self).execute
def refresh_authorized_projects(source: nil)
Users::RefreshAuthorizedProjectsService.new(self, source: source).execute
end
# rubocop: enable CodeReuse/ServiceClass

View file

@ -9,7 +9,7 @@ module AuthorizedProjectUpdate
def execute
User.where(id: start_user_id..end_user_id).select(:id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
Users::RefreshAuthorizedProjectsService.new(user).execute
Users::RefreshAuthorizedProjectsService.new(user, source: self.class.name).execute
end
end

View file

@ -38,10 +38,15 @@ module Ci
# mark builds that are retried
if latest_statuses.any?
pipeline.latest_statuses
.where(name: latest_statuses.map(&:second))
.where.not(id: latest_statuses.map(&:first))
.update_all(retried: true)
updated_count = pipeline.latest_statuses
.where(name: latest_statuses.map(&:second))
.where.not(id: latest_statuses.map(&:first))
.update_all(retried: true)
# This counter is temporary. It will be used to check whether if we still use this method or not
# after setting correct value of `GenericCommitStatus#retried`.
# More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50465#note_491657115
metrics.legacy_update_jobs_counter.increment if updated_count > 0
end
end
# rubocop: enable CodeReuse/ActiveRecord

View file

@ -0,0 +1,57 @@
# frozen_string_literal: true
module Ci
module PrometheusMetrics
class ObserveHistogramsService
class << self
def available_histograms
@available_histograms ||= [
histogram(:pipeline_graph_link_calculation_duration_seconds, 'Total time spent calculating links, in seconds', {}, [0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2]),
histogram(:pipeline_graph_links_total, 'Number of links per graph', {}, [1, 5, 10, 25, 50, 100, 200]),
histogram(:pipeline_graph_links_per_job_ratio, 'Ratio of links to job per graph', {}, [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1])
].to_h
end
private
def histogram(name, *attrs)
[name.to_s, proc { Gitlab::Metrics.histogram(name, *attrs) }]
end
end
def initialize(project, params)
@project = project
@params = params
end
def execute
return ServiceResponse.success(http_status: :accepted) unless enabled?
params
.fetch(:histograms, [])
.each(&method(:observe))
ServiceResponse.success(http_status: :created)
end
private
attr_reader :project, :params
def observe(data)
histogram = find_histogram(data[:name])
histogram.observe({ project: project.full_path }, data[:value].to_f)
end
def find_histogram(name)
self.class.available_histograms
.fetch(name) { raise ActiveRecord::RecordNotFound }
.call
end
def enabled?
::Feature.enabled?(:ci_accept_frontend_prometheus_metrics, project, default_enabled: :yaml)
end
end
end
end

View file

@ -13,7 +13,7 @@ module DependencyProxy
def execute
@manifest = @group.dependency_proxy_manifests
.find_or_initialize_by_file_name(@file_name)
.find_or_initialize_by_file_name_or_digest(file_name: @file_name, digest: @tag)
head_result = DependencyProxy::HeadManifestService.new(@image, @tag, @token).execute
@ -30,6 +30,7 @@ module DependencyProxy
def pull_new_manifest
DependencyProxy::PullManifestService.new(@image, @tag, @token).execute_with_manifest do |new_manifest|
@manifest.update!(
content_type: new_manifest[:content_type],
digest: new_manifest[:digest],
file: new_manifest[:file],
size: new_manifest[:file].size
@ -38,7 +39,9 @@ module DependencyProxy
end
def cached_manifest_matches?(head_result)
@manifest && @manifest.digest == head_result[:digest]
return false if head_result[:status] == :error
@manifest && @manifest.digest == head_result[:digest] && @manifest.content_type == head_result[:content_type]
end
def respond

View file

@ -2,6 +2,8 @@
module DependencyProxy
class HeadManifestService < DependencyProxy::BaseService
ACCEPT_HEADERS = ::ContainerRegistry::Client::ACCEPTED_TYPES.join(',')
def initialize(image, tag, token)
@image = image
@tag = tag
@ -9,10 +11,10 @@ module DependencyProxy
end
def execute
response = Gitlab::HTTP.head(manifest_url, headers: auth_headers)
response = Gitlab::HTTP.head(manifest_url, headers: auth_headers.merge(Accept: ACCEPT_HEADERS))
if response.success?
success(digest: response.headers['docker-content-digest'])
success(digest: response.headers['docker-content-digest'], content_type: response.headers['content-type'])
else
error(response.body, response.code)
end

View file

@ -11,7 +11,7 @@ module DependencyProxy
def execute_with_manifest
raise ArgumentError, 'Block must be provided' unless block_given?
response = Gitlab::HTTP.get(manifest_url, headers: auth_headers)
response = Gitlab::HTTP.get(manifest_url, headers: auth_headers.merge(Accept: ::ContainerRegistry::Client::ACCEPTED_TYPES.join(',')))
if response.success?
file = Tempfile.new
@ -20,7 +20,7 @@ module DependencyProxy
file.write(response)
file.flush
yield(success(file: file, digest: response.headers['docker-content-digest']))
yield(success(file: file, digest: response.headers['docker-content-digest'], content_type: response.headers['content-type']))
ensure
file.close
file.unlink

View file

@ -17,8 +17,21 @@ class IssueRebalancingService
start = RelativePositioning::START_POSITION - (gaps / 2) * gap_size
Issue.transaction do
indexed_ids.each_slice(100) { |pairs| assign_positions(start, pairs) }
if Feature.enabled?(:issue_rebalancing_optimization)
Issue.transaction do
assign_positions(start, indexed_ids)
.sort_by(&:first)
.each_slice(100) do |pairs_with_position|
update_positions(pairs_with_position, 'rebalance issue positions in batches ordered by id')
end
end
else
Issue.transaction do
indexed_ids.each_slice(100) do |pairs|
pairs_with_position = assign_positions(start, pairs)
update_positions(pairs_with_position, 'rebalance issue positions')
end
end
end
end
@ -32,13 +45,22 @@ class IssueRebalancingService
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def assign_positions(start, positions)
values = positions.map do |id, index|
"(#{id}, #{start + (index * gap_size)})"
def assign_positions(start, pairs)
pairs.map do |id, index|
[id, start + (index * gap_size)]
end
end
def update_positions(pairs_with_position, query_name)
values = pairs_with_position.map do |id, index|
"(#{id}, #{index})"
end.join(', ')
Issue.connection.exec_query(<<~SQL, "rebalance issue positions")
run_update_query(values, query_name)
end
def run_update_query(values, query_name)
Issue.connection.exec_query(<<~SQL, query_name)
WITH cte(cte_id, new_pos) AS (
SELECT *
FROM (VALUES #{values}) as t (id, pos)
@ -49,7 +71,6 @@ class IssueRebalancingService
WHERE cte_id = id
SQL
end
# rubocop: enable CodeReuse/ActiveRecord
def issue_count
@issue_count ||= base.count

View file

@ -14,13 +14,14 @@ module Users
# service = Users::RefreshAuthorizedProjectsService.new(some_user)
# service.execute
class RefreshAuthorizedProjectsService
attr_reader :user
attr_reader :user, :source
LEASE_TIMEOUT = 1.minute.to_i
# user - The User for which to refresh the authorized projects.
def initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
def initialize(user, source: nil, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
@user = user
@source = source
@incorrect_auth_found_callback = incorrect_auth_found_callback
@missing_auth_found_callback = missing_auth_found_callback
@ -91,6 +92,8 @@ module Users
# remove - The IDs of the authorization rows to remove.
# add - Rows to insert in the form `[user id, project id, access level]`
def update_authorizations(remove = [], add = [])
log_refresh_details(remove.length, add.length)
User.transaction do
user.remove_project_authorizations(remove) unless remove.empty?
ProjectAuthorization.insert_authorizations(add) unless add.empty?
@ -101,6 +104,13 @@ module Users
user.reset
end
def log_refresh_details(rows_deleted, rows_added)
Gitlab::AppJsonLogger.info(event: 'authorized_projects_refresh',
'authorized_projects_refresh.source': source,
'authorized_projects_refresh.rows_deleted': rows_deleted,
'authorized_projects_refresh.rows_added': rows_added)
end
def fresh_access_levels_per_project
fresh_authorizations.each_with_object({}) do |row, hash|
hash[row.project_id] = row.access_level

View file

@ -24,9 +24,9 @@
.control
= form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do
= search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full gl-inset-border-1-gray-200!', spellcheck: false }
= search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false }
.control.d-none.d-md-block
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-svg' do
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-default btn-icon' do
= sprite_icon('rss', css_class: 'qa-rss-icon')
= render_if_exists 'projects/commits/mirror_status'

View file

@ -25,7 +25,7 @@ class AuthorizedProjectsWorker
def perform(user_id)
user = User.find_by(id: user_id)
user&.refresh_authorized_projects
user&.refresh_authorized_projects(source: self.class.name)
end
# rubocop: enable CodeReuse/ActiveRecord
end

View file

@ -0,0 +1,5 @@
---
title: Pull-by-digest and Docker 20.x support for the Dependency Proxy
merge_request: 52805
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Apply new GitLab UI for RSS button in commits page
merge_request: 52900
author: Yogi (@yo)
type: other

View file

@ -0,0 +1,8 @@
---
name: ci_accept_frontend_prometheus_metrics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52820
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300770
milestone: '13.9'
type: development
group: group::pipeline authoring
default_enabled: false

View file

@ -0,0 +1,8 @@
---
name: codequality_backend_comparison
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53068
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300796
milestone: '13.9'
type: development
group: group::testing
default_enabled: false

View file

@ -0,0 +1,8 @@
---
name: issue_rebalancing_optimization
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53384
rollout_issue_url:
milestone: '13.9'
type: development
group: group::project management
default_enabled: false

View file

@ -87,6 +87,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :lint, only: [:show, :create]
resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor'
resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ }
namespace :prometheus_metrics do
resources :histograms, only: [:create], constraints: { format: 'json' }
end
end
namespace :settings do

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
class AddContentTypeToDependencyProxyManifests < ActiveRecord::Migration[6.0]
DOWNTIME = false
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20210128140232_add_text_limit_to_dependency_proxy_manifests_content_type.rb
def change
add_column :dependency_proxy_manifests, :content_type, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddTextLimitToDependencyProxyManifestsContentType < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_text_limit :dependency_proxy_manifests, :content_type, 255
end
def down
remove_text_limit :dependency_proxy_manifests, :content_type
end
end

View file

@ -0,0 +1 @@
0fa84b2038f33e27e549bdb3eb137e1813f604b6e81abc67a49a54d3e1e4bcf5

View file

@ -0,0 +1 @@
93f337364eb5ca5c67f4d4767d1aee9972bfe0596c89f006317dd6103558e35c

View file

@ -11775,7 +11775,9 @@ CREATE TABLE dependency_proxy_manifests (
file_name text NOT NULL,
file text NOT NULL,
digest text NOT NULL,
content_type text,
CONSTRAINT check_079b293a7b CHECK ((char_length(file) <= 255)),
CONSTRAINT check_167a9a8a91 CHECK ((char_length(content_type) <= 255)),
CONSTRAINT check_c579e3f586 CHECK ((char_length(file_name) <= 255)),
CONSTRAINT check_f5d9996bf1 CHECK ((char_length(digest) <= 255))
);

View file

@ -4,7 +4,7 @@ group: Compliance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Audit Events **(STARTER)**
# Audit Events **(PREMIUM)**
GitLab offers a way to view the changes made within the GitLab server for owners and administrators on a [paid plan](https://about.gitlab.com/pricing/).
@ -42,7 +42,7 @@ There are two kinds of events logged:
Impersonation is where an administrator uses credentials to perform an action as a different user.
### Group events **(STARTER)**
### Group events **(PREMIUM)**
A user with a Owner role (or above) can retrieve group audit events of all users.
A user with a Developer or Maintainer role is limited to group audit events based on their individual actions.
@ -72,7 +72,7 @@ From there, you can see the following actions:
Group events can also be accessed via the [Group Audit Events API](../api/audit_events.md#group-audit-events)
### Project events **(STARTER)**
### Project events **(PREMIUM)**
A user with a Maintainer role (or above) can retrieve project audit events of all users.
A user with a Developer role is limited to project audit events based on their individual actions.

View file

@ -119,6 +119,9 @@ The following metrics are available:
| `gitlab_external_http_duration_seconds` | Counter | 13.8 | Duration in seconds spent on each HTTP call to external systems | |
| `gitlab_external_http_exception_total` | Counter | 13.8 | Total number of exceptions raised when making external HTTP calls | |
| `ci_report_parser_duration_seconds` | Histogram | 13.9 | Time to parse CI/CD report artifacts | `parser` |
| `pipeline_graph_link_calculation_duration_seconds` | Histogram | 13.9 | Total time spent calculating links, in seconds | `project` |
| `pipeline_graph_links_total` | Histogram | 13.9 | Number of links per graph | `project` |
| `pipeline_graph_links_per_job_ratio` | Histogram | 13.9 | Ratio of links to job per graph | `project` |
## Metrics controlled by a feature flag

View file

@ -1,7 +1,7 @@
---
type: reference, howto
stage: Create
group: Knowledge
group: Editor
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---

View file

@ -126,7 +126,7 @@ Example response:
}
```
## Group Audit Events **(STARTER)**
## Group Audit Events **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34078) in GitLab 12.5.
@ -233,7 +233,7 @@ Example response:
}
```
## Project Audit Events **(STARTER)**
## Project Audit Events **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219238) in GitLab 13.1.

View file

@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Free](https://about.gitlab.com/pricing/) in GitLab 13.6.
Deletes the cached manifests and blobs for a group. This endpoint requires group admin access.
Deletes the cached manifests and blobs for a group. This endpoint requires group owner access.
WARNING:
[A bug exists](https://gitlab.com/gitlab-org/gitlab/-/issues/277161) for this API.

View file

@ -1,6 +1,6 @@
---
stage: Create
group: Knowledge
group: Editor
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference, api
---

View file

@ -123,7 +123,7 @@ Parameters:
| `tag_name` | string | yes | The name of a tag |
| `ref` | string | yes | Create tag using commit SHA, another tag name, or branch name |
| `message` | string | no | Creates annotated tag |
| `release_description` | string | no | Add release notes to the Git tag and store it in the GitLab database |
| `release_description` | string | no | This parameter is [deprecated](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766) for use in GitLab 11.7, and is planned for [removal](https://gitlab.com/gitlab-org/gitlab/-/issues/290311) in GitLab 14.0. Use the [Releases API](../api/releases/index.md) instead. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/tags?tag_name=test&ref=master"
@ -186,6 +186,11 @@ Parameters:
## Create a new release
WARNING:
This feature is in its end-of-life process. It is [deprecated](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766)
for use in GitLab 11.7, and is planned for [removal](https://gitlab.com/gitlab-org/gitlab/-/issues/290311)
in GitLab 14.0. Use the [Releases API](../api/releases/index.md) instead.
Add release notes to the existing Git tag. If there
already exists a release for the given tag, status code `409` is returned.
@ -221,6 +226,11 @@ Response:
## Update a release
WARNING:
This feature is in its end-of-life process. It is [deprecated](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766)
for use in GitLab 11.7, and is planned for [removal](https://gitlab.com/gitlab-org/gitlab/-/issues/290311)
in GitLab 14.0. Use the [Releases API](../api/releases/index.md) instead.
Updates the release notes of a given release.
```plaintext

View file

@ -1,6 +1,6 @@
---
stage: Create
group: Knowledge
group: Editor
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference, api
---

View file

@ -209,7 +209,7 @@ as well as encourage inner sourcing.
In self-managed GitLab instances, you can build an [Instance Template Repository](../../user/admin_area/settings/instance_template_repository.md).
Development teams across the whole organization can select templates from a dropdown menu.
A group administrator is able to set a group to use as the source for the
A group maintainer or a group owner is able to set a group to use as the source for the
[custom project templates](../../user/admin_area/custom_project_templates.md), which can
be used by all projects in the group. An instance administrator can set a group as
the source for [instance project templates](../../user/group/custom_project_templates.md),

View file

@ -262,6 +262,23 @@ To [prevent duplicate pipelines](yaml/README.md#prevent-duplicate-pipelines), us
[`workflow: rules`](yaml/README.md#workflowrules) or rewrite your rules to control
which pipelines can run.
### Console workaround if job using resource_group gets stuck
```ruby
# find resource group by name
resource_group = Project.find_by_full_path('...').resource_groups.find_by(key: 'the-group-name')
busy_resources = resource_group.resources.where('build_id IS NOT NULL')
# identify which builds are occupying the resource
# (I think it should be 1 as of today)
busy_resources.pluck(:build_id)
# it's good to check why this build is holding the resource.
# Is it stuck? Has it been forcefully dropped by the system?
# free up busy resources
busy_resources.update_all(build_id: nil)
```
## How to get help
If you are unable to resolve pipeline issues, you can get help from:

View file

@ -997,7 +997,8 @@ To link to internal documentation:
- Use relative links to Markdown files in the same repository.
- Do not use absolute URLs or URLs from `docs.gitlab.com`.
- Use `../` to navigate to higher-level directories.
- Don't prepend `./` to links to files or directories.
- Don't prepend `./` to links to files or directories. To link to a file in the
same directory or one of its sub-directories, use the syntax `path/to/file.md`.
- Don't link relative to root. For example, `/ee/user/gitlab_com/index.md`.
Don't:
@ -1022,6 +1023,7 @@ To link to internal documentation:
- `../../merge_requests/index.md`
- `../../issues/tags.md`
- `../../issues/tags.md#stages`
- `issues/tags.md`
NOTE:
Using the Markdown extension is necessary for the [`/help`](../index.md#gitlab-help)

View file

@ -1,7 +1,7 @@
---
type: reference, dev
stage: Create
group: Knowledge
group: Editor
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
description: "GitLab's development guidelines for Wikis"
---

View file

@ -56,7 +56,7 @@ If you're using:
- Self-managed GitLab, self-managed Jira, or both, configure the integration using
[Jira's DVCS Connector](#jira-dvcs-configuration), which syncs data hourly.
We recommend that a GitLab group administrator or instance administrator (in the case of
We recommend that a GitLab group maintainer or group owner, or instance administrator (in the case of
self-managed GitLab) set up the integration to simplify administration.
### Jira DVCS configuration

View file

@ -242,7 +242,7 @@ For each runner, the following attributes are listed:
| Projects | Projects to which the runner is assigned |
| Jobs | Total of jobs run by the runner |
| Tags | Tags associated with the runner |
| Last contact | Timestamp indicating when the GitLab instance last contacted the runner |
| Last contact | Timestamp indicating when the runner last contacted the GitLab instance |
You can also edit, pause, or remove each runner.

View file

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
---
# Code Review Analytics **(STARTER)**
# Code Review Analytics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38062) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.7.

View file

@ -5,7 +5,7 @@ group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Merge Request Analytics **(STARTER)**
# Merge Request Analytics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229045) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.3.

View file

@ -4,7 +4,7 @@ stage: Manage
group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Contribution Analytics **(STARTER)**
# Contribution Analytics **(PREMIUM)**
> - Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3090) for subgroups in GitLab 12.2.

View file

@ -560,7 +560,7 @@ You can change settings that are specific to repositories in your group.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43290) in GitLab 13.6.
By default, when you create a new project in GitLab, the initial branch is called `master`.
For groups, a group administrator can customize the initial branch name to something
For groups, a group owner can customize the initial branch name to something
else. This way, every new project created under that group from then on will start from the custom branch name rather than `master`. To do so:
1. Go to the **Group page > Settings > Repository** and expand **Default initial

View file

@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
WARNING:
This [Closed Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#sts=Closed%20Beta) feature is being re-evaluated in favor of a different
[identity model](https://gitlab.com/groups/gitlab-org/-/epics/4345) that does not require separate accounts.
We recommend that group administrators who haven't yet implemented this feature wait for
We recommend that group owners who haven't yet implemented this feature wait for
the new solution.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/709) in GitLab 12.1.

View file

@ -7,9 +7,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Dependency Proxy
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
> - [Support for private groups](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
> - Anonymous access to images in public groups is no longer available starting in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Free](https://about.gitlab.com/pricing/) in GitLab 13.6.
> - [Support for private groups](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
> - Anonymous access to images in public groups is no longer available starting in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
> - [Support for pull-by-digest and Docker version 20.x](https://gitlab.com/gitlab-org/gitlab/-/issues/290944) in [GitLab Free](https://about.gitlab.com/pricing/) 13.9.
The GitLab Dependency Proxy is a local proxy you can use for your frequently-accessed
upstream images.
@ -17,11 +18,6 @@ upstream images.
In the case of CI/CD, the Dependency Proxy receives a request and returns the
upstream image from a registry, acting as a pull-through cache.
NOTE:
The Dependency Proxy is not compatible with Docker version 20.x and later.
If you are using the Dependency Proxy, Docker version 19.x.x is recommended until
[issue #290944](https://gitlab.com/gitlab-org/gitlab/-/issues/290944) is resolved.
## Prerequisites
The Dependency Proxy must be [enabled by an administrator](../../../administration/packages/dependency_proxy.md).
@ -60,7 +56,7 @@ Prerequisites:
### Authenticate with the Dependency Proxy
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
> - It's [deployed behind a feature flag](../../feature_flags.md), enabled by default.
> - It's enabled on GitLab.com.
> - It's recommended for production use.
@ -162,7 +158,7 @@ the [Dependency Proxy API](../../../api/dependency_proxy.md).
## Docker Hub rate limits and the Dependency Proxy
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241639) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241639) in [GitLab Free](https://about.gitlab.com/pricing/) 13.7.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch how to [use the Dependency Proxy to help avoid Docker Hub rate limits](https://youtu.be/Nc4nUo7Pq08).

View file

@ -68,7 +68,7 @@ To use the GitLab endpoint for NuGet Packages, choose an option:
- **Project-level**: Use when you have few NuGet packages and they are not in
the same GitLab group.
- **Group-level**: Use when you have many NuGet packages in different within the
- **Group-level**: Use when you have many NuGet packages in different projects within the
same GitLab group.
Some features such as [publishing](#publish-a-nuget-package) a package are only available on the project-level endpoint.

View file

@ -1,6 +1,6 @@
---
stage: Create
group: Knowledge
stage: Plan
group: Product Planning
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
---

View file

@ -38,7 +38,9 @@ For a demonstration, see [Pages access controls](https://www.youtube.com/watch?v
- **Only project members**: Only project members are able to browse the website.
- **Everyone with access**: Everyone, both logged into and logged out of GitLab, is able to browse the website, no matter their project membership.
1. Click **Save changes**.
1. Click **Save changes**. Note that your changes may not take effect immediately. GitLab Pages uses
a caching mechanism for efficiency. Your changes may not take effect until that cache is
invalidated, which usually takes less than a minute.
The next time someone tries to access your website and the access control is
enabled, they're presented with a page to sign into GitLab and verify they

View file

@ -266,7 +266,7 @@ This action:
- Deletes a project including all associated resources (issues, merge requests etc).
- From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on [Premium](https://about.gitlab.com/pricing/) or higher tiers,
group administrators can [configure](../../group/index.md#enabling-delayed-project-removal) projects within a group
group owners can [configure](../../group/index.md#enabling-delayed-project-removal) projects within a group
to be deleted after a delayed period.
When enabled, actual deletion happens after number of days
specified in [instance settings](../../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).

View file

@ -1,6 +1,6 @@
---
stage: Create
group: Knowledge
group: Editor
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference, how-to
---

View file

@ -71,6 +71,10 @@ module Gitlab
def self.display_quality_on_mr_diff?(project)
::Feature.enabled?(:codequality_mr_diff, project, default_enabled: false)
end
def self.display_codequality_backend_comparison?(project)
::Feature.enabled?(:codequality_backend_comparison, project, default_enabled: :yaml)
end
end
end
end

View file

@ -45,6 +45,15 @@ module Gitlab
Gitlab::Metrics.counter(name, comment)
end
end
def legacy_update_jobs_counter
strong_memoize(:legacy_update_jobs_counter) do
name = :ci_legacy_update_jobs_as_retried_total
comment = 'Counter of occurrences when jobs were not being set as retried before update_retried'
Gitlab::Metrics.counter(name, comment)
end
end
end
end
end

View file

@ -2483,9 +2483,6 @@ msgstr ""
msgid "AlertManagement|Create incident"
msgstr ""
msgid "AlertManagement|Critical"
msgstr ""
msgid "AlertManagement|Display alerts from all your monitoring tools directly within GitLab. Streamline the investigation of your alerts and the escalation of alerts to incidents."
msgstr ""
@ -2498,24 +2495,12 @@ msgstr ""
msgid "AlertManagement|Events"
msgstr ""
msgid "AlertManagement|High"
msgstr ""
msgid "AlertManagement|Incident"
msgstr ""
msgid "AlertManagement|Info"
msgstr ""
msgid "AlertManagement|Key"
msgstr ""
msgid "AlertManagement|Low"
msgstr ""
msgid "AlertManagement|Medium"
msgstr ""
msgid "AlertManagement|Metrics"
msgstr ""
@ -2597,9 +2582,6 @@ msgstr ""
msgid "AlertManagement|Triggered"
msgstr ""
msgid "AlertManagement|Unknown"
msgstr ""
msgid "AlertManagement|Value"
msgstr ""
@ -24544,6 +24526,9 @@ msgstr ""
msgid "Reports|Actions"
msgstr ""
msgid "Reports|Activity"
msgstr ""
msgid "Reports|An error occured while loading report"
msgstr ""
@ -25805,6 +25790,9 @@ msgstr ""
msgid "SecurityReports|Add projects to your group"
msgstr ""
msgid "SecurityReports|All"
msgstr ""
msgid "SecurityReports|Comment added to '%{vulnerabilityName}'"
msgstr ""
@ -25883,6 +25871,12 @@ msgstr ""
msgid "SecurityReports|More information"
msgstr ""
msgid "SecurityReports|No activity"
msgstr ""
msgid "SecurityReports|No longer detected"
msgstr ""
msgid "SecurityReports|No vulnerabilities found"
msgstr ""
@ -26008,6 +26002,9 @@ msgstr ""
msgid "SecurityReports|While it's rare to have no vulnerabilities, it can happen. In any event, we ask that you double check your settings to make sure you've set up your dashboard correctly."
msgstr ""
msgid "SecurityReports|With issues"
msgstr ""
msgid "SecurityReports|Won't fix / Accept risk"
msgstr ""

View file

@ -45,7 +45,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.182.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "27.1.5",
"@gitlab/ui": "27.4.3",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",

View file

@ -130,7 +130,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
}
end
it 'proxies status from the remote token request' do
it 'proxies status from the remote token request', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:service_unavailable)
@ -147,7 +147,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
}
end
it 'proxies status from the remote manifest request' do
it 'proxies status from the remote manifest request', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:bad_request)
@ -156,15 +156,19 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
it 'sends a file' do
expect(controller).to receive(:send_file).with(manifest.file.path, {})
expect(controller).to receive(:send_file).with(manifest.file.path, type: manifest.content_type)
subject
end
it 'returns Content-Disposition: attachment' do
it 'returns Content-Disposition: attachment', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Docker-Content-Digest']).to eq(manifest.digest)
expect(response.headers['Content-Length']).to eq(manifest.size)
expect(response.headers['Docker-Distribution-Api-Version']).to eq(DependencyProxy::DISTRIBUTION_API_VERSION)
expect(response.headers['Etag']).to eq("\"#{manifest.digest}\"")
expect(response.headers['Content-Disposition']).to match(/^attachment/)
end
end
@ -207,7 +211,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
}
end
it 'proxies status from the remote blob request' do
it 'proxies status from the remote blob request', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:bad_request)
@ -221,7 +225,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
subject
end
it 'returns Content-Disposition: attachment' do
it 'returns Content-Disposition: attachment', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)

View file

@ -10,7 +10,8 @@ FactoryBot.define do
factory :dependency_proxy_manifest, class: 'DependencyProxy::Manifest' do
group
file { fixture_file_upload('spec/fixtures/dependency_proxy/manifest') }
digest { 'sha256:5ab5a6872b264fe4fd35d63991b9b7d8425f4bc79e7cf4d563c10956581170c9' }
digest { 'sha256:d0710affa17fad5f466a70159cc458227bd25d4afb39514ef662ead3e6c99515' }
file_name { 'alpine:latest.json' }
content_type { 'application/vnd.docker.distribution.manifest.v2+json' }
end
end

View file

@ -1,38 +1,16 @@
{
"schemaVersion": 1,
"name": "library/alpine",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1472,
"digest": "sha256:7731472c3f2a25edbb9c085c78f42ec71259f2b83485aa60648276d408865839"
},
"layers": [
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:188c0c94c7c576fff0792aca7ec73d67a2f7f4cb3a6e53a84559337260b36964"
}
],
"history": [
{
"v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"container\":\"8c59eb170e19b8c3768b8d06c91053b0debf4a6fa6a452df394145fe9b885ea5\",\"container_config\":{\"Hostname\":\"8c59eb170e19\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"/bin/sh\\\"]\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2020-10-22T02:19:24.499382102Z\",\"docker_version\":\"18.09.7\",\"id\":\"c5f1aab5bb88eaf1aa62bea08ea6654547d43fd4d15b1a476c77e705dd5385ba\",\"os\":\"linux\",\"parent\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"created\":\"2020-10-22T02:19:24.33416307Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:f17f65714f703db9012f00e5ec98d0b2541ff6147c2633f7ab9ba659d0c507f4 in / \"]}}"
}
],
"signatures": [
{
"header": {
"jwk": {
"crv": "P-256",
"kid": "XOTE:DZ4C:YBPJ:3O3L:YI4B:NYXU:T4VR:USH6:CXXN:SELU:CSCC:FVPE",
"kty": "EC",
"x": "cR1zye_3354mdbD7Dn-mtXNXvtPtmLlUVDa5vH6Lp74",
"y": "rldUXSllLit6_2BW6AV8aqkwWJXHoYPG9OwkIBouwxQ"
},
"alg": "ES256"
},
"signature": "DYB2iB-XKIisqp5Q0OXFOBIOlBOuRV7pnZuKy0cxVB2Qj1VFRhWX4Tq336y0VMWbF6ma1he5A1E_Vk4jazrJ9g",
"protected": "eyJmb3JtYXRMZW5ndGgiOjIxMzcsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAyMC0xMS0yNFQyMjowMTo1MVoifQ"
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2810825,
"digest": "sha256:596ba82af5aaa3e2fd9d6f955b8b94f0744a2b60710e3c243ba3e4a467f051d1"
}
]
}

View file

@ -2,11 +2,11 @@ import { mount } from '@vue/test-utils';
import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar } from '@gitlab/ui';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import mockAlerts from 'jest/vue_shared/alert_details/mocks/alerts.json';
import { visitUrl } from '~/lib/utils/url_utility';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import AlertManagementTable from '~/alert_management/components/alert_management_table.vue';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import mockAlerts from '../mocks/alerts.json';
import defaultProvideValues from '../mocks/alerts_provide_config.json';
jest.mock('~/lib/utils/url_utility', () => ({

View file

@ -1,72 +1,307 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
"<form class=\\"gl-mt-6\\">
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
<div id=\\"integration-type\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-type__BV_label_\\" for=\\"integration-type\\" class=\\"d-block col-form-label\\">1. Select integration type</label>
<div class=\\"bv-no-focus-ring\\"><select class=\\"gl-form-select mw-100 custom-select\\" id=\\"__BVID__8\\">
<option value=\\"\\">Select integration type</option>
<option value=\\"HTTP\\">HTTP Endpoint</option>
<option value=\\"PROMETHEUS\\">External Prometheus</option>
<form
class="gl-mt-6"
>
<h5
class="gl-font-lg gl-my-5"
>
Add new integrations
</h5>
<div
class="form-group gl-form-group"
id="integration-type"
role="group"
>
<label
class="d-block col-form-label"
for="integration-type"
id="integration-type__BV_label_"
>
1. Select integration type
</label>
<div
class="bv-no-focus-ring"
>
<select
class="gl-form-select mw-100 custom-select"
id="__BVID__8"
>
<option
value=""
>
Select integration type
</option>
<option
value="HTTP"
>
HTTP Endpoint
</option>
<option
value="PROMETHEUS"
>
External Prometheus
</option>
</select>
<!---->
<!---->
<!---->
<!---->
</div>
</div>
<transition-stub css=\\"true\\" enterclass=\\"\\" leaveclass=\\"collapse show\\" entertoclass=\\"collapse show\\" leavetoclass=\\"collapse\\" enteractiveclass=\\"collapsing\\" leaveactiveclass=\\"collapsing\\" class=\\"gl-mt-3\\">
<div class=\\"collapse\\" style=\\"display: none;\\" id=\\"__BVID__10\\">
<transition-stub
class="gl-mt-3"
css="true"
enteractiveclass="collapsing"
enterclass=""
entertoclass="collapse show"
leaveactiveclass="collapsing"
leaveclass="collapse show"
leavetoclass="collapse"
>
<div
class="collapse"
id="__BVID__10"
style="display: none;"
>
<div>
<div id=\\"name-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"name-integration__BV_label_\\" for=\\"name-integration\\" class=\\"d-block col-form-label\\">2. Name integration</label>
<div class=\\"bv-no-focus-ring\\"><input type=\\"text\\" placeholder=\\"Enter integration name\\" class=\\"gl-form-input form-control\\" id=\\"__BVID__15\\">
<div
class="form-group gl-form-group"
id="name-integration"
role="group"
>
<label
class="d-block col-form-label"
for="name-integration"
id="name-integration__BV_label_"
>
2. Name integration
</label>
<div
class="bv-no-focus-ring"
>
<input
class="gl-form-input form-control"
id="__BVID__15"
placeholder="Enter integration name"
type="text"
/>
<!---->
<!---->
<!---->
</div>
</div>
<div id=\\"integration-webhook\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-webhook__BV_label_\\" for=\\"integration-webhook\\" class=\\"d-block col-form-label\\">3. Set up webhook</label>
<div class=\\"bv-no-focus-ring\\"><span>Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html\\" class=\\"gl-link gl-display-inline-block\\">GitLab documentation</a> to learn more about configuring your endpoint.</span> <label class=\\"gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal\\">
<div class=\\"gl-toggle-wrapper\\"><span class=\\"gl-toggle-label\\">Active</span>
<!----> <button aria-label=\\"Active\\" type=\\"button\\" class=\\"gl-toggle\\"><span class=\\"toggle-icon\\"><svg data-testid=\\"close-icon\\" aria-hidden=\\"true\\" class=\\"gl-icon s16\\"><use href=\\"#close\\"></use></svg></span></button></div>
<div
class="form-group gl-form-group"
id="integration-webhook"
role="group"
>
<label
class="d-block col-form-label"
for="integration-webhook"
id="integration-webhook__BV_label_"
>
3. Set up webhook
</label>
<div
class="bv-no-focus-ring"
>
<span>
Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the
<a
class="gl-link gl-display-inline-block"
href="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
rel="noopener noreferrer"
target="_blank"
>
GitLab documentation
</a>
to learn more about configuring your endpoint.
</span>
<label
class="gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal"
>
<span
class="gl-toggle-wrapper"
>
<span
class="gl-toggle-label"
data-testid="toggle-label"
>
Active
</span>
<!---->
<button
aria-label="Active"
class="gl-toggle"
role="switch"
type="button"
>
<span
class="toggle-icon"
>
<svg
aria-hidden="true"
class="gl-icon s16"
data-testid="close-icon"
>
<use
href="#close"
/>
</svg>
</span>
</button>
</span>
<!---->
</label>
<!---->
<div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
<div
class="gl-my-4"
>
<span
class="gl-font-weight-bold"
>
Webhook URL
</span>
<div id=\\"url\\" readonly=\\"readonly\\">
<div role=\\"group\\" class=\\"input-group\\">
</span>
<div
id="url"
readonly="readonly"
>
<div
class="input-group"
role="group"
>
<!---->
<!----> <input id=\\"url\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" aria-hidden=\\"true\\" class=\\"gl-button-icon gl-icon s16\\">
<use href=\\"#copy-to-clipboard\\"></use>
<!---->
<input
class="gl-form-input form-control"
id="url"
readonly="readonly"
type="text"
/>
<div
class="input-group-append"
>
<button
aria-label="Copy this value"
class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
data-clipboard-text=""
title="Copy"
type="button"
>
<!---->
<svg
aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="copy-to-clipboard-icon"
>
<use
href="#copy-to-clipboard"
/>
</svg>
<!----></button></div>
<!---->
</button>
</div>
<!---->
</div>
</div>
</div>
<div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
<div
class="gl-my-4"
>
<span
class="gl-font-weight-bold"
>
Authorization key
</span>
<div id=\\"authorization-key\\" readonly=\\"readonly\\" class=\\"gl-mb-3\\">
<div role=\\"group\\" class=\\"input-group\\">
</span>
<div
class="gl-mb-3"
id="authorization-key"
readonly="readonly"
>
<div
class="input-group"
role="group"
>
<!---->
<!----> <input id=\\"authorization-key\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" aria-hidden=\\"true\\" class=\\"gl-button-icon gl-icon s16\\">
<use href=\\"#copy-to-clipboard\\"></use>
<!---->
<input
class="gl-form-input form-control"
id="authorization-key"
readonly="readonly"
type="text"
/>
<div
class="input-group-append"
>
<button
aria-label="Copy this value"
class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
data-clipboard-text=""
title="Copy"
type="button"
>
<!---->
<svg
aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="copy-to-clipboard-icon"
>
<use
href="#copy-to-clipboard"
/>
</svg>
<!----></button></div>
<!---->
</button>
</div>
<!---->
</div>
</div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn btn-default btn-md disabled gl-button\\">
</div>
<button
class="btn btn-default btn-md disabled gl-button"
disabled="disabled"
type="button"
>
<!---->
<!----> <span class=\\"gl-button-text\\">
<!---->
<span
class="gl-button-text"
>
Reset Key
</span></button>
</span>
</button>
<!---->
</div>
<!---->
@ -74,25 +309,98 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
<!---->
</div>
</div>
<div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Sample alert payload (optional)</label>
<div class=\\"bv-no-focus-ring\\"><span>Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"{ &quot;events&quot;: [{ &quot;application&quot;: &quot;Name of application&quot; }] }\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-3 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
<div
class="form-group gl-form-group"
id="test-integration"
role="group"
>
<label
class="d-block col-form-label"
for="test-integration"
id="test-integration__BV_label_"
>
4. Sample alert payload (optional)
</label>
<div
class="bv-no-focus-ring"
>
<span>
Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).
</span>
<textarea
class="gl-form-input gl-form-textarea gl-my-3 form-control is-valid"
disabled="disabled"
id="test-payload"
placeholder="{ \\"events\\": [{ \\"application\\": \\"Name of application\\" }] }"
style="resize: none; overflow-y: scroll;"
wrap="soft"
/>
<!---->
<!---->
<!---->
</div>
</div>
<!---->
<!---->
</div>
<div class=\\"gl-display-flex gl-justify-content-start gl-py-3\\"><button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\">
<div
class="gl-display-flex gl-justify-content-start gl-py-3"
>
<button
class="btn js-no-auto-disable btn-success btn-md gl-button"
data-testid="integration-form-submit"
type="submit"
>
<!---->
<!----> <span class=\\"gl-button-text\\">Save integration
</span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" disabled=\\"disabled\\" class=\\"btn gl-mx-3 js-no-auto-disable btn-success btn-md disabled gl-button btn-success-secondary\\">
<!---->
<!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button type=\\"reset\\" class=\\"btn js-no-auto-disable btn-default btn-md gl-button\\">
<span
class="gl-button-text"
>
Save integration
</span>
</button>
<button
class="btn gl-mx-3 js-no-auto-disable btn-success btn-md disabled gl-button btn-success-secondary"
data-testid="integration-test-and-submit"
disabled="disabled"
type="button"
>
<!---->
<!----> <span class=\\"gl-button-text\\">Cancel</span></button></div>
<!---->
<span
class="gl-button-text"
>
Save and test payload
</span>
</button>
<button
class="btn js-no-auto-disable btn-default btn-md gl-button"
type="reset"
>
<!---->
<!---->
<span
class="gl-button-text"
>
Cancel
</span>
</button>
</div>
</div>
</transition-stub>
</form>"
</form>
`;

View file

@ -81,7 +81,7 @@ describe('AlertsSettingsFormNew', () => {
});
it('renders the initial template', () => {
expect(wrapper.html()).toMatchSnapshot();
expect(wrapper.element).toMatchSnapshot();
});
it('render the initial form with only an integration type dropdown', () => {

View file

@ -60,8 +60,8 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
>
<svg
aria-hidden="true"
class="gl-icon s16 gl-new-dropdown-item-check-icon"
data-testid="mobile-issue-close-icon"
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-mt-3 gl-align-self-start"
data-testid="dropdown-item-checkbox"
>
<use
href="#mobile-issue-close"
@ -115,8 +115,8 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
>
<svg
aria-hidden="true"
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-visibility-hidden"
data-testid="mobile-issue-close-icon"
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-visibility-hidden gl-mt-3 gl-align-self-start"
data-testid="dropdown-item-checkbox"
>
<use
href="#mobile-issue-close"

View file

@ -22,6 +22,13 @@ describe('graph component', () => {
pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'),
};
const defaultData = {
measurements: {
width: 800,
height: 800,
},
};
const createComponent = ({
data = {},
mountFn = shallowMount,
@ -34,7 +41,10 @@ describe('graph component', () => {
...props,
},
data() {
return { ...data };
return {
...defaultData,
...data,
};
},
provide: {
dataMethod: GRAPHQL,

View file

@ -65,7 +65,7 @@ describe('Codequality Reports actions', () => {
let mock;
let diffFeatureFlagEnabled;
describe('with codequalityMrDiff feature flag enabled', () => {
describe('with codequalityBackendComparison feature flag enabled', () => {
beforeEach(() => {
diffFeatureFlagEnabled = true;
localState.reportsPath = `${TEST_HOST}/codequality_reports.json`;
@ -112,7 +112,7 @@ describe('Codequality Reports actions', () => {
});
});
describe('with codequalityMrDiff feature flag disabled', () => {
describe('with codequalityBackendComparison feature flag disabled', () => {
beforeEach(() => {
diffFeatureFlagEnabled = false;
localState.headPath = `${TEST_HOST}/head.json`;

View file

@ -4,17 +4,14 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import AlertDetails from '~/alert_management/components/alert_details.vue';
import AlertSummaryRow from '~/alert_management/components/alert_summary_row.vue';
import {
ALERTS_SEVERITY_LABELS,
trackAlertsDetailsViewsOptions,
} from '~/alert_management/constants';
import createIssueMutation from '~/alert_management/graphql/mutations/create_issue_from_alert.mutation.graphql';
import AlertDetails from '~/vue_shared/alert_details/components/alert_details.vue';
import AlertSummaryRow from '~/vue_shared/alert_details/components/alert_summary_row.vue';
import { SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants';
import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql';
import { joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import mockAlerts from '../mocks/alerts.json';
import mockAlerts from './mocks/alerts.json';
const mockAlert = mockAlerts[0];
const environmentName = 'Production';
@ -29,7 +26,13 @@ describe('AlertDetails', () => {
const projectId = '1';
const $router = { replace: jest.fn() };
function mountComponent({ data, loading = false, mountMethod = shallowMount, stubs = {} } = {}) {
function mountComponent({
data,
loading = false,
mountMethod = shallowMount,
provide = {},
stubs = {},
} = {}) {
wrapper = extendedWrapper(
mountMethod(AlertDetails, {
provide: {
@ -37,6 +40,7 @@ describe('AlertDetails', () => {
projectPath,
projectIssuesPath,
projectId,
...provide,
},
data() {
return {
@ -112,9 +116,7 @@ describe('AlertDetails', () => {
});
it('renders severity', () => {
expect(wrapper.findByTestId('severity').text()).toBe(
ALERTS_SEVERITY_LABELS[mockAlert.severity],
);
expect(wrapper.findByTestId('severity').text()).toBe(SEVERITY_LEVELS[mockAlert.severity]);
});
it('renders a title', () => {
@ -321,16 +323,27 @@ describe('AlertDetails', () => {
});
describe('Snowplow tracking', () => {
const mountOptions = {
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alert: mockAlert },
loading: false,
};
beforeEach(() => {
jest.spyOn(Tracking, 'event');
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alert: mockAlert },
loading: false,
});
});
it('should track alert details page views', () => {
it('should not track alert details page views when the tracking options do not exist', () => {
mountComponent(mountOptions);
expect(Tracking.event).not.toHaveBeenCalled();
});
it('should track alert details page views when the tracking options exist', () => {
const trackAlertsDetailsViewsOptions = {
category: 'Alert Management',
action: 'view_alert_details',
};
mountComponent({ ...mountOptions, provide: { trackAlertsDetailsViewsOptions } });
const { category, action } = trackAlertsDetailsViewsOptions;
expect(Tracking.event).toHaveBeenCalledWith(category, action);
});

View file

@ -1,8 +1,8 @@
import { mount } from '@vue/test-utils';
import SidebarTodo from '~/alert_management/components/sidebar/sidebar_todo.vue';
import createAlertTodoMutation from '~/alert_management/graphql/mutations/alert_todo_create.mutation.graphql';
import SidebarTodo from '~/vue_shared/alert_details/components/sidebar/sidebar_todo.vue';
import createAlertTodoMutation from '~/vue_shared/alert_details/graphql/mutations/alert_todo_create.mutation.graphql';
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import mockAlerts from '../mocks/alerts.json';
import mockAlerts from './mocks/alerts.json';
const mockAlert = mockAlerts[0];

View file

@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import waitForPromises from 'helpers/wait_for_promises';
import AlertMetrics from '~/alert_management/components/alert_metrics.vue';
import AlertMetrics from '~/vue_shared/alert_details/components/alert_metrics.vue';
import MetricEmbed from '~/monitoring/components/embeds/metric_embed.vue';
jest.mock('~/monitoring/stores', () => ({

View file

@ -1,11 +1,10 @@
import { shallowMount } from '@vue/test-utils';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
import { trackAlertStatusUpdateOptions } from '~/alert_management/constants';
import AlertManagementStatus from '~/alert_management/components/alert_status.vue';
import updateAlertStatusMutation from '~/graphql_shared/mutations/update_alert_status.mutation.graphql';
import AlertManagementStatus from '~/vue_shared/alert_details/components/alert_status.vue';
import updateAlertStatusMutation from '~/graphql_shared//mutations/alert_status_update.mutation.graphql';
import Tracking from '~/tracking';
import mockAlerts from '../mocks/alerts.json';
import mockAlerts from './mocks/alerts.json';
const mockAlert = mockAlerts[0];
@ -20,7 +19,7 @@ describe('AlertManagementStatus', () => {
return waitForPromises();
};
function mountComponent({ props = {}, loading = false, stubs = {} } = {}) {
function mountComponent({ props = {}, provide = {}, loading = false, stubs = {} } = {}) {
wrapper = shallowMount(AlertManagementStatus, {
propsData: {
alert: { ...mockAlert },
@ -28,6 +27,7 @@ describe('AlertManagementStatus', () => {
isSidebar: false,
...props,
},
provide,
mocks: {
$apollo: {
mutate: jest.fn(),
@ -134,10 +134,25 @@ describe('AlertManagementStatus', () => {
describe('Snowplow tracking', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
mountComponent({});
});
it('should track alert status updates', () => {
it('should not track alert status updates when the tracking options do not exist', () => {
mountComponent({});
Tracking.event.mockClear();
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
findFirstStatusOption().vm.$emit('click');
setImmediate(() => {
expect(Tracking.event).not.toHaveBeenCalled();
});
});
it('should track alert status updates when the tracking options exist', () => {
const trackAlertStatusUpdateOptions = {
category: 'Alert Management',
action: 'update_alert_status',
label: 'Status',
};
mountComponent({ provide: { trackAlertStatusUpdateOptions } });
Tracking.event.mockClear();
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({});
findFirstStatusOption().vm.$emit('click');

Some files were not shown because too many files have changed in this diff Show more