Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-06-01 15:08:16 +00:00
parent 65f197cdb6
commit 711f859532
59 changed files with 371 additions and 114 deletions

View file

@ -11,6 +11,7 @@ import {
GlTabs,
GlTab,
GlBadge,
GlPagination,
} from '@gitlab/ui';
import createFlash from '~/flash';
import { s__ } from '~/locale';
@ -22,6 +23,7 @@ import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query
import {
ALERTS_STATUS_TABS,
ALERTS_SEVERITY_LABELS,
DEFAULT_PAGE_SIZE,
trackAlertListViewsOptions,
trackAlertStatusUpdateOptions,
} from '../constants';
@ -34,6 +36,14 @@ const bodyTrClass =
'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-bg-blue-50 gl-hover-cursor-pointer gl-hover-border-b-solid gl-hover-border-blue-200';
const findDefaultSortColumn = () => document.querySelector('.js-started-at');
const initialPaginationState = {
currentPage: 1,
prevPageCursor: '',
nextPageCursor: '',
firstPageSize: DEFAULT_PAGE_SIZE,
lastPageSize: null,
};
export default {
i18n: {
noAlertsMsg: s__(
@ -110,6 +120,7 @@ export default {
GlTabs,
GlTab,
GlBadge,
GlPagination,
},
props: {
projectPath: {
@ -142,10 +153,20 @@ export default {
projectPath: this.projectPath,
statuses: this.statusFilter,
sort: this.sort,
firstPageSize: this.pagination.firstPageSize,
lastPageSize: this.pagination.lastPageSize,
prevPageCursor: this.pagination.prevPageCursor,
nextPageCursor: this.pagination.nextPageCursor,
};
},
update(data) {
return data.project?.alertManagementAlerts?.nodes;
const { alertManagementAlerts: { nodes: list = [], pageInfo = {} } = {} } =
data.project || {};
return {
list,
pageInfo,
};
},
error() {
this.errored = true;
@ -169,7 +190,9 @@ export default {
isAlertDismissed: false,
isErrorAlertDismissed: false,
sort: 'STARTED_AT_ASC',
statusFilter: this.$options.statusTabs[4].filters,
statusFilter: [],
filteredByStatus: '',
pagination: initialPaginationState,
};
},
computed: {
@ -185,19 +208,34 @@ export default {
return this.$apollo.queries.alerts.loading;
},
hasAlerts() {
return this.alerts?.length;
return this.alerts?.list?.length;
},
tbodyTrClass() {
return !this.loading && this.hasAlerts ? bodyTrClass : '';
},
showPaginationControls() {
return Boolean(this.prevPage || this.nextPage);
},
alertsForCurrentTab() {
return this.alertsCount ? this.alertsCount[this.filteredByStatus.toLowerCase()] : 0;
},
prevPage() {
return Math.max(this.pagination.currentPage - 1, 0);
},
nextPage() {
const nextPage = this.pagination.currentPage + 1;
return nextPage > Math.ceil(this.alertsForCurrentTab / DEFAULT_PAGE_SIZE) ? null : nextPage;
},
},
mounted() {
findDefaultSortColumn().ariaSort = 'ascending';
this.trackPageViews();
},
methods: {
filterAlertsByStatus(tabIndex) {
this.statusFilter = this.$options.statusTabs[tabIndex].filters;
this.resetPagination();
const { filters, status } = this.$options.statusTabs[tabIndex];
this.statusFilter = filters;
this.filteredByStatus = status;
},
fetchSortedData({ sortBy, sortDesc }) {
const sortDirection = sortDesc ? 'DESC' : 'ASC';
@ -206,6 +244,7 @@ export default {
if (sortBy !== 'startedAt') {
findDefaultSortColumn().ariaSort = 'none';
}
this.resetPagination();
this.sort = `${sortColumn}_${sortDirection}`;
},
updateAlertStatus(status, iid) {
@ -222,6 +261,7 @@ export default {
this.trackStatusUpdate(status);
this.$apollo.queries.alerts.refetch();
this.$apollo.queries.alertsCount.refetch();
this.resetPagination();
})
.catch(() => {
createFlash(
@ -246,6 +286,28 @@ export default {
// TODO: Update to show list of assignee(s) after https://gitlab.com/gitlab-org/gitlab/-/issues/218405
return assignees?.length > 0 ? assignees[0]?.username : s__('AlertManagement|Unassigned');
},
handlePageChange(page) {
const { startCursor, endCursor } = this.alerts.pageInfo;
if (page > this.pagination.currentPage) {
this.pagination = {
...initialPaginationState,
nextPageCursor: endCursor,
currentPage: page,
};
} else {
this.pagination = {
lastPageSize: DEFAULT_PAGE_SIZE,
firstPageSize: null,
prevPageCursor: startCursor,
nextPageCursor: '',
currentPage: page,
};
}
},
resetPagination() {
this.pagination = initialPaginationState;
},
},
};
</script>
@ -275,7 +337,7 @@ export default {
</h4>
<gl-table
class="alert-management-table mt-3"
:items="alerts"
:items="alerts ? alerts.list : []"
:fields="$options.fields"
:show-empty="true"
:busy="loading"
@ -283,6 +345,7 @@ export default {
:tbody-tr-class="tbodyTrClass"
:no-local-sorting="true"
sort-icon-left
sort-by="startedAt"
@row-clicked="navigateToAlertDetails"
@sort-changed="fetchSortedData"
>
@ -350,6 +413,16 @@ export default {
<gl-loading-icon size="lg" color="dark" class="mt-3" />
</template>
</gl-table>
<gl-pagination
v-if="showPaginationControls"
:value="pagination.currentPage"
:prev-page="prevPage"
:next-page="nextPage"
align="center"
class="gl-pagination prepend-top-default"
@input="handlePageChange"
/>
</div>
<gl-empty-state
v-else

View file

@ -63,3 +63,5 @@ export const trackAlertStatusUpdateOptions = {
action: 'update_alert_status',
label: 'Status',
};
export const DEFAULT_PAGE_SIZE = 10;

View file

@ -1,11 +1,32 @@
#import "../fragments/list_item.fragment.graphql"
query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!], $sort: AlertManagementAlertSort ) {
project(fullPath: $projectPath) {
alertManagementAlerts(statuses: $statuses, sort: $sort) {
nodes {
...AlertListItem
}
query getAlerts(
$projectPath: ID!,
$statuses: [AlertManagementStatus!],
$sort: AlertManagementAlertSort,
$firstPageSize: Int,
$lastPageSize: Int,
$prevPageCursor: String = ""
$nextPageCursor: String = ""
) {
project(fullPath: $projectPath, ) {
alertManagementAlerts(
statuses: $statuses,
sort: $sort,
first: $firstPageSize
last: $lastPageSize,
after: $nextPageCursor,
before: $prevPageCursor
) {
nodes {
...AlertListItem
},
pageInfo {
hasNextPage
endCursor
hasPreviousPage
startCursor
}
}
}
}
}

View file

@ -84,10 +84,10 @@ export default {
<div v-show="hasError" class="btn-group">
<div class="btn btn-default btn-sm disabled">
<icon :size="16" class="gl-ml-3 append-right-8" name="doc-image" aria-hidden="true" />
<icon :size="16" class="gl-ml-3 gl-mr-3" name="doc-image" aria-hidden="true" />
</div>
<div class="btn btn-default btn-sm disabled">
<span class="gl-ml-3 append-right-8">{{ s__('Badges|No badge image') }}</span>
<span class="gl-ml-3 gl-mr-3">{{ s__('Badges|No badge image') }}</span>
</div>
</div>

View file

@ -54,7 +54,7 @@ export default {
<div v-if="canEditBadge" class="table-action-buttons">
<button
:disabled="badge.isDeleting"
class="btn btn-default append-right-8"
class="btn btn-default gl-mr-3"
type="button"
@click="editBadge(badge)"
>

View file

@ -86,7 +86,7 @@ export default {
<publish-button
:show-count="true"
:should-publish="false"
class="btn btn-success btn-inverted append-right-8"
class="btn btn-success btn-inverted gl-mr-3"
/>
<loading-button
ref="publishNowButton"

View file

@ -103,7 +103,7 @@ export default {
:show-count="false"
:should-publish="true"
:label="__('Submit review')"
class="float-right append-right-8"
class="float-right gl-mr-3"
/>
</div>
</div>

View file

@ -83,7 +83,7 @@ export default {
@click="scrollToDraft(draft)"
>
<span class="review-preview-item-header">
<icon class="append-right-8 flex-shrink-0" :name="iconName" />
<icon class="gl-mr-3 flex-shrink-0" :name="iconName" />
<span class="bold text-nowrap">
<span class="review-preview-item-header-text block-truncated"> {{ titleText }} </span>
<template v-if="showLinePosition">
@ -98,7 +98,7 @@ export default {
v-if="draft.discussion_id && resolvedStatusMessage"
class="review-preview-item-footer draft-note-resolution p-0"
>
<icon class="append-right-8" name="status_success" /> {{ resolvedStatusMessage }}
<icon class="gl-mr-3" name="status_success" /> {{ resolvedStatusMessage }}
</span>
</button>
</template>

View file

@ -188,7 +188,7 @@ export default {
>
<span
v-if="issue.referencePath"
class="board-card-number overflow-hidden d-flex append-right-8 gl-mt-3"
class="board-card-number overflow-hidden d-flex gl-mr-3 gl-mt-3"
>
<tooltip-on-truncate
v-if="issueReferencePath"

View file

@ -170,7 +170,7 @@ export default {
v-if="tableIsNotEmpty"
ref="secret-value-reveal-button"
data-qa-selector="reveal_ci_variable_value_button"
class="append-right-8"
class="gl-mr-3"
@click="toggleValues(!valuesHidden)"
>{{ valuesButtonText }}</gl-deprecated-button
>

View file

@ -313,7 +313,7 @@ export default {
:data-qa-selector="id"
>
<div class="gl-responsive-table-row-layout" role="row">
<div class="table-section append-right-8 section-align-top" role="gridcell">
<div class="table-section gl-mr-3 section-align-top" role="gridcell">
<img
v-if="hasLogo"
:src="logoUrl"

View file

@ -168,7 +168,7 @@ export default {
}}
</gl-alert>
<div class="gl-responsive-table-row-layout" role="row">
<div class="table-section append-right-8 section-align-top" role="gridcell">
<div class="table-section gl-mr-3 section-align-top" role="gridcell">
<img
:src="modSecurityLogo"
:alt="`${$options.title} logo`"

View file

@ -86,7 +86,7 @@ export default {
<button
v-gl-tooltip.hover
type="button"
class="btn btn-default append-right-8 js-toggle-tree-list"
class="btn btn-default gl-mr-3 js-toggle-tree-list"
:class="{
active: showTreeList,
}"
@ -126,15 +126,11 @@ export default {
<gl-deprecated-button
v-if="commit || startVersion"
:href="latestVersionPath"
class="append-right-8 js-latest-version"
class="gl-mr-3 js-latest-version"
>
{{ __('Show latest version') }}
</gl-deprecated-button>
<gl-deprecated-button
v-show="hasCollapsedFile"
class="append-right-8"
@click="expandAllFiles"
>
<gl-deprecated-button v-show="hasCollapsedFile" class="gl-mr-3" @click="expandAllFiles">
{{ __('Expand all') }}
</gl-deprecated-button>
<settings-dropdown />

View file

@ -104,7 +104,7 @@ export default {
<gl-loading-icon
v-if="group.isChildrenLoading"
size="lg"
class="d-none d-sm-inline-flex flex-shrink-0 append-right-8"
class="d-none d-sm-inline-flex flex-shrink-0 gl-mr-3"
/>
<div
:class="{ 'd-sm-flex': !group.isChildrenLoading }"
@ -117,12 +117,12 @@ export default {
</div>
<div class="group-text-container d-flex flex-fill align-items-center">
<div class="group-text flex-grow-1 flex-shrink-1">
<div class="d-flex align-items-center flex-wrap title namespace-title append-right-8">
<div class="d-flex align-items-center flex-wrap title namespace-title gl-mr-3">
<a
v-tooltip
:href="group.relativePath"
:title="group.fullName"
class="no-expand gl-mt-3 append-right-8"
class="no-expand gl-mt-3 gl-mr-3"
data-placement="bottom"
>{{
// ending bracket must be by closing tag to prevent

View file

@ -56,7 +56,7 @@ export default {
v-if="canDiscard"
ref="discardButton"
type="button"
class="btn btn-remove btn-inverted append-right-8"
class="btn btn-remove btn-inverted gl-mr-3"
@click="showDiscardModal"
>
{{ __('Discard changes') }}

View file

@ -74,7 +74,7 @@ export default {
<div class="ide-commit-list-container">
<header class="multi-file-commit-panel-header d-flex mb-0">
<div class="d-flex align-items-center flex-fill">
<icon v-once :name="iconName" :size="18" class="append-right-8" />
<icon v-once :name="iconName" :size="18" class="gl-mr-3" />
<strong> {{ titleText }} </strong>
<div class="d-flex ml-auto">
<button

View file

@ -87,7 +87,7 @@ export default {
@click="openFileInEditor"
>
<span class="multi-file-commit-list-file-path d-flex align-items-center">
<file-icon :file-name="file.name" class="append-right-8" />
<file-icon :file-name="file.name" class="gl-mr-3" />
<template v-if="file.prevName && file.prevName !== file.name">
{{ file.prevName }} &#x2192;
</template>

View file

@ -75,7 +75,7 @@ export default {
>
{{ stage.name }}
</strong>
<div v-if="!stage.isLoading || stage.jobs.length" class="append-right-8 gl-ml-2">
<div v-if="!stage.isLoading || stage.jobs.length" class="gl-mr-3 gl-ml-2">
<span class="badge badge-pill"> {{ jobsCount }} </span>
</div>
<icon :name="collapseIcon" class="ide-stage-collapse-icon" />

View file

@ -18,6 +18,6 @@ export default {
:title="__('Part of merge request changes')"
:size="12"
name="git-merge"
class="append-right-8"
class="gl-mr-3"
/>
</template>

View file

@ -285,7 +285,7 @@ export default {
<slot name="topLeft"></slot>
<h5
ref="graphTitle"
class="prometheus-graph-title gl-font-lg font-weight-bold text-truncate append-right-8"
class="prometheus-graph-title gl-font-lg font-weight-bold text-truncate gl-mr-3"
tabindex="0"
>
{{ title }}

View file

@ -71,7 +71,7 @@ export default {
:download="evidenceTitle(index)"
:href="evidenceUrl(index)"
>
<gl-icon name="review-list" class="align-middle append-right-8" />
<gl-icon name="review-list" class="align-middle gl-mr-3" />
<span>{{ evidenceTitle(index) }}</span>
</gl-link>
@ -96,7 +96,7 @@ export default {
<gl-icon
v-gl-tooltip
name="clock"
class="align-middle append-right-8"
class="align-middle gl-mr-3"
:title="collectedAt(index)"
/>
<span>{{ timeSummary(index) }}</span>

View file

@ -57,7 +57,7 @@ export default {
<template>
<div class="card-subtitle d-flex flex-wrap text-secondary">
<div class="append-right-8">
<div class="gl-mr-3">
<icon name="commit" class="align-middle" />
<gl-link v-if="commitUrl" v-gl-tooltip.bottom :title="commit.title" :href="commitUrl">
{{ commit.shortId }}
@ -65,7 +65,7 @@ export default {
<span v-else v-gl-tooltip.bottom :title="commit.title">{{ commit.shortId }}</span>
</div>
<div class="append-right-8">
<div class="gl-mr-3">
<icon name="tag" class="align-middle" />
<gl-link v-if="tagUrl" v-gl-tooltip.bottom :title="__('Tag')" :href="tagUrl">
{{ release.tagName }}

View file

@ -117,7 +117,7 @@ export default {
:href="webIdePath"
:title="ideButtonTitle"
:class="{ disabled: !mr.canPushToSourceBranch }"
class="btn btn-default js-web-ide d-none d-md-inline-block append-right-8"
class="btn btn-default js-web-ide d-none d-md-inline-block gl-mr-3"
data-placement="bottom"
tabindex="0"
role="button"
@ -129,7 +129,7 @@ export default {
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
class="btn btn-default js-check-out-branch append-right-8"
class="btn btn-default js-check-out-branch gl-mr-3"
type="button"
>
{{ s__('mrWidget|Check out branch') }}

View file

@ -52,7 +52,7 @@ export default {
:download="fileName"
target="_blank"
>
<icon :size="16" name="download" class="float-left append-right-8" />
<icon :size="16" name="download" class="float-left gl-mr-3" />
{{ __('Download') }}
</gl-link>
</div>

View file

@ -75,12 +75,8 @@ export default {
@mouseover="mouseOverRow"
@mousemove="mouseMove"
>
<file-icon
:file-name="file.name"
:size="16"
css-classes="diff-file-changed-icon append-right-8"
/>
<span class="diff-changed-file-content append-right-8">
<file-icon :file-name="file.name" :size="16" css-classes="diff-file-changed-icon gl-mr-3" />
<span class="diff-changed-file-content gl-mr-3">
<strong class="diff-changed-file-name">
<span
v-for="(char, charIndex) in file.name.split('')"

View file

@ -414,7 +414,6 @@ img.emoji {
.append-right-2 { margin-right: 2px; }
.append-right-4 { margin-right: 4px; }
.append-right-5 { margin-right: 5px; }
.append-right-8 { margin-right: 8px; }
.append-right-10 { margin-right: 10px; }
.append-right-15 { margin-right: 15px; }
.append-right-default { margin-right: $gl-padding; }

View file

@ -920,7 +920,7 @@ button.mini-pipeline-graph-dropdown-toggle {
.ci-status-icon {
@extend .append-right-8;
@include gl-mr-3;
position: relative;

View file

@ -14,6 +14,13 @@ module Releases
scope :sorted, -> { order(created_at: :desc) }
enum link_type: {
other: 0,
runbook: 1,
package: 2,
image: 3
}
def internal?
url.start_with?(release.project.web_url)
end

View file

@ -9,7 +9,7 @@
= form_for @hook, as: :hook, url: admin_hook_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
%span>= f.submit _('Save changes'), class: 'btn btn-success append-right-8'
%span>= f.submit _('Save changes'), class: 'btn btn-success gl-mr-3'
= render 'shared/web_hooks/test_button', hook: @hook
= link_to _('Delete'), admin_hook_path(@hook), method: :delete, class: 'btn btn-remove float-right', data: { confirm: _('Are you sure?') }

View file

@ -2,7 +2,7 @@
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
%button.close.js-close{ type: "button" } &times;
.gcp-signup-offer--content
.gcp-signup-offer--icon.append-right-8
.gcp-signup-offer--icon.gl-mr-3
= sprite_icon("information", size: 16)
.gcp-signup-offer--copy
%h4= s_('ClusterIntegration|Did you know?')

View file

@ -13,14 +13,14 @@
.form-group.row
= label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :bitbucket_server_url, '', class: 'form-control append-right-8', placeholder: _('https://your-bitbucket-server'), size: 40
= text_field_tag :bitbucket_server_url, '', class: 'form-control gl-mr-3', placeholder: _('https://your-bitbucket-server'), size: 40
.form-group.row
= label_tag :bitbucket_server_url, 'Username', class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :bitbucket_username, '', class: 'form-control append-right-8', placeholder: _('username'), size: 40
= text_field_tag :bitbucket_username, '', class: 'form-control gl-mr-3', placeholder: _('username'), size: 40
.form-group.row
= label_tag :personal_access_token, 'Password/Personal Access Token', class: 'col-form-label col-md-2'
.col-md-4
= password_field_tag :personal_access_token, '', class: 'form-control append-right-8', placeholder: _('Personal Access Token'), size: 40
= password_field_tag :personal_access_token, '', class: 'form-control gl-mr-3', placeholder: _('Personal Access Token'), size: 40
.form-actions
= submit_tag _('List your Bitbucket Server repositories'), class: 'btn btn-success'

View file

@ -16,10 +16,10 @@
.form-group.row
= label_tag :phabricator_server_url, _('Phabricator Server URL'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :phabricator_server_url, params[:phabricator_server_url], class: 'form-control append-right-8', placeholder: 'https://your-phabricator-server', size: 40
= text_field_tag :phabricator_server_url, params[:phabricator_server_url], class: 'form-control gl-mr-3', placeholder: 'https://your-phabricator-server', size: 40
.form-group.row
= label_tag :api_token, _('API Token'), class: 'col-form-label col-md-2'
.col-md-4
= password_field_tag :api_token, params[:api_token], class: 'form-control append-right-8', placeholder: _('Personal Access Token'), size: 40
= password_field_tag :api_token, params[:api_token], class: 'form-control gl-mr-3', placeholder: _('Personal Access Token'), size: 40
.form-actions
= submit_tag _('Import tasks'), class: 'btn btn-success'

View file

@ -80,7 +80,7 @@
- if @project.badges.present?
.project-badges.mb-2
- @project.badges.each do |badge|
%a.append-right-8{ href: badge.rendered_link_url(@project),
%a.gl-mr-3{ href: badge.rendered_link_url(@project),
target: '_blank',
rel: 'noopener noreferrer' }>
%img.project-badge{ src: badge.rendered_image_url(@project),

View file

@ -1,6 +1,6 @@
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project)
.count-badge.d-inline-flex.align-item-stretch.append-right-8
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do
= sprite_icon('fork', { css_class: 'icon' })

View file

@ -1,5 +1,5 @@
- if current_user
.count-badge.d-inline-flex.align-item-stretch.append-right-8
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3
%button.count-badge-button.btn.btn-default.btn-xs.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
- if current_user.starred?(@project)
= sprite_icon('star', { css_class: 'icon' })
@ -12,7 +12,7 @@
= @project.star_count
- else
.count-badge.d-inline-flex.align-item-stretch.append-right-8
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3
= link_to new_user_session_path, class: 'btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star')

View file

@ -22,8 +22,8 @@
- diff_files.each do |diff_file|
%li
%a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
= sprite_icon(diff_file_changed_icon(diff_file), size: 16, css_class: "#{diff_file_changed_icon_color(diff_file)} diff-file-changed-icon append-right-8")
%span.diff-changed-file-content.append-right-8
= sprite_icon(diff_file_changed_icon(diff_file), size: 16, css_class: "#{diff_file_changed_icon_color(diff_file)} diff-file-changed-icon gl-mr-3")
%span.diff-changed-file-content.gl-mr-3
- if diff_file.file_path
%strong.diff-changed-file-name
= diff_file.file_path

View file

@ -10,7 +10,7 @@
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
%span>= f.submit 'Save changes', class: 'btn btn-success append-right-8'
%span>= f.submit 'Save changes', class: 'btn btn-success gl-mr-3'
= render 'shared/web_hooks/test_button', hook: @hook
= link_to _('Delete'), project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove float-right', data: { confirm: _('Are you sure?') }

View file

@ -41,7 +41,7 @@
= _('Create branch')
%li.divider.droplab-item-ignore
%li.droplab-item-ignore.gl-ml-3.append-right-8.prepend-top-16
%li.droplab-item-ignore.gl-ml-3.gl-mr-3.prepend-top-16
- if can_create_confidential_merge_request?
#js-forked-project{ data: { namespace_path: @project.namespace.full_path, project_path: @project.full_path, new_fork_path: new_project_fork_path(@project), help_page_path: help_page_path('user/project/merge_requests') } }
.form-group

View file

@ -8,7 +8,7 @@
- else
- button_title = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }
.js-notification-dropdown.notification-dropdown.home-panel-action-button.prepend-top-default.append-right-8.dropdown.inline
.js-notification-dropdown.notification-dropdown.home-panel-action-button.prepend-top-default.gl-mr-3.dropdown.inline
= form_for notification_setting, remote: true, html: { class: "inline notification-form no-label" } do |f|
= hidden_setting_source_input(notification_setting)
= hidden_field_tag "hide_label", true

View file

@ -32,7 +32,7 @@
.d-flex.align-items-center.flex-wrap.project-title
%h2.d-flex.gl-mt-3
= link_to project_path(project), class: 'text-plain' do
%span.project-full-name.append-right-8><
%span.project-full-name.gl-mr-3><
%span.namespace-name
- if project.namespace && !skip_namespace
= project.namespace.human_name

View file

@ -11,6 +11,6 @@
= hook.enable_ssl_verification ? _('enabled') : _('disabled')
.col-md-4.col-lg-5.text-right-md.prepend-top-5
%span>= render 'shared/web_hooks/test_button', hook: hook, button_class: 'btn-sm append-right-8'
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'btn btn-sm append-right-8'
%span>= render 'shared/web_hooks/test_button', hook: hook, button_class: 'btn-sm gl-mr-3'
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'btn btn-sm gl-mr-3'
= link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn btn-sm'

View file

@ -0,0 +1,5 @@
---
title: Add link_type column to release_links table
merge_request: 33156
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Alerts list pagination
merge_request: 33073
author:
type: added

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddLinkTypeToReleaseLinks < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :release_links, :link_type, :integer, limit: 2, default: 0
end
end
def down
with_lock_retries do
remove_column :release_links, :link_type
end
end
end

View file

@ -5730,7 +5730,8 @@ CREATE TABLE public.release_links (
name character varying NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
filepath character varying(128)
filepath character varying(128),
link_type smallint DEFAULT 0
);
CREATE SEQUENCE public.release_links_id_seq
@ -14031,6 +14032,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200526153844
20200526164946
20200526164947
20200527092027
20200527094322
20200527095401
20200527151413

View file

@ -126,10 +126,12 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `sidekiq_jobs_db_seconds` | Histogram | 12.9 | Seconds of DB time to run Sidekiq job | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_jobs_gitaly_seconds` | Histogram | 12.9 | Seconds of Gitaly time to run Sidekiq job | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_redis_requests_duration_seconds` | Histogram | 13.1 | Duration in seconds that a Sidekiq job spent querying a Redis server | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_elasticsearch_requests_duration_seconds` | Histogram | 13.1 | Duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_jobs_queue_duration_seconds` | Histogram | 12.5 | Duration in seconds that a Sidekiq job was queued before being executed | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_jobs_failed_total` | Counter | 12.2 | Sidekiq jobs failed | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_jobs_retried_total` | Counter | 12.2 | Sidekiq jobs retried | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_redis_requests_total` | Counter | 13.1 | Redis requests during a Sidekiq job execution | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_elasticsearch_requests_total` | Counter | 13.1 | Elasticsearch requests during a Sidekiq job execution | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_running_jobs` | Gauge | 12.2 | Number of Sidekiq jobs running | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_concurrency` | Gauge | 12.5 | Maximum number of Sidekiq jobs | |
| `geo_db_replication_lag_seconds` | Gauge | 10.2 | Database replication lag (seconds) | `url` |

View file

@ -4,29 +4,31 @@ group: Project Management
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/#designated-technical-writers
---
# Bulk editing issues and merge requests
# Bulk editing issues, epics, and merge requests
> **Notes:**
>
> - A permission level of `Reporter` or higher is required in order to manage
> issues.
> - A permission level of `Reporter` or higher is required in order to manage
> epics.
> - A permission level of `Developer` or higher is required in order to manage
> merge requests.
Attributes can be updated simultaneously across multiple issues or merge requests
Attributes can be updated simultaneously across multiple issues, epics, or merge requests
by using the bulk editing feature.
![Bulk editing](img/bulk-editing.png)
NOTE: **Note:**
Bulk editing issues and merge requests is also available at the group level.
Bulk editing issues, epics, and merge requests is also available at the group level.
For more details, see [bulk editing group issues and merge requests](../group/bulk_editing/index.md).
To update multiple project issues or merge requests at the same time:
To update multiple project issues, epics, or merge requests at the same time:
1. Navigate to their respective list.
1. Click **Edit issues** or **Edit merge requests**.
1. Click either **Edit issues**, **Edit epics**, or **Edit merge requests**.
- This will open a sidebar on the right-hand side of your screen
where editable fields will be displayed.

View file

@ -6,6 +6,7 @@ module Gitlab
class BaseFormatter
attr_reader :old_path
attr_reader :new_path
attr_reader :file_identifier_hash
attr_reader :base_sha
attr_reader :start_sha
attr_reader :head_sha
@ -16,6 +17,7 @@ module Gitlab
attrs[:diff_refs] = diff_file.diff_refs
attrs[:old_path] = diff_file.old_path
attrs[:new_path] = diff_file.new_path
attrs[:file_identifier_hash] = diff_file.file_identifier_hash
end
if diff_refs = attrs[:diff_refs]
@ -26,6 +28,7 @@ module Gitlab
@old_path = attrs[:old_path]
@new_path = attrs[:new_path]
@file_identifier_hash = attrs[:file_identifier_hash]
@base_sha = attrs[:base_sha]
@start_sha = attrs[:start_sha]
@head_sha = attrs[:head_sha]
@ -36,7 +39,7 @@ module Gitlab
end
def to_h
{
out = {
base_sha: base_sha,
start_sha: start_sha,
head_sha: head_sha,
@ -44,6 +47,12 @@ module Gitlab
new_path: new_path,
position_type: position_type
}
if Feature.enabled?(:file_identifier_hash)
out[:file_identifier_hash] = file_identifier_hash
end
out
end
def position_type

View file

@ -9,6 +9,7 @@ module Gitlab
delegate :old_path,
:new_path,
:file_identifier_hash,
:base_sha,
:start_sha,
:head_sha,

View file

@ -49,6 +49,8 @@ module Gitlab
@metrics[:sidekiq_jobs_gitaly_seconds].observe(labels, get_gitaly_time(job))
@metrics[:sidekiq_redis_requests_total].increment(labels, get_redis_calls(job))
@metrics[:sidekiq_redis_requests_duration_seconds].observe(labels, get_redis_time(job))
@metrics[:sidekiq_elasticsearch_requests_total].increment(labels, get_elasticsearch_calls(job))
@metrics[:sidekiq_elasticsearch_requests_duration_seconds].observe(labels, get_elasticsearch_time(job))
end
end
@ -62,9 +64,11 @@ module Gitlab
sidekiq_jobs_gitaly_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_gitaly_seconds, 'Seconds of Gitaly time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_queue_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_redis_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_redis_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent requests a Redis server', {}, Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS),
sidekiq_elasticsearch_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_elasticsearch_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_failed_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed'),
sidekiq_jobs_retried_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_retried_total, 'Sidekiq jobs retried'),
sidekiq_redis_requests_total: ::Gitlab::Metrics.counter(:sidekiq_redis_requests_total, 'Redis requests during a Sidekiq job execution'),
sidekiq_elasticsearch_requests_total: ::Gitlab::Metrics.counter(:sidekiq_elasticsearch_requests_total, 'Elasticsearch requests during a Sidekiq job execution'),
sidekiq_running_jobs: ::Gitlab::Metrics.gauge(:sidekiq_running_jobs, 'Number of Sidekiq jobs running', {}, :all),
sidekiq_concurrency: ::Gitlab::Metrics.gauge(:sidekiq_concurrency, 'Maximum number of Sidekiq jobs', {}, :all)
}
@ -82,6 +86,14 @@ module Gitlab
job.fetch(:redis_calls, 0)
end
def get_elasticsearch_time(job)
job.fetch(:elasticsearch_duration_s, 0)
end
def get_elasticsearch_calls(job)
job.fetch(:elasticsearch_calls, 0)
end
def get_gitaly_time(job)
job.fetch(:gitaly_duration_s, 0)
end

View file

@ -7,8 +7,10 @@ import {
GlDropdown,
GlDropdownItem,
GlIcon,
GlTabs,
GlTab,
GlBadge,
GlPagination,
} from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
@ -39,19 +41,21 @@ describe('AlertManagementList', () => {
const findLoader = () => wrapper.find(GlLoadingIcon);
const findStatusDropdown = () => wrapper.find(GlDropdown);
const findStatusFilterTabs = () => wrapper.findAll(GlTab);
const findStatusTabs = () => wrapper.find(GlTabs);
const findStatusFilterBadge = () => wrapper.findAll(GlBadge);
const findDateFields = () => wrapper.findAll(TimeAgo);
const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
const findAssignees = () => wrapper.findAll('[data-testid="assigneesField"]');
const findSeverityFields = () => wrapper.findAll('[data-testid="severityField"]');
const findSeverityColumnHeader = () => wrapper.findAll('th').at(0);
const findStartTimeColumnHeader = () => wrapper.findAll('th').at(1);
const findPagination = () => wrapper.find(GlPagination);
const alertsCount = {
acknowledged: 6,
all: 16,
open: 14,
resolved: 2,
triggered: 10,
acknowledged: 6,
resolved: 1,
all: 16,
};
function mountComponent({
@ -76,6 +80,7 @@ describe('AlertManagementList', () => {
mocks: {
$apollo: {
mutate: jest.fn(),
query: jest.fn(),
queries: {
alerts: {
loading,
@ -134,7 +139,7 @@ describe('AlertManagementList', () => {
it('loading state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: null, alertsCount: null },
data: { alerts: {}, alertsCount: null },
loading: true,
});
expect(findAlertsTable().exists()).toBe(true);
@ -149,7 +154,7 @@ describe('AlertManagementList', () => {
it('error state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: null, alertsCount: null, errored: true },
data: { alerts: { errors: ['error'] }, alertsCount: null, errored: true },
loading: false,
});
expect(findAlertsTable().exists()).toBe(true);
@ -166,7 +171,7 @@ describe('AlertManagementList', () => {
it('empty state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: [], alertsCount: { all: 0 }, errored: false },
data: { alerts: { list: [], pageInfo: {} }, alertsCount: { all: 0 }, errored: false },
loading: false,
});
expect(findAlertsTable().exists()).toBe(true);
@ -183,7 +188,7 @@ describe('AlertManagementList', () => {
it('has data state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
expect(findLoader().exists()).toBe(false);
@ -199,7 +204,7 @@ describe('AlertManagementList', () => {
it('displays status dropdown', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
expect(findStatusDropdown().exists()).toBe(true);
@ -208,7 +213,7 @@ describe('AlertManagementList', () => {
it('shows correct severity icons', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@ -225,7 +230,7 @@ describe('AlertManagementList', () => {
it('renders severity text', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@ -239,7 +244,7 @@ describe('AlertManagementList', () => {
it('renders Unassigned when no assignee(s) present', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@ -253,7 +258,7 @@ describe('AlertManagementList', () => {
it('renders username(s) when assignee(s) present', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@ -267,7 +272,7 @@ describe('AlertManagementList', () => {
it('navigates to the detail page when alert row is clicked', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
@ -282,15 +287,17 @@ describe('AlertManagementList', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: {
alerts: [
{
iid: 1,
status: 'acknowledged',
startedAt: '2020-03-17T23:18:14.996Z',
endedAt: '2020-04-17T23:18:14.996Z',
severity: 'high',
},
],
alerts: {
list: [
{
iid: 1,
status: 'acknowledged',
startedAt: '2020-03-17T23:18:14.996Z',
endedAt: '2020-04-17T23:18:14.996Z',
severity: 'high',
},
],
},
alertsCount,
errored: false,
},
@ -326,27 +333,31 @@ describe('AlertManagementList', () => {
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false, sort: 'STARTED_AT_ASC', alertsCount },
data: { alerts: { list: mockAlerts }, errored: false, sort: 'STARTED_AT_ASC', alertsCount },
loading: false,
stubs: { GlTable },
});
});
it('updates sort with new direction and column key', () => {
findSeverityColumnHeader().trigger('click');
expect(wrapper.vm.$data.sort).toEqual('SEVERITY_ASC');
expect(wrapper.vm.$data.sort).toBe('SEVERITY_ASC');
findSeverityColumnHeader().trigger('click');
expect(wrapper.vm.$data.sort).toEqual('SEVERITY_DESC');
expect(wrapper.vm.$data.sort).toBe('SEVERITY_DESC');
});
it('updates the `ariaSort` attribute so the sort icon appears in the proper column', () => {
expect(mockStartedAtCol.ariaSort).toEqual('ascending');
expect(findStartTimeColumnHeader().attributes('aria-sort')).toBe('ascending');
findSeverityColumnHeader().trigger('click');
expect(mockStartedAtCol.ariaSort).toEqual('none');
wrapper.vm.$nextTick(() => {
expect(findStartTimeColumnHeader().attributes('aria-sort')).toBe('none');
expect(findSeverityColumnHeader().attributes('aria-sort')).toBe('ascending');
});
});
});
@ -367,7 +378,7 @@ describe('AlertManagementList', () => {
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount, errored: false },
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
loading: false,
});
});
@ -403,7 +414,7 @@ describe('AlertManagementList', () => {
jest.spyOn(Tracking, 'event');
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount },
data: { alerts: { list: mockAlerts }, alertsCount },
loading: false,
});
});
@ -424,4 +435,64 @@ describe('AlertManagementList', () => {
});
});
});
describe('Pagination', () => {
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: { list: mockAlerts, pageInfo: {} }, alertsCount, errored: false },
loading: false,
});
});
it('does NOT show pagination control when list is smaller than default page size', () => {
findStatusTabs().vm.$emit('input', 3);
wrapper.vm.$nextTick(() => {
expect(findPagination().exists()).toBe(false);
});
});
it('shows pagination control when list is larger than default page size', () => {
findStatusTabs().vm.$emit('input', 0);
wrapper.vm.$nextTick(() => {
expect(findPagination().exists()).toBe(true);
});
});
describe('prevPage', () => {
it('returns prevPage number', () => {
findPagination().vm.$emit('input', 3);
return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.prevPage).toBe(2);
});
});
it('returns 0 when it is the first page', () => {
findPagination().vm.$emit('input', 1);
return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.prevPage).toBe(0);
});
});
});
describe('nextPage', () => {
it('returns nextPage number', () => {
findPagination().vm.$emit('input', 1);
return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.nextPage).toBe(2);
});
});
it('returns `null` when currentPage is already last page', () => {
findStatusTabs().vm.$emit('input', 3);
findPagination().vm.$emit('input', 1);
return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.nextPage).toBeNull();
});
});
});
});
});

View file

@ -25,7 +25,7 @@ exports[`IDE pipeline stage renders stage details & icon 1`] = `
</strong>
<div
class="append-right-8 gl-ml-2"
class="gl-mr-3 gl-ml-2"
>
<span
class="badge badge-pill"

View file

@ -10,7 +10,7 @@ describe('Job Log', () => {
let vm;
const trace =
'<span>Running with gitlab-runner 12.1.0 (de7731dd)<br/></span><span> on docker-auto-scale-com d5ae8d25<br/></span><div class="append-right-8" data-timestamp="1565502765" data-section="prepare-executor" role="button"></div><span class="section section-header js-s-prepare-executor">Using Docker executor with image ruby:2.6 ...<br/></span>';
'<span>Running with gitlab-runner 12.1.0 (de7731dd)<br/></span><span> on docker-auto-scale-com d5ae8d25<br/></span><div class="gl-mr-3" data-timestamp="1565502765" data-section="prepare-executor" role="button"></div><span class="section section-header js-s-prepare-executor">Using Docker executor with image ruby:2.6 ...<br/></span>';
beforeEach(() => {
store = createStore();

View file

@ -10,6 +10,7 @@ describe Gitlab::Diff::Formatters::ImageFormatter do
head_sha: 789,
old_path: 'old_image.png',
new_path: 'new_image.png',
file_identifier_hash: '777',
position_type: 'image'
}
end

View file

@ -10,6 +10,7 @@ describe Gitlab::Diff::Formatters::TextFormatter do
head_sha: 789,
old_path: 'old_path.txt',
new_path: 'new_path.txt',
file_identifier_hash: '777',
line_range: nil
}
end

View file

@ -144,6 +144,7 @@ Releases::Link:
- url
- name
- filepath
- link_type
- created_at
- updated_at
ProjectMember:

View file

@ -34,6 +34,8 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
let(:redis_requests_total) { double('redis calls total metric') }
let(:running_jobs_metric) { double('running jobs metric') }
let(:redis_seconds_metric) { double('redis seconds metric') }
let(:elasticsearch_seconds_metric) { double('elasticsearch seconds metric') }
let(:elasticsearch_requests_total) { double('elasticsearch calls total metric') }
before do
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_queue_duration_seconds, anything, anything, anything).and_return(queue_duration_seconds)
@ -42,9 +44,11 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_db_seconds, anything, anything, anything).and_return(db_seconds_metric)
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_gitaly_seconds, anything, anything, anything).and_return(gitaly_seconds_metric)
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_redis_requests_duration_seconds, anything, anything, anything).and_return(redis_seconds_metric)
allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_elasticsearch_requests_duration_seconds, anything, anything, anything).and_return(elasticsearch_seconds_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_retried_total, anything).and_return(retried_total_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_redis_requests_total, anything).and_return(redis_requests_total)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_elasticsearch_requests_total, anything).and_return(elasticsearch_requests_total)
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric)
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric)
@ -76,6 +80,9 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
let(:redis_calls) { 2 }
let(:redis_duration) { 0.01 }
let(:elasticsearch_calls) { 8 }
let(:elasticsearch_duration) { 0.54 }
before do
allow(subject).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
@ -86,14 +93,19 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
job[:redis_calls] = redis_calls
job[:redis_duration_s] = redis_duration
job[:elasticsearch_calls] = elasticsearch_calls
job[:elasticsearch_duration_s] = elasticsearch_duration
allow(running_jobs_metric).to receive(:increment)
allow(redis_requests_total).to receive(:increment)
allow(elasticsearch_requests_total).to receive(:increment)
allow(queue_duration_seconds).to receive(:observe)
allow(user_execution_seconds_metric).to receive(:observe)
allow(db_seconds_metric).to receive(:observe)
allow(gitaly_seconds_metric).to receive(:observe)
allow(completion_seconds_metric).to receive(:observe)
allow(redis_seconds_metric).to receive(:observe)
allow(elasticsearch_seconds_metric).to receive(:observe)
end
it 'yields block' do
@ -109,7 +121,9 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
expect(gitaly_seconds_metric).to receive(:observe).with(labels_with_job_status, gitaly_duration)
expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, monotonic_time_duration)
expect(redis_seconds_metric).to receive(:observe).with(labels_with_job_status, redis_duration)
expect(elasticsearch_seconds_metric).to receive(:observe).with(labels_with_job_status, elasticsearch_duration)
expect(redis_requests_total).to receive(:increment).with(labels_with_job_status, redis_calls)
expect(elasticsearch_requests_total).to receive(:increment).with(labels_with_job_status, elasticsearch_calls)
subject.call(worker, job, :test) { nil }
end

View file

@ -195,6 +195,10 @@ RSpec.configure do |config|
stub_feature_flags(flag => enable_rugged)
end
# Disable the usage of file_identifier_hash by default until it is ready
# See https://gitlab.com/gitlab-org/gitlab/-/issues/33867
stub_feature_flags(file_identifier_hash: false)
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
end

View file

@ -32,7 +32,21 @@ RSpec.shared_examples "position formatter" do
subject { formatter.to_h }
it { is_expected.to eq(formatter_hash) }
context 'when file_identifier_hash is disabled' do
before do
stub_feature_flags(file_identifier_hash: false)
end
it { is_expected.to eq(formatter_hash.except(:file_identifier_hash)) }
end
context 'when file_identifier_hash is enabled' do
before do
stub_feature_flags(file_identifier_hash: true)
end
it { is_expected.to eq(formatter_hash) }
end
end
describe '#==' do