Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-09-21 03:10:15 +00:00
parent 11bda5e7e1
commit d63d9aebd7
27 changed files with 639 additions and 313 deletions

View File

@ -253,7 +253,7 @@ gem 'rainbow', '~> 3.0'
gem 'ruby-progressbar', '~> 1.10'
# Linear-time regex library for untrusted regular expressions
gem 're2', '2.0.0'
gem 're2', '2.1.2'
# Misc

View File

@ -491,16 +491,16 @@
{"name":"rbtree","version":"0.4.6","platform":"ruby","checksum":"14eea4469b24fd2472542e5f3eb105d6344c8ccf36f0b56d55fdcfeb4e0f10fc"},
{"name":"rchardet","version":"1.8.0","platform":"ruby","checksum":"693acd5253d5ade81a51940697955f6dd4bb2f0d245bda76a8e23deec70a52c7"},
{"name":"rdoc","version":"6.3.2","platform":"ruby","checksum":"def4a720235c27d56c176ae73555e647eb04ea58a8bbaa927f8f9f79de7805a6"},
{"name":"re2","version":"2.0.0","platform":"aarch64-linux","checksum":"677ddce4c38d659de899651acbfd7c6b5331f984a7101d9179ac247284f2212a"},
{"name":"re2","version":"2.0.0","platform":"arm-linux","checksum":"f657d689922e5ac215b486e4f2ca909f1079eab616269a1d8fc0cccd63ef28af"},
{"name":"re2","version":"2.0.0","platform":"arm64-darwin","checksum":"ffc8e5663381ff344ee6a2e55c7d0be81ef9b43174a41e977c4e18a11f965be1"},
{"name":"re2","version":"2.0.0","platform":"ruby","checksum":"09075fab88b7ab40c2374d75a20504408dc26539c11931b146d5f72892718925"},
{"name":"re2","version":"2.0.0","platform":"x64-mingw-ucrt","checksum":"253b3de21ca563cdb93c9fd69738a2a66713e381bae4530ff2cae105c6fd1a8e"},
{"name":"re2","version":"2.0.0","platform":"x64-mingw32","checksum":"d4b52fc21719f262c2a438912f009da868b31aed1688ec90e4e1696898fb53d3"},
{"name":"re2","version":"2.0.0","platform":"x86-linux","checksum":"26abee219e3fd69ba5c6a7bdb882880b8af6502cf912da7a7837e38ad02a29e7"},
{"name":"re2","version":"2.0.0","platform":"x86-mingw32","checksum":"ffec6da4c547e44a6c1a467b0b01b2dcc2940e081923221a2ac3e4b08a219c26"},
{"name":"re2","version":"2.0.0","platform":"x86_64-darwin","checksum":"48b3ba3fea8cc84709a4195300cd1c627b2496f16e5865662c54e67f7aca1ccf"},
{"name":"re2","version":"2.0.0","platform":"x86_64-linux","checksum":"1fb161e6e5d9efed59ed0062536f2cb9ab5fba367e209d0dc66f99f2864d42ff"},
{"name":"re2","version":"2.1.2","platform":"aarch64-linux","checksum":"dbd87fb2432f17734cfb948d38cb0d138335228d31c4316719c75ac0a976731f"},
{"name":"re2","version":"2.1.2","platform":"arm-linux","checksum":"4c0d903508bc0d82f27d09c84498e0fdc6ab87ef418bea884d711b85f7fca62f"},
{"name":"re2","version":"2.1.2","platform":"arm64-darwin","checksum":"39bb8a44a4afbd2d3f2e07d531d223728f7c4b83946bc55e8ce6aae8c2c34579"},
{"name":"re2","version":"2.1.2","platform":"ruby","checksum":"06bd25bf566dda720cbc607ceecb65ed16871427fbcb3e5239c300ec796fee9c"},
{"name":"re2","version":"2.1.2","platform":"x64-mingw-ucrt","checksum":"a1ad9cda576dae6020664c7578d7e43d2062ca21e5e945aea125f539944ee713"},
{"name":"re2","version":"2.1.2","platform":"x64-mingw32","checksum":"804fc9bafc6590e3e75d27d289546d7223b51bf3e46e9d81ee89cf5168c1a9be"},
{"name":"re2","version":"2.1.2","platform":"x86-linux","checksum":"6a048f8a1511a5481f7a386045e67ecbb221a856d2987b33a231efb5e17250bc"},
{"name":"re2","version":"2.1.2","platform":"x86-mingw32","checksum":"8e0e9d0f3166ff3000ffa38a05c9e5275ba431f2abce494fa7600033206ce108"},
{"name":"re2","version":"2.1.2","platform":"x86_64-darwin","checksum":"6b41f328b551173e58eb04320c70295de143b5aeb38c78122aa623a4308bc472"},
{"name":"re2","version":"2.1.2","platform":"x86_64-linux","checksum":"e082a1db722b7da3adc9e1f9d8681cba80a2d1176c54ae741443965ce277e6af"},
{"name":"recaptcha","version":"5.12.3","platform":"ruby","checksum":"37d1894add9e70a54d0c6c7f0ecbeedffbfa7d075acfbd4c509818dfdebdb7ee"},
{"name":"recursive-open-struct","version":"1.1.3","platform":"ruby","checksum":"a3538a72552fcebcd0ada657bdff313641a4a5fbc482c08cfb9a65acb1c9de5a"},
{"name":"redcarpet","version":"3.6.0","platform":"ruby","checksum":"8ad1889c0355ff4c47174af14edd06d62f45a326da1da6e8a121d59bdcd2e9e9"},

View File

@ -1296,7 +1296,7 @@ GEM
rbtree (0.4.6)
rchardet (1.8.0)
rdoc (6.3.2)
re2 (2.0.0)
re2 (2.1.2)
mini_portile2 (~> 2.8.4)
recaptcha (5.12.3)
json
@ -1963,7 +1963,7 @@ DEPENDENCIES
rainbow (~> 3.0)
rbtrace (~> 0.4)
rdoc (~> 6.3.2)
re2 (= 2.0.0)
re2 (= 2.1.2)
recaptcha (~> 5.12)
redis (~> 4.8.0)
redis-actionpack (~> 5.3.0)

View File

@ -8,9 +8,7 @@ import { TRACKING_CATEGORIES } from '~/ci/constants';
import { keepLatestDownstreamPipelines } from '~/ci/pipeline_details/utils/parsing_utils';
import LegacyPipelineMiniGraph from '~/ci/pipeline_mini_graph/legacy_pipeline_mini_graph.vue';
import PipelineFailedJobsWidget from '~/ci/pipelines_page/components/failure_widget/pipeline_failed_jobs_widget.vue';
import eventHub from '~/ci/event_hub';
import PipelineOperations from '../pipelines_page/components/pipeline_operations.vue';
import PipelineStopModal from '../pipelines_page/components/pipeline_stop_modal.vue';
import PipelineTriggerer from '../pipelines_page/components/pipeline_triggerer.vue';
import PipelineUrl from '../pipelines_page/components/pipeline_url.vue';
import PipelinesStatusBadge from '../pipelines_page/components/pipelines_status_badge.vue';
@ -19,6 +17,23 @@ const HIDE_TD_ON_MOBILE = 'gl-display-none! gl-lg-display-table-cell!';
const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';
/**
* Pipelines Table
*
* Presentational component of a table of pipelines. This component does not
* fetch the list of pipelines and instead expects it as a prop.
* GraphQL actions for pipelines, such as retrying, canceling, etc.
* are handled within this component.
*
* Use this `legacy_pipelines_table_wrapper` if you need a fully functional REST component.
*
* IMPORTANT: When using this component, make sure to handle the following events:
* 1- @refresh-pipeline-table
* 2- @cancel-pipeline
* 3- @retry-pipeline
*
*/
export default {
components: {
GlTableLite,
@ -26,7 +41,6 @@ export default {
PipelineFailedJobsWidget,
PipelineOperations,
PipelinesStatusBadge,
PipelineStopModal,
PipelineTriggerer,
PipelineUrl,
},
@ -63,14 +77,6 @@ export default {
required: true,
},
},
data() {
return {
pipelineId: 0,
pipeline: {},
endpoint: '',
cancelingPipeline: null,
};
},
computed: {
showFailedJobsWidget() {
return this.glFeatures.ciJobFailuresInMr;
@ -131,17 +137,6 @@ export default {
return this.pipelines;
},
},
watch: {
pipelines() {
this.cancelingPipeline = null;
},
},
created() {
eventHub.$on('openConfirmationModal', this.setModalData);
},
beforeDestroy() {
eventHub.$off('openConfirmationModal', this.setModalData);
},
methods: {
getDownstreamPipelines(pipeline) {
const downstream = pipeline.triggered;
@ -153,14 +148,16 @@ export default {
failedJobsCount(pipeline) {
return pipeline?.failed_builds?.length || 0;
},
setModalData(data) {
this.pipelineId = data.pipeline.id;
this.pipeline = data.pipeline;
this.endpoint = data.endpoint;
onRefreshPipelinesTable() {
this.$emit('refresh-pipelines-table');
},
onSubmit() {
eventHub.$emit('postAction', this.endpoint);
this.cancelingPipeline = this.pipelineId;
onRetryPipeline(pipeline) {
// This emit is only used by the `legacy_pipelines_table_wrapper`.
this.$emit('retry-pipeline', pipeline);
},
onCancelPipeline(pipeline) {
// This emit is only used by the `legacy_pipelines_table_wrapper`.
this.$emit('cancel-pipeline', pipeline);
},
trackPipelineMiniGraph() {
this.track('click_minigraph', { label: TRACKING_CATEGORIES.table });
@ -219,7 +216,12 @@ export default {
</template>
<template #cell(actions)="{ item }">
<pipeline-operations :pipeline="item" :canceling-pipeline="cancelingPipeline" />
<pipeline-operations
:pipeline="item"
@cancel-pipeline="onCancelPipeline"
@refresh-pipelines-table="onRefreshPipelinesTable"
@retry-pipeline="onRetryPipeline"
/>
</template>
<template #row-details="{ item }">
@ -234,7 +236,5 @@ export default {
/>
</template>
</gl-table-lite>
<pipeline-stop-modal :pipeline="pipeline" @submit="onSubmit" />
</div>
</template>

View File

@ -52,14 +52,12 @@ export default {
});
eventHub.$on('postAction', this.postAction);
eventHub.$on('retryPipeline', this.postAction);
eventHub.$on('clickedDropdown', this.updateTable);
eventHub.$on('updateTable', this.updateTable);
eventHub.$on('runMergeRequestPipeline', this.runMergeRequestPipeline);
},
beforeDestroy() {
eventHub.$off('postAction', this.postAction);
eventHub.$off('retryPipeline', this.postAction);
eventHub.$off('clickedDropdown', this.updateTable);
eventHub.$off('updateTable', this.updateTable);
eventHub.$off('runMergeRequestPipeline', this.runMergeRequestPipeline);
@ -68,6 +66,15 @@ export default {
this.poll.stop();
},
methods: {
onCancelPipeline(pipeline) {
this.postAction(pipeline.cancel_path);
},
onRefreshPipelinesTable() {
this.updateTable();
},
onRetryPipeline(pipeline) {
this.postAction(pipeline.retry_path);
},
updateInternalState(parameters) {
this.poll.stop();

View File

@ -7,29 +7,26 @@ export default {
GlButton,
},
props: {
newPipelinePath: {
type: String,
required: false,
default: null,
},
resetCachePath: {
type: String,
required: false,
default: null,
},
ciLintPath: {
type: String,
required: false,
default: null,
},
isResetCacheButtonLoading: {
type: Boolean,
required: false,
default: false,
},
newPipelinePath: {
type: String,
required: false,
default: null,
},
resetCachePath: {
type: String,
required: false,
default: null,
},
},
methods: {
onClickResetCache() {

View File

@ -1,22 +1,22 @@
<script>
import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import Tracking from '~/tracking';
import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, TRACKING_CATEGORIES } from '~/ci/constants';
import eventHub from '../../event_hub';
import PipelineMultiActions from './pipeline_multi_actions.vue';
import PipelinesManualActions from './pipelines_manual_actions.vue';
import PipelineStopModal from './pipeline_stop_modal.vue';
export default {
BUTTON_TOOLTIP_RETRY,
BUTTON_TOOLTIP_CANCEL,
directives: {
GlTooltip: GlTooltipDirective,
GlModalDirective,
},
components: {
GlButton,
PipelineMultiActions,
PipelinesManualActions,
PipelineStopModal,
},
mixins: [Tracking.mixin()],
props: {
@ -24,15 +24,12 @@ export default {
type: Object,
required: true,
},
cancelingPipeline: {
type: Number,
required: false,
default: null,
},
},
data() {
return {
isCanceling: false,
isRetrying: false,
showConfirmationModal: false,
};
},
computed: {
@ -41,27 +38,36 @@ export default {
this.pipeline?.details?.has_manual_actions || this.pipeline?.details?.has_scheduled_actions
);
},
isCancelling() {
return this.cancelingPipeline === this.pipeline.id;
},
},
watch: {
pipeline() {
this.isRetrying = false;
if (this.isCanceling || this.isRetrying) {
this.isCanceling = false;
this.isRetrying = false;
}
},
},
methods: {
onCloseModal() {
this.showConfirmationModal = false;
},
onConfirmCancelPipeline() {
this.isCanceling = true;
this.showConfirmationModal = false;
this.$emit('cancel-pipeline', this.pipeline);
},
handleCancelClick() {
this.showConfirmationModal = true;
this.trackClick('click_cancel_button');
eventHub.$emit('openConfirmationModal', {
pipeline: this.pipeline,
endpoint: this.pipeline.cancel_path,
});
},
handleRetryClick() {
this.isRetrying = true;
this.trackClick('click_retry_button');
eventHub.$emit('retryPipeline', this.pipeline.retry_path);
this.$emit('retry-pipeline', this.pipeline);
},
trackClick(action) {
this.track(action, { label: TRACKING_CATEGORIES.table });
@ -72,8 +78,19 @@ export default {
<template>
<div class="gl-text-right">
<pipeline-stop-modal
:pipeline="pipeline"
:show-confirmation-modal="showConfirmationModal"
@submit="onConfirmCancelPipeline"
@close-modal="onCloseModal"
/>
<div class="btn-group">
<pipelines-manual-actions v-if="hasActions" :iid="pipeline.iid" />
<pipelines-manual-actions
v-if="hasActions"
:iid="pipeline.iid"
@refresh-pipeline-table="$emit('refresh-pipelines-table')"
/>
<gl-button
v-if="pipeline.flags.retryable"
@ -94,11 +111,10 @@ export default {
<gl-button
v-if="pipeline.flags.cancelable"
v-gl-tooltip.hover
v-gl-modal-directive="'confirmation-modal'"
:aria-label="$options.BUTTON_TOOLTIP_CANCEL"
:title="$options.BUTTON_TOOLTIP_CANCEL"
:loading="isCancelling"
:disabled="isCancelling"
:loading="isCanceling"
:disabled="isCanceling"
icon="cancel"
variant="danger"
category="primary"

View File

@ -7,7 +7,7 @@ import CiIcon from '~/vue_shared/components/ci_icon.vue';
/**
* Pipeline Stop Modal.
*
* Renders the modal used to confirm stopping a pipeline.
* Renders the modal used to confirm cancelling a pipeline.
*/
export default {
components: {
@ -22,8 +22,15 @@ export default {
required: true,
deep: true,
},
showConfirmationModal: {
type: Boolean,
required: true,
},
},
computed: {
hasRef() {
return !isEmpty(this.pipeline.ref);
},
modalTitle() {
return sprintf(
s__('Pipeline|Stop pipeline #%{pipelineId}?'),
@ -34,10 +41,7 @@ export default {
);
},
modalText() {
return s__(`Pipeline|Youre about to stop pipeline #%{pipelineId}.`);
},
hasRef() {
return !isEmpty(this.pipeline.ref);
return s__(`Pipeline|You're about to stop pipeline #%{pipelineId}.`);
},
primaryProps() {
return {
@ -45,10 +49,13 @@ export default {
attributes: { variant: 'danger' },
};
},
cancelProps() {
return {
text: __('Cancel'),
};
showModal: {
get() {
return this.showConfirmationModal;
},
set() {
this.$emit('close-modal');
},
},
},
methods: {
@ -56,14 +63,16 @@ export default {
this.$emit('submit', event);
},
},
cancelProps: { text: __('Cancel') },
};
</script>
<template>
<gl-modal
v-model="showModal"
modal-id="confirmation-modal"
:title="modalTitle"
:action-primary="primaryProps"
:action-cancel="cancelProps"
:action-cancel="$options.cancelProps"
@primary="emitSubmit($event)"
>
<p>
@ -74,7 +83,7 @@ export default {
</gl-sprintf>
</p>
<p v-if="pipeline">
<p>
<ci-icon
v-if="pipeline.details"
:status="pipeline.details.status"

View File

@ -6,7 +6,6 @@ import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_m
import { s__, __, sprintf } from '~/locale';
import Tracking from '~/tracking';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
import eventHub from '../../event_hub';
import { TRACKING_CATEGORIES } from '../../constants';
import getPipelineActionsQuery from '../graphql/queries/get_pipeline_actions.query.graphql';
@ -94,7 +93,7 @@ export default {
.post(`${action.playPath}.json`)
.then(() => {
this.isLoading = false;
eventHub.$emit('updateTable');
this.$emit('refresh-pipeline-table');
})
.catch(() => {
this.isLoading = false;

View File

@ -13,7 +13,7 @@ import {
RAW_TEXT_WARNING,
TRACKING_CATEGORIES,
} from '~/ci/constants';
import PipelinesTableComponent from '~/ci/common/pipelines_table.vue';
import PipelinesTable from '~/ci/common/pipelines_table.vue';
import PipelinesMixin from '~/ci/pipeline_details/mixins/pipelines_mixin';
import { validateParams } from '~/ci/pipeline_details/utils';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
@ -37,7 +37,7 @@ export default {
NavigationTabs,
NavigationControls,
PipelinesFilteredSearch,
PipelinesTableComponent,
PipelinesTable,
TablePagination,
},
mixins: [PipelinesMixin, Tracking.mixin()],
@ -431,12 +431,15 @@ export default {
/>
<div v-else-if="stateToRender === $options.stateMap.tableList">
<pipelines-table-component
<pipelines-table
:pipelines="state.pipelines"
:pipeline-schedule-url="pipelineScheduleUrl"
:update-graph-dropdown="updateGraphDropdown"
:view-type="viewType"
:pipeline-key-option="selectedPipelineKeyOption"
@cancel-pipeline="onCancelPipeline"
@refresh-pipelines-table="onRefreshPipelinesTable"
@retry-pipeline="onRetryPipeline"
/>
</div>

View File

@ -2,7 +2,7 @@
import { GlButton, GlEmptyState, GlLoadingIcon, GlModal, GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { getParameterByName } from '~/lib/utils/url_utility';
import PipelinesTableComponent from '~/ci/common/pipelines_table.vue';
import PipelinesTable from '~/ci/common/pipelines_table.vue';
import { PipelineKeyOptions } from '~/ci/constants';
import eventHub from '~/ci/event_hub';
import PipelinesMixin from '~/ci/pipeline_details/mixins/pipelines_mixin';
@ -21,7 +21,7 @@ export default {
GlLoadingIcon,
GlModal,
GlSprintf,
PipelinesTableComponent,
PipelinesTable,
TablePagination,
},
mixins: [PipelinesMixin, glFeatureFlagMixin()],
@ -279,11 +279,14 @@ export default {
{{ $options.i18n.runPipelineText }}
</gl-button>
<pipelines-table-component
<pipelines-table
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:view-type="viewType"
:pipeline-key-option="$options.PipelineKeyOptions[0]"
@cancel-pipeline="onCancelPipeline"
@refresh-pipelines-table="onRefreshPipelinesTable"
@retry-pipeline="onRetryPipeline"
>
<template #table-header-actions>
<div v-if="canRenderPipelineButton" class="gl-text-right">
@ -296,7 +299,7 @@ export default {
</gl-button>
</div>
</template>
</pipelines-table-component>
</pipelines-table>
</div>
<gl-modal

View File

@ -11,7 +11,7 @@ const apolloProvider = new VueApollo({
/**
* Used in:
* - Project Pipelines List (projects:pipelines:index)
* - Project Pipelines List (projects:pipelines)
* - Commit details View > Pipelines Tab > Pipelines Table (projects:commit:pipelines)
* - Merge request details View > Pipelines Tab > Pipelines Table (projects:merge_requests:show)
* - New merge request View > Pipelines Tab > Pipelines Table (projects:merge_requests:creations:new)

View File

@ -93,7 +93,7 @@ function mountPipelines() {
const { mrWidgetData } = gl;
const table = new Vue({
components: {
CommitPipelinesTable: () => {
MergeRequestPipelinesTable: () => {
return gon.features.mrPipelinesGraphql
? import('~/ci/merge_requests/components/pipelines_table_wrapper.vue')
: import('~/commit/pipelines/legacy_pipelines_table_wrapper.vue');
@ -112,7 +112,7 @@ function mountPipelines() {
withFailedJobsDetails: true,
},
render(createElement) {
return createElement('commit-pipelines-table', {
return createElement('merge-request-pipelines-table', {
props: {
endpoint: pipelineTableViewEl.dataset.endpoint,
emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath,
@ -347,11 +347,11 @@ export default class MergeRequestTabs {
}
// this.hideSidebar();
this.resetViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable);
} else if (action === 'new') {
this.expandView();
this.resetViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable);
} else if (this.isDiffAction(action)) {
if (!isInVueNoteablePage()) {
/*
@ -366,7 +366,7 @@ export default class MergeRequestTabs {
}
// this.hideSidebar();
this.expandViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable);
this.commitsTab.classList.remove('active');
} else if (action === 'pipelines') {
// this.hideSidebar();
@ -384,7 +384,7 @@ export default class MergeRequestTabs {
// this.showSidebar();
this.resetViewContainer();
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable);
}
renderGFM(document.querySelector('.detail-page-description'));
@ -522,7 +522,7 @@ export default class MergeRequestTabs {
}
mountPipelinesView() {
this.commitPipelinesTable = mountPipelines();
this.mergeRequestPipelinesTable = mountPipelines();
}
// load the diff tab content from the backend

View File

@ -78,10 +78,6 @@ module MergeRequestsHelper
.execute(include_routes: true)
end
def merge_request_button_visibility(merge_request, closed)
return 'hidden' if merge_request_button_hidden?(merge_request, closed)
end
def merge_request_button_hidden?(merge_request, closed)
merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_or_merged_without_fork?
end

View File

@ -17,12 +17,42 @@ module Analytics
where(condition.arel.exists)
end
def self.issuable_id_column
:issue_id
end
class << self
def project_column
:project_id
end
def self.issuable_model
::Issue
def issuable_id_column
:issue_id
end
def issuable_model
::Issue
end
def select_columns
[
*super,
issuable_model.arel_table[:weight],
issuable_model.arel_table[:sprint_id]
]
end
def column_list
[
*super,
:weight,
:sprint_id
]
end
def insert_column_list
[
*super,
:weight,
:sprint_id
]
end
end
end
end

View File

@ -17,6 +17,10 @@ module Analytics
where(condition.arel.exists)
end
def self.project_column
:target_project_id
end
def self.issuable_id_column
:merge_request_id
end

View File

@ -57,45 +57,19 @@ module Analytics
class_methods do
def upsert_data(data)
upsert_values = data.map do |row|
row.values_at(
:stage_event_hash_id,
:issuable_id,
:group_id,
:project_id,
:milestone_id,
:author_id,
:state_id,
:start_event_timestamp,
:end_event_timestamp
)
end
upsert_values = data.map { |row| row.values_at(*column_list) }
value_list = Arel::Nodes::ValuesList.new(upsert_values).to_sql
query = <<~SQL
INSERT INTO #{quoted_table_name}
(
stage_event_hash_id,
#{connection.quote_column_name(issuable_id_column)},
group_id,
project_id,
milestone_id,
author_id,
state_id,
start_event_timestamp,
end_event_timestamp
#{insert_column_list.join(",\n")}
)
#{value_list}
ON CONFLICT(stage_event_hash_id, #{issuable_id_column})
DO UPDATE SET
group_id = excluded.group_id,
project_id = excluded.project_id,
milestone_id = excluded.milestone_id,
author_id = excluded.author_id,
state_id = excluded.state_id,
start_event_timestamp = excluded.start_event_timestamp,
end_event_timestamp = excluded.end_event_timestamp
#{column_updates.join(",\n")}
SQL
result = connection.execute(query)
@ -113,6 +87,51 @@ module Analytics
def arel_order(arel_node, direction)
direction.to_sym == :desc ? arel_node.desc : arel_node.asc
end
def select_columns
[
issuable_model.arel_table[:id],
issuable_model.arel_table[project_column].as('project_id'),
issuable_model.arel_table[:milestone_id],
issuable_model.arel_table[:author_id],
issuable_model.arel_table[:state_id],
Project.arel_table[:parent_id].as('group_id')
]
end
def column_list
[
:stage_event_hash_id,
:issuable_id,
:group_id,
:project_id,
:milestone_id,
:author_id,
:state_id,
:start_event_timestamp,
:end_event_timestamp
]
end
def insert_column_list
[
:stage_event_hash_id,
connection.quote_column_name(issuable_id_column),
:group_id,
:project_id,
:milestone_id,
:author_id,
:state_id,
:start_event_timestamp,
:end_event_timestamp
]
end
def column_updates
insert_column_list.map do |column|
"#{column} = excluded.#{column}"
end
end
end
end
end

View File

@ -1,28 +1,31 @@
= gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-sentry-settings'), html: { class: 'fieldset-form', id: 'sentry-settings' } do |f|
= form_errors(@application_setting)
%span.text-muted
= _('Changing any setting here requires an application restart')
%fieldset.gl-text-secondary
= safe_format(s_('AdminSettings|GitLab uses the %{bold_start}Rails%{bold_end} and %{bold_start}Browser JavaScript%{bold_end} Sentry SDKs to send events to Sentry. For changes to Rails integration settings to take effect, restart GitLab.'), tag_pair(tag.b, :bold_start, :bold_end))
%fieldset
.form-group
= f.gitlab_ui_checkbox_component :sentry_enabled, _('Enable Sentry error tracking')
.form-group
= f.label :sentry_dsn, _('DSN'), class: 'label-light'
= f.text_field :sentry_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/1'
.form-group
= f.label :sentry_clientside_dsn, _('Clientside DSN'), class: 'label-light'
= f.text_field :sentry_clientside_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/2'
= f.gitlab_ui_checkbox_component :sentry_enabled, s_('AdminSettings|Enable Sentry for Rails and Browser JavaScript')
.form-group
= f.label :sentry_environment, _('Environment'), class: 'label-light'
= f.text_field :sentry_environment, class: 'form-control gl-form-input', placeholder: Rails.env
%p.text-muted
= _("Changing any setting bellow doesn't require an application restart")
%fieldset
.form-text.text-muted
= safe_format(s_('AdminSettings|%{setting_name} value used by both Rails and Browser JavaScript SDKs.'), setting_name: content_tag(:code, 'environment'))
.form-group
= f.label :sentry_dsn, _('DSN'), class: 'label-light'
= f.text_field :sentry_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/1'
.form-text.text-muted
= safe_format(s_('AdminSettings|%{setting_name} value used by the Rails SDK.'), setting_name: content_tag(:code, 'dsn'))
.form-group
= f.label :sentry_clientside_dsn, _('Clientside DSN'), class: 'label-light'
= f.text_field :sentry_clientside_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/2'
.form-text.text-muted
= safe_format(s_('AdminSettings|%{setting_name} value used by the Browser JavaScript SDK.'), setting_name: content_tag(:code, 'dsn'))
.form-group
= f.label :sentry_clientside_traces_sample_rate, _('Clientside traces sample rate'), class: 'label-light'
= f.number_field :sentry_clientside_traces_sample_rate, class: 'form-control gl-form-input', placeholder: '0.5', min: 0, max: 1, step: 0.001
.form-text.text-muted
= safe_format(s_('AdminSettings|%{setting_name} value used by the Browser JavaScript SDK.'), setting_name: content_tag(:code, 'tracesSampleRate'))
= f.submit _('Save changes'), pajamas_button: true

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddSprintIdAndWeightToVsaIssues < Gitlab::Database::Migration[2.1]
enable_lock_retries!
def up
add_column :analytics_cycle_analytics_issue_stage_events, :weight, :integer
add_column :analytics_cycle_analytics_issue_stage_events, :sprint_id, :bigint
end
def down
remove_column :analytics_cycle_analytics_issue_stage_events, :sprint_id
remove_column :analytics_cycle_analytics_issue_stage_events, :weight
end
end

View File

@ -0,0 +1 @@
99e55170557dcda361f441d1333f4dc9d99133a469f1d17805478f3407d2a093

View File

@ -720,7 +720,9 @@ CREATE TABLE analytics_cycle_analytics_issue_stage_events (
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
)
PARTITION BY HASH (stage_event_hash_id);
@ -733,7 +735,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_00 FOR VALUES WITH (modulus 32, remainder 0);
@ -746,7 +750,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01 FOR VALUES WITH (modulus 32, remainder 1);
@ -759,7 +765,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_02 FOR VALUES WITH (modulus 32, remainder 2);
@ -772,7 +780,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_03 FOR VALUES WITH (modulus 32, remainder 3);
@ -785,7 +795,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_04 FOR VALUES WITH (modulus 32, remainder 4);
@ -798,7 +810,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_05 FOR VALUES WITH (modulus 32, remainder 5);
@ -811,7 +825,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_06 FOR VALUES WITH (modulus 32, remainder 6);
@ -824,7 +840,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_07 FOR VALUES WITH (modulus 32, remainder 7);
@ -837,7 +855,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_08 FOR VALUES WITH (modulus 32, remainder 8);
@ -850,7 +870,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_09 FOR VALUES WITH (modulus 32, remainder 9);
@ -863,7 +885,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_10 FOR VALUES WITH (modulus 32, remainder 10);
@ -876,7 +900,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_11 FOR VALUES WITH (modulus 32, remainder 11);
@ -889,7 +915,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 FOR VALUES WITH (modulus 32, remainder 12);
@ -902,7 +930,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_13 FOR VALUES WITH (modulus 32, remainder 13);
@ -915,7 +945,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_14 FOR VALUES WITH (modulus 32, remainder 14);
@ -928,7 +960,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_15 FOR VALUES WITH (modulus 32, remainder 15);
@ -941,7 +975,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_16 FOR VALUES WITH (modulus 32, remainder 16);
@ -954,7 +990,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_17 FOR VALUES WITH (modulus 32, remainder 17);
@ -967,7 +1005,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_18 FOR VALUES WITH (modulus 32, remainder 18);
@ -980,7 +1020,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_19 FOR VALUES WITH (modulus 32, remainder 19);
@ -993,7 +1035,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_20 FOR VALUES WITH (modulus 32, remainder 20);
@ -1006,7 +1050,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_21 FOR VALUES WITH (modulus 32, remainder 21);
@ -1019,7 +1065,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_22 FOR VALUES WITH (modulus 32, remainder 22);
@ -1032,7 +1080,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_23 FOR VALUES WITH (modulus 32, remainder 23);
@ -1045,7 +1095,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_24 FOR VALUES WITH (modulus 32, remainder 24);
@ -1058,7 +1110,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_25 FOR VALUES WITH (modulus 32, remainder 25);
@ -1071,7 +1125,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 FOR VALUES WITH (modulus 32, remainder 26);
@ -1084,7 +1140,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_27 FOR VALUES WITH (modulus 32, remainder 27);
@ -1097,7 +1155,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_28 FOR VALUES WITH (modulus 32, remainder 28);
@ -1110,7 +1170,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_29 FOR VALUES WITH (modulus 32, remainder 29);
@ -1123,7 +1185,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_30 FOR VALUES WITH (modulus 32, remainder 30);
@ -1136,7 +1200,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even
author_id bigint,
start_event_timestamp timestamp with time zone NOT NULL,
end_event_timestamp timestamp with time zone,
state_id smallint DEFAULT 1 NOT NULL
state_id smallint DEFAULT 1 NOT NULL,
weight integer,
sprint_id bigint
);
ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_31 FOR VALUES WITH (modulus 32, remainder 31);

View File

@ -3375,6 +3375,15 @@ msgstr ""
msgid "AdminProjects|Delete Project %{projectName}?"
msgstr ""
msgid "AdminSettings|%{setting_name} value used by both Rails and Browser JavaScript SDKs."
msgstr ""
msgid "AdminSettings|%{setting_name} value used by the Browser JavaScript SDK."
msgstr ""
msgid "AdminSettings|%{setting_name} value used by the Rails SDK."
msgstr ""
msgid "AdminSettings|%{strongStart}WARNING:%{strongEnd} Environment variable %{environment_variable} does not exist or is not pointing to a valid directory. %{icon_link}"
msgstr ""
@ -3450,6 +3459,9 @@ msgstr ""
msgid "AdminSettings|Enable Registration Features"
msgstr ""
msgid "AdminSettings|Enable Sentry for Rails and Browser JavaScript"
msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
@ -3492,6 +3504,9 @@ msgstr ""
msgid "AdminSettings|Git abuse rate limit"
msgstr ""
msgid "AdminSettings|GitLab uses the %{bold_start}Rails%{bold_end} and %{bold_start}Browser JavaScript%{bold_end} Sentry SDKs to send events to Sentry. For changes to Rails integration settings to take effect, restart GitLab."
msgstr ""
msgid "AdminSettings|Group runners expiration"
msgstr ""
@ -9610,12 +9625,6 @@ msgstr ""
msgid "Changes:"
msgstr ""
msgid "Changing any setting bellow doesn't require an application restart"
msgstr ""
msgid "Changing any setting here requires an application restart"
msgstr ""
msgid "Characters left"
msgstr ""
@ -17959,9 +17968,6 @@ msgstr ""
msgid "Enable SSL verification"
msgstr ""
msgid "Enable Sentry error tracking"
msgstr ""
msgid "Enable Snowplow tracking"
msgstr ""
@ -34963,7 +34969,7 @@ msgstr ""
msgid "Pipeline|We are currently unable to fetch pipeline data"
msgstr ""
msgid "Pipeline|Youre about to stop pipeline #%{pipelineId}."
msgid "Pipeline|You're about to stop pipeline #%{pipelineId}."
msgstr ""
msgid "Pipeline|for"

View File

@ -36,7 +36,7 @@ RSpec.describe 'Database schema', feature_category: :database do
approvers: %w[target_id user_id],
analytics_cycle_analytics_aggregations: %w[last_full_issues_id last_full_merge_requests_id last_incremental_issues_id last_full_run_issues_id last_full_run_merge_requests_id last_incremental_merge_requests_id last_consistency_check_issues_stage_event_hash_id last_consistency_check_issues_issuable_id last_consistency_check_merge_requests_stage_event_hash_id last_consistency_check_merge_requests_issuable_id],
analytics_cycle_analytics_merge_request_stage_events: %w[author_id group_id merge_request_id milestone_id project_id stage_event_hash_id state_id],
analytics_cycle_analytics_issue_stage_events: %w[author_id group_id issue_id milestone_id project_id stage_event_hash_id state_id],
analytics_cycle_analytics_issue_stage_events: %w[author_id group_id issue_id milestone_id project_id stage_event_hash_id state_id sprint_id],
audit_events: %w[author_id entity_id target_id],
award_emoji: %w[awardable_id user_id],
aws_roles: %w[role_external_id],

View File

@ -1,9 +1,7 @@
import '~/commons';
import { GlTableLite } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import fixture from 'test_fixtures/pipelines/pipelines.json';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import LegacyPipelineMiniGraph from '~/ci/pipeline_mini_graph/legacy_pipeline_mini_graph.vue';
import PipelineFailedJobsWidget from '~/ci/pipelines_page/components/failure_widget/pipeline_failed_jobs_widget.vue';
import PipelineOperations from '~/ci/pipelines_page/components/pipeline_operations.vue';
@ -20,14 +18,12 @@ import {
import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
jest.mock('~/ci/event_hub');
describe('Pipelines Table', () => {
let pipeline;
let wrapper;
let trackingSpy;
const defaultProvide = {
fullPath: '/my-project/',
glFeatures: {},
withFailedJobsDetails: false,
};
@ -39,32 +35,31 @@ describe('Pipelines Table', () => {
withFailedJobsDetails: true,
};
const { pipelines } = fixture;
const defaultProps = {
pipelines: [],
pipelines,
viewType: 'root',
pipelineKeyOption: PipelineKeyOptions[0],
};
const createMockPipeline = () => {
// Clone fixture as it could be modified by tests
const { pipelines } = JSON.parse(JSON.stringify(fixture));
return pipelines.find((p) => p.user !== null && p.commit !== null);
};
const [firstPipeline] = pipelines;
const createComponent = (props = {}, provide = {}) => {
wrapper = extendedWrapper(
mount(PipelinesTable, {
propsData: {
...defaultProps,
...props,
},
provide: {
...defaultProvide,
...provide,
},
stubs: ['PipelineFailedJobsWidget'],
}),
);
const createComponent = ({ props = {}, provide = {}, stubs = {} } = {}) => {
wrapper = mountExtended(PipelinesTable, {
propsData: {
...defaultProps,
...props,
},
provide: {
...defaultProvide,
...provide,
},
stubs: {
PipelineOperations: true,
...stubs,
},
});
};
const findGlTableLite = () => wrapper.findComponent(GlTableLite);
@ -84,13 +79,9 @@ describe('Pipelines Table', () => {
const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button');
const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button');
beforeEach(() => {
pipeline = createMockPipeline();
});
describe('Pipelines Table', () => {
beforeEach(() => {
createComponent({ pipelines: [pipeline], viewType: 'root' });
createComponent({ props: { viewType: 'root' } });
});
it('displays table', () => {
@ -105,7 +96,7 @@ describe('Pipelines Table', () => {
});
it('should display a table row', () => {
expect(findTableRows()).toHaveLength(1);
expect(findTableRows()).toHaveLength(pipelines.length);
});
describe('status cell', () => {
@ -120,7 +111,7 @@ describe('Pipelines Table', () => {
});
it('should display the pipeline id', () => {
expect(findPipelineInfo().text()).toContain(`#${pipeline.id}`);
expect(findPipelineInfo().text()).toContain(`#${firstPipeline.id}`);
});
});
@ -130,24 +121,33 @@ describe('Pipelines Table', () => {
});
it('should render the right number of stages', () => {
const stagesLength = pipeline.details.stages.length;
expect(findLegacyPipelineMiniGraph().props('stages').length).toBe(stagesLength);
const stagesLength = firstPipeline.details.stages.length;
expect(findLegacyPipelineMiniGraph().props('stages')).toHaveLength(stagesLength);
});
it('should render the latest downstream pipelines only', () => {
// component receives two downstream pipelines. one of them is already outdated
// because we retried the trigger job, so the mini pipeline graph will only
// render the newly created downstream pipeline instead
expect(pipeline.triggered).toHaveLength(2);
expect(firstPipeline.triggered).toHaveLength(2);
expect(findLegacyPipelineMiniGraph().props('downstreamPipelines')).toHaveLength(1);
});
describe('when pipeline does not have stages', () => {
beforeEach(() => {
pipeline = createMockPipeline();
pipeline.details.stages = [];
createComponent({ pipelines: [pipeline] });
createComponent({
props: {
pipelines: [
{
...firstPipeline,
details: {
...firstPipeline.details,
stages: [],
},
},
],
},
});
});
it('stages are not rendered', () => {
@ -163,6 +163,10 @@ describe('Pipelines Table', () => {
});
describe('operations cell', () => {
beforeEach(() => {
createComponent({ stubs: { PipelineOperations } });
});
it('should render pipeline operations', () => {
expect(findActions().exists()).toBe(true);
});
@ -186,11 +190,11 @@ describe('Pipelines Table', () => {
describe('row', () => {
describe('when the FF is disabled', () => {
beforeEach(() => {
createComponent({ pipelines: [pipeline] });
createComponent();
});
it('does not render', () => {
expect(findTableRows()).toHaveLength(1);
expect(findTableRows()).toHaveLength(pipelines.length);
expect(findPipelineFailureWidget().exists()).toBe(false);
});
});
@ -198,20 +202,21 @@ describe('Pipelines Table', () => {
describe('when the FF is enabled', () => {
describe('and `withFailedJobsDetails` value is provided', () => {
beforeEach(() => {
createComponent({ pipelines: [pipeline] }, provideWithDetails);
createComponent({ provide: provideWithDetails });
});
it('renders', () => {
expect(findTableRows()).toHaveLength(2);
// We have 2 rows per pipeline with the widget
expect(findTableRows()).toHaveLength(pipelines.length * 2);
expect(findPipelineFailureWidget().exists()).toBe(true);
});
it('passes the expected props', () => {
expect(findPipelineFailureWidget().props()).toStrictEqual({
failedJobsCount: pipeline.failed_builds.length,
isPipelineActive: pipeline.active,
pipelineIid: pipeline.iid,
pipelinePath: pipeline.path,
failedJobsCount: firstPipeline.failed_builds.length,
isPipelineActive: firstPipeline.active,
pipelineIid: firstPipeline.iid,
pipelinePath: firstPipeline.path,
// Make sure the forward slash was removed
projectPath: 'frontend-fixtures/pipelines-project',
});
@ -220,14 +225,13 @@ describe('Pipelines Table', () => {
describe('and `withFailedJobsDetails` value is not provided', () => {
beforeEach(() => {
createComponent(
{ pipelines: [pipeline] },
{ glFeatures: { ciJobFailuresInMr: true } },
);
createComponent({
provide: { glFeatures: { ciJobFailuresInMr: true } },
});
});
it('does not render', () => {
expect(findTableRows()).toHaveLength(1);
expect(findTableRows()).toHaveLength(pipelines.length);
expect(findPipelineFailureWidget().exists()).toBe(false);
});
});
@ -235,6 +239,42 @@ describe('Pipelines Table', () => {
});
});
describe('events', () => {
beforeEach(() => {
createComponent();
});
describe('when confirming to cancel a pipeline', () => {
beforeEach(async () => {
await findActions().vm.$emit('cancel-pipeline', firstPipeline);
});
it('emits the `cancel-pipeline` event', () => {
expect(wrapper.emitted('cancel-pipeline')).toEqual([[firstPipeline]]);
});
});
describe('when retrying a pipeline', () => {
beforeEach(() => {
findActions().vm.$emit('retry-pipeline', firstPipeline);
});
it('emits the `retry-pipeline` event', () => {
expect(wrapper.emitted('retry-pipeline')).toEqual([[firstPipeline]]);
});
});
describe('when refreshing pipelines', () => {
beforeEach(() => {
findActions().vm.$emit('refresh-pipelines-table');
});
it('emits the `refresh-pipelines-table` event', () => {
expect(wrapper.emitted('refresh-pipelines-table')).toEqual([[]]);
});
});
});
describe('tracking', () => {
beforeEach(() => {
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
@ -252,22 +292,6 @@ describe('Pipelines Table', () => {
});
});
it('tracks retry pipeline button click', () => {
findRetryBtn().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', {
label: TRACKING_CATEGORIES.table,
});
});
it('tracks cancel pipeline button click', () => {
findCancelBtn().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', {
label: TRACKING_CATEGORIES.table,
});
});
it('tracks pipeline mini graph stage click', () => {
findLegacyPipelineMiniGraph().vm.$emit('miniGraphStageClick');

View File

@ -1,10 +1,13 @@
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import PipelinesManualActions from '~/ci/pipelines_page/components/pipelines_manual_actions.vue';
import PipelineMultiActions from '~/ci/pipelines_page/components/pipeline_multi_actions.vue';
import PipelineOperations from '~/ci/pipelines_page/components/pipeline_operations.vue';
import eventHub from '~/ci/event_hub';
import PipelineStopModal from '~/ci/pipelines_page/components/pipeline_stop_modal.vue';
import { TRACKING_CATEGORIES } from '~/ci/constants';
describe('Pipeline operations', () => {
let trackingSpy;
let wrapper;
const defaultProps = {
@ -36,6 +39,7 @@ describe('Pipeline operations', () => {
const findMultiActions = () => wrapper.findComponent(PipelineMultiActions);
const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button');
const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button');
const findPipelineStopModal = () => wrapper.findComponent(PipelineStopModal);
it('should display pipeline manual actions', () => {
createComponent();
@ -49,28 +53,71 @@ describe('Pipeline operations', () => {
expect(findMultiActions().exists()).toBe(true);
});
it('does not show the confirmation modal', () => {
createComponent();
expect(findPipelineStopModal().props().showConfirmationModal).toBe(false);
});
describe('when cancelling a pipeline', () => {
beforeEach(async () => {
createComponent();
await findCancelBtn().vm.$emit('click');
});
it('should show a confirmation modal', () => {
expect(findPipelineStopModal().props().showConfirmationModal).toBe(true);
});
it('should emit cancel-pipeline event when confirming', async () => {
await findPipelineStopModal().vm.$emit('submit');
expect(wrapper.emitted('cancel-pipeline')).toEqual([[defaultProps.pipeline]]);
expect(findPipelineStopModal().props().showConfirmationModal).toBe(false);
});
it('should hide the modal when closing', async () => {
await findPipelineStopModal().vm.$emit('close-modal');
expect(findPipelineStopModal().props().showConfirmationModal).toBe(false);
});
});
describe('events', () => {
beforeEach(() => {
createComponent();
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
});
it('should emit retryPipeline event', () => {
findRetryBtn().vm.$emit('click');
expect(eventHub.$emit).toHaveBeenCalledWith(
'retryPipeline',
defaultProps.pipeline.retry_path,
);
expect(wrapper.emitted('retry-pipeline')).toEqual([[defaultProps.pipeline]]);
});
});
describe('tracking', () => {
beforeEach(() => {
createComponent();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
it('should emit openConfirmationModal event', () => {
afterEach(() => {
unmockTracking();
});
it('tracks retry pipeline button click', () => {
findRetryBtn().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', {
label: TRACKING_CATEGORIES.table,
});
});
it('tracks cancel pipeline button click', () => {
findCancelBtn().vm.$emit('click');
expect(eventHub.$emit).toHaveBeenCalledWith('openConfirmationModal', {
pipeline: defaultProps.pipeline,
endpoint: defaultProps.pipeline.cancel_path,
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', {
label: TRACKING_CATEGORIES.table,
});
});
});

View File

@ -1,15 +1,17 @@
import { shallowMount } from '@vue/test-utils';
import { GlSprintf } from '@gitlab/ui';
import { GlModal, GlSprintf } from '@gitlab/ui';
import { mockPipelineHeader } from 'jest/ci/pipeline_details/mock_data';
import PipelineStopModal from '~/ci/pipelines_page/components/pipeline_stop_modal.vue';
describe('PipelineStopModal', () => {
let wrapper;
const createComponent = () => {
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(PipelineStopModal, {
propsData: {
pipeline: mockPipelineHeader,
showConfirmationModal: false,
...props,
},
stubs: {
GlSprintf,
@ -17,11 +19,43 @@ describe('PipelineStopModal', () => {
});
};
const findModal = () => wrapper.findComponent(GlModal);
beforeEach(() => {
createComponent();
});
it('should render "stop pipeline" warning', () => {
expect(wrapper.text()).toMatch(`Youre about to stop pipeline #${mockPipelineHeader.id}.`);
describe('when `showConfirmationModal` is false', () => {
it('passes the visiblity value to the modal', () => {
expect(findModal().props().visible).toBe(false);
});
});
describe('when `showConfirmationModal` is true', () => {
beforeEach(() => {
createComponent({ props: { showConfirmationModal: true } });
});
it('passes the visiblity value to the modal', () => {
expect(findModal().props().visible).toBe(true);
});
it('renders "stop pipeline" warning', () => {
expect(wrapper.text()).toMatch(`You're about to stop pipeline #${mockPipelineHeader.id}.`);
});
});
describe('events', () => {
beforeEach(() => {
createComponent({ props: { showConfirmationModal: true } });
});
it('emits the close-modal event when the visiblity changes', async () => {
expect(wrapper.emitted('close-modal')).toBeUndefined();
await findModal().vm.$emit('change', false);
expect(wrapper.emitted('close-modal')).toEqual([[]]);
});
});
});

View File

@ -1,13 +1,13 @@
import { GlLoadingIcon, GlModal, GlTableLite } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import fixture from 'test_fixtures/pipelines/pipelines.json';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
import LegacyPipelinesTableWraper from '~/commit/pipelines/legacy_pipelines_table_wrapper.vue';
import LegacyPipelinesTableWrapper from '~/commit/pipelines/legacy_pipelines_table_wrapper.vue';
import PipelinesTable from '~/ci/common/pipelines_table.vue';
import {
HTTP_STATUS_BAD_REQUEST,
HTTP_STATUS_INTERNAL_SERVER_ERROR,
@ -39,27 +39,26 @@ describe('Pipelines table in Commits and Merge requests', () => {
const findTableRows = () => wrapper.findAllByTestId('pipeline-table-row');
const findModal = () => wrapper.findComponent(GlModal);
const findMrPipelinesDocsLink = () => wrapper.findByTestId('mr-pipelines-docs-link');
const findPipelinesTable = () => wrapper.findComponent(PipelinesTable);
const createComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper(
mount(LegacyPipelinesTableWraper, {
propsData: {
endpoint: 'endpoint.json',
emptyStateSvgPath: 'foo',
errorStateSvgPath: 'foo',
...props,
},
mocks: {
$toast,
},
stubs: {
GlModal: stubComponent(GlModal, {
template: '<div />',
methods: { show: showMock },
}),
},
}),
);
const createComponent = ({ props = {}, mountFn = mountExtended } = {}) => {
wrapper = mountFn(LegacyPipelinesTableWrapper, {
propsData: {
endpoint: 'endpoint.json',
emptyStateSvgPath: 'foo',
errorStateSvgPath: 'foo',
...props,
},
mocks: {
$toast,
},
stubs: {
GlModal: stubComponent(GlModal, {
template: '<div />',
methods: { show: showMock },
}),
},
});
};
beforeEach(() => {
@ -116,7 +115,6 @@ describe('Pipelines table in Commits and Merge requests', () => {
it('should make an API request when using pagination', async () => {
expect(mock.history.get).toHaveLength(1);
expect(mock.history.get[0].params.page).toBe('1');
wrapper.find('.next-page-item').trigger('click');
@ -359,4 +357,53 @@ describe('Pipelines table in Commits and Merge requests', () => {
);
});
});
describe('events', () => {
beforeEach(async () => {
mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipeline]);
createComponent({ mountFn: shallowMountExtended });
await waitForPromises();
});
describe('When cancelling a pipeline', () => {
it('sends the cancel action', async () => {
expect(mock.history.post).toHaveLength(0);
findPipelinesTable().vm.$emit('cancel-pipeline', pipeline);
await waitForPromises();
expect(mock.history.post).toHaveLength(1);
expect(mock.history.post[0].url).toContain('cancel.json');
});
});
describe('When retrying a pipeline', () => {
it('sends the retry action', async () => {
expect(mock.history.post).toHaveLength(0);
findPipelinesTable().vm.$emit('retry-pipeline', pipeline);
await waitForPromises();
expect(mock.history.post).toHaveLength(1);
expect(mock.history.post[0].url).toContain('retry.json');
});
});
describe('When refreshing a pipeline', () => {
it('calls the pipelines endpoint again', async () => {
expect(mock.history.get).toHaveLength(1);
findPipelinesTable().vm.$emit('refresh-pipelines-table');
await waitForPromises();
expect(mock.history.get).toHaveLength(2);
expect(mock.history.get[1].url).toContain('endpoint.json');
});
});
});
});