Add latest changes from gitlab-org/gitlab@master
|
@ -23,6 +23,3 @@ export const receiveStatisticsError = ({ commit }, error) => {
|
|||
commit(types.RECEIVE_STATISTICS_ERROR, error);
|
||||
createFlash(s__('AdminDashboard|Error loading the statistics. Please try again'));
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* and returns an array of the following form:
|
||||
* [{ key: "forks", label: "Forks", value: 50 }]
|
||||
*/
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const getStatistics = state => labels =>
|
||||
Object.keys(labels).map(key => {
|
||||
const result = {
|
||||
|
@ -12,6 +13,3 @@ export const getStatistics = state => labels =>
|
|||
};
|
||||
return result;
|
||||
});
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -146,6 +146,3 @@ export const expandAllDiscussions = ({ dispatch, state }) =>
|
|||
export const toggleResolveDiscussion = ({ commit }, draftId) => {
|
||||
commit(types.TOGGLE_RESOLVE_DISCUSSION, draftId);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -82,6 +82,3 @@ export const isPublishingDraft = state => draftId =>
|
|||
state.currentlyPublishingDrafts.indexOf(draftId) !== -1;
|
||||
|
||||
export const sortedDrafts = state => [...state.drafts].sort((a, b) => a.id > b.id);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -76,6 +76,3 @@ export const fetchClusters = ({ state, commit, dispatch }) => {
|
|||
export const setPage = ({ commit }, page) => {
|
||||
commit(types.SET_PAGE, page);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -3,6 +3,7 @@ import { __ } from '~/locale';
|
|||
import service from '../services/contributors_service';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const fetchChartData = ({ commit }, endpoint) => {
|
||||
commit(types.SET_LOADING_STATE, true);
|
||||
|
||||
|
@ -15,6 +16,3 @@ export const fetchChartData = ({ commit }, endpoint) => {
|
|||
})
|
||||
.catch(() => flash(__('An error occurred while loading chart data')));
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -28,6 +28,3 @@ export const parsedData = state => {
|
|||
byAuthorEmail,
|
||||
};
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -90,6 +90,3 @@ export const fetchMachineTypes = ({ commit, state }) =>
|
|||
mutation: types.SET_MACHINE_TYPES,
|
||||
payloadKey: 'items',
|
||||
});
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -246,28 +246,28 @@ export default {
|
|||
this.onUploadDesign([newFile]);
|
||||
}
|
||||
},
|
||||
toggleOnPasteListener(route) {
|
||||
if (route === DESIGNS_ROUTE_NAME) {
|
||||
document.addEventListener('paste', this.onDesignPaste);
|
||||
} else {
|
||||
document.removeEventListener('paste', this.onDesignPaste);
|
||||
}
|
||||
toggleOnPasteListener() {
|
||||
document.addEventListener('paste', this.onDesignPaste);
|
||||
},
|
||||
toggleOffPasteListener() {
|
||||
document.removeEventListener('paste', this.onDesignPaste);
|
||||
},
|
||||
},
|
||||
beforeRouteUpdate(to, from, next) {
|
||||
this.toggleOnPasteListener(to.name);
|
||||
this.selectedDesigns = [];
|
||||
next();
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
this.toggleOnPasteListener(to.name);
|
||||
next();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-testid="designs-root" class="gl-mt-5">
|
||||
<div
|
||||
data-testid="designs-root"
|
||||
class="gl-mt-5"
|
||||
:class="{ 'designs-root': !isDesignListEmpty }"
|
||||
@mouseenter="toggleOnPasteListener"
|
||||
@mouseleave="toggleOffPasteListener"
|
||||
>
|
||||
<header v-if="showToolbar" class="row-content-block border-top-0 p-2 d-flex">
|
||||
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-w-full">
|
||||
<div>
|
||||
|
|
|
@ -759,6 +759,3 @@ export const navigateToDiffFileIndex = ({ commit, state }, index) => {
|
|||
|
||||
commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -74,7 +74,6 @@ export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) =
|
|||
discussion => discussion.diff_discussion && discussion.diff_file.file_hash === diff.file_hash,
|
||||
) || [];
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
|
||||
export const getDiffFileByHash = state => fileHash =>
|
||||
state.diffFiles.find(file => file.file_hash === fileHash);
|
||||
|
||||
|
@ -130,6 +129,3 @@ export const fileLineCoverage = state => (file, line) => {
|
|||
*/
|
||||
export const currentDiffIndex = state =>
|
||||
Math.max(0, state.diffFiles.findIndex(diff => diff.file_hash === state.currentDiffFileId));
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -34,5 +34,3 @@ export const updateIgnoreStatus = ({ commit, dispatch }, params) => {
|
|||
commit(types.SET_UPDATING_IGNORE_STATUS, false);
|
||||
});
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -10,6 +10,7 @@ const stopPolling = poll => {
|
|||
if (poll) poll.stop();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function startPollingStacktrace({ commit }, endpoint) {
|
||||
stackTracePoll = new Poll({
|
||||
resource: service,
|
||||
|
@ -32,5 +33,3 @@ export function startPollingStacktrace({ commit }, endpoint) {
|
|||
|
||||
stackTracePoll.makeRequest();
|
||||
}
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const stacktrace = state =>
|
||||
state.stacktraceData.stack_trace_entries
|
||||
? state.stacktraceData.stack_trace_entries.reverse()
|
||||
: [];
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -102,5 +102,3 @@ export const fetchPaginatedResults = ({ commit, dispatch }, cursor) => {
|
|||
export const removeIgnoredResolvedErrors = ({ commit }, error) => {
|
||||
commit(types.REMOVE_IGNORED_RESOLVED_ERRORS, error);
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -89,6 +89,3 @@ export const updateSelectedProject = ({ commit }, selectedProject) => {
|
|||
export const setInitialState = ({ commit }, data) => {
|
||||
commit(types.SET_INITIAL_STATE, data);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -39,6 +39,3 @@ export const projectSelectionLabel = state => {
|
|||
}
|
||||
return s__('ErrorTracking|To enable project selection, enter a valid Auth Token');
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -14,5 +14,3 @@ export const transformFrontendSettings = ({ apiHost, enabled, token, selectedPro
|
|||
};
|
||||
|
||||
export const getDisplayName = project => `${project.organizationName} | ${project.slug}`;
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -76,6 +76,3 @@ export const setSearchQuery = ({ commit, dispatch }, query) => {
|
|||
dispatch('fetchFrequentItems');
|
||||
}
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const hasSearchQuery = state => state.searchQuery !== '';
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -49,7 +49,7 @@ const multiMetricLabel = metricAttributes => {
|
|||
* @param {Object} metricAttributes - Default metric attribute values (e.g. method, instance)
|
||||
* @returns {String} The formatted query label
|
||||
*/
|
||||
const getSeriesLabel = (queryLabel, metricAttributes) => {
|
||||
export const getSeriesLabel = (queryLabel, metricAttributes) => {
|
||||
return (
|
||||
singleAttributeLabel(queryLabel, metricAttributes) ||
|
||||
templatedLabel(queryLabel, metricAttributes) ||
|
||||
|
@ -63,7 +63,6 @@ const getSeriesLabel = (queryLabel, metricAttributes) => {
|
|||
* @param {Object} defaultConfig - Default chart config values (e.g. lineStyle, name)
|
||||
* @returns {Array} The formatted values
|
||||
*/
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const makeDataSeries = (queryResults, defaultConfig) =>
|
||||
queryResults.map(result => {
|
||||
return {
|
||||
|
|
|
@ -32,5 +32,3 @@ export const fetchBranches = ({ dispatch, rootGetters }, { search = '' }) => {
|
|||
};
|
||||
|
||||
export const resetBranches = ({ commit }) => commit(types.RESET_BRANCHES);
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -23,5 +23,3 @@ export const templateTypes = () => [
|
|||
export const showFileTemplatesBar = (_, getters, rootState) => name =>
|
||||
getters.templateTypes.find(t => t.name === name) &&
|
||||
rootState.currentActivityView === leftSidebarViews.edit.name;
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -41,5 +41,3 @@ export const fetchMergeRequests = (
|
|||
};
|
||||
|
||||
export const resetMergeRequests = ({ commit }) => commit(types.RESET_MERGE_REQUESTS);
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -149,5 +149,3 @@ export const resetLatestPipeline = ({ commit }) => {
|
|||
commit(types.RECEIVE_LASTEST_PIPELINE_SUCCESS, null);
|
||||
commit(types.SET_DETAIL_JOB, null);
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -20,5 +20,3 @@ export const failedJobsCount = state =>
|
|||
);
|
||||
|
||||
export const jobsCount = state => state.stages.reduce((acc, stage) => acc + stage.jobs.length, 0);
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -2,4 +2,3 @@ export * from './setup';
|
|||
export * from './checks';
|
||||
export * from './session_controls';
|
||||
export * from './session_status';
|
||||
export default () => {};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const allCheck = state => {
|
||||
const checks = Object.values(state.checks);
|
||||
|
||||
|
@ -15,5 +16,3 @@ export const allCheck = state => {
|
|||
message,
|
||||
};
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -128,6 +128,3 @@ export const fetchJobs = ({ state, commit, dispatch }) => {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -85,9 +85,6 @@ export default {
|
|||
dueDateWords() {
|
||||
return this.dueDate ? dateInWords(this.dueDate, true) : undefined;
|
||||
},
|
||||
hasNoComments() {
|
||||
return !this.userNotesCount;
|
||||
},
|
||||
isOverdue() {
|
||||
return this.dueDate ? this.dueDate < new Date() : false;
|
||||
},
|
||||
|
@ -148,32 +145,51 @@ export default {
|
|||
time_ago: escape(getTimeago().format(this.issuable.updated_at)),
|
||||
});
|
||||
},
|
||||
userNotesCount() {
|
||||
return this.issuable.user_notes_count;
|
||||
},
|
||||
issuableMeta() {
|
||||
return [
|
||||
{
|
||||
key: 'merge-requests',
|
||||
visible: this.issuable.merge_requests_count > 0,
|
||||
value: this.issuable.merge_requests_count,
|
||||
title: __('Related merge requests'),
|
||||
class: 'js-merge-requests',
|
||||
dataTestId: 'merge-requests',
|
||||
icon: 'merge-request',
|
||||
},
|
||||
{
|
||||
key: 'upvotes',
|
||||
visible: this.issuable.upvotes > 0,
|
||||
value: this.issuable.upvotes,
|
||||
title: __('Upvotes'),
|
||||
class: 'js-upvotes',
|
||||
dataTestId: 'upvotes',
|
||||
icon: 'thumb-up',
|
||||
},
|
||||
{
|
||||
key: 'downvotes',
|
||||
visible: this.issuable.downvotes > 0,
|
||||
value: this.issuable.downvotes,
|
||||
title: __('Downvotes'),
|
||||
class: 'js-downvotes',
|
||||
dataTestId: 'downvotes',
|
||||
icon: 'thumb-down',
|
||||
},
|
||||
{
|
||||
key: 'blocking-issues',
|
||||
visible: this.issuable.blocking_issues_count > 0,
|
||||
value: this.issuable.blocking_issues_count,
|
||||
title: __('Blocking issues'),
|
||||
dataTestId: 'blocking-issues',
|
||||
href: `${this.issuable.web_url}#related-issues`,
|
||||
icon: 'issue-block',
|
||||
},
|
||||
{
|
||||
key: 'comments-count',
|
||||
visible: !this.isJiraIssue,
|
||||
value: this.issuable.user_notes_count,
|
||||
title: __('Comments'),
|
||||
dataTestId: 'notes-count',
|
||||
href: `${this.issuable.web_url}#notes`,
|
||||
class: !this.issuable.user_notes_count ? 'no-comments' : '',
|
||||
icon: 'comments',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
|
@ -202,6 +218,9 @@ export default {
|
|||
selected: ev.target.checked,
|
||||
});
|
||||
},
|
||||
issuableMetaComponent(href) {
|
||||
return href ? 'gl-link' : 'span';
|
||||
},
|
||||
},
|
||||
|
||||
confidentialTooltipText: __('Confidential'),
|
||||
|
@ -216,9 +235,9 @@ export default {
|
|||
:data-labels="labelIdsString"
|
||||
:data-url="issuable.web_url"
|
||||
>
|
||||
<div class="d-flex">
|
||||
<div class="gl-display-flex">
|
||||
<!-- Bulk edit checkbox -->
|
||||
<div v-if="isBulkEditing" class="mr-2">
|
||||
<div v-if="isBulkEditing" class="gl-mr-3">
|
||||
<input
|
||||
:checked="selected"
|
||||
class="selected-issuable"
|
||||
|
@ -230,7 +249,7 @@ export default {
|
|||
|
||||
<!-- Issuable info container -->
|
||||
<!-- Issuable main info -->
|
||||
<div class="flex-grow-1">
|
||||
<div class="gl-flex-grow-1">
|
||||
<div class="title">
|
||||
<span class="issue-title-text">
|
||||
<gl-icon
|
||||
|
@ -251,7 +270,10 @@ export default {
|
|||
/>
|
||||
</gl-link>
|
||||
</span>
|
||||
<span v-if="issuable.has_tasks" class="ml-1 task-status d-none d-sm-inline-block">
|
||||
<span
|
||||
v-if="issuable.has_tasks"
|
||||
class="gl-ml-2 task-status gl-display-none d-sm-inline-block"
|
||||
>
|
||||
{{ issuable.task_status }}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -267,7 +289,7 @@ export default {
|
|||
{{ referencePath }}
|
||||
</span>
|
||||
|
||||
<span data-testid="openedByMessage" class="d-none d-sm-inline-block mr-1">
|
||||
<span data-testid="openedByMessage" class="gl-display-none d-sm-inline-block gl-mr-2">
|
||||
·
|
||||
<gl-sprintf
|
||||
:message="isJiraIssue ? $options.i18n.openedAgoJira : $options.i18n.openedAgo"
|
||||
|
@ -291,7 +313,7 @@ export default {
|
|||
<gl-link
|
||||
v-if="issuable.milestone"
|
||||
v-gl-tooltip
|
||||
class="d-none d-sm-inline-block mr-1 js-milestone"
|
||||
class="gl-display-none d-sm-inline-block gl-mr-2 js-milestone"
|
||||
:href="milestoneLink"
|
||||
:title="milestoneTooltipText"
|
||||
>
|
||||
|
@ -302,7 +324,7 @@ export default {
|
|||
<span
|
||||
v-if="dueDate"
|
||||
v-gl-tooltip
|
||||
class="d-none d-sm-inline-block mr-1 js-due-date"
|
||||
class="gl-display-none d-sm-inline-block gl-mr-2 js-due-date"
|
||||
:class="{ cred: isOverdue }"
|
||||
:title="__('Due date')"
|
||||
>
|
||||
|
@ -321,7 +343,7 @@ export default {
|
|||
:title="label.name"
|
||||
:scoped="isScoped(label)"
|
||||
size="sm"
|
||||
class="mr-1"
|
||||
class="gl-mr-2"
|
||||
>{{ label.name }}</gl-label
|
||||
>
|
||||
|
||||
|
@ -329,7 +351,7 @@ export default {
|
|||
v-if="hasWeight"
|
||||
v-gl-tooltip
|
||||
:title="__('Weight')"
|
||||
class="d-none d-sm-inline-block js-weight"
|
||||
class="gl-display-none d-sm-inline-block"
|
||||
data-testid="weight"
|
||||
>
|
||||
<gl-icon name="weight" class="align-text-bottom" />
|
||||
|
@ -339,43 +361,37 @@ export default {
|
|||
</div>
|
||||
|
||||
<!-- Issuable meta -->
|
||||
<div class="flex-shrink-0 d-flex flex-column align-items-end justify-content-center">
|
||||
<div class="controls d-flex">
|
||||
<div
|
||||
class="gl-flex-shrink-0 gl-display-flex gl-flex-direction-column align-items-end gl-justify-content-center"
|
||||
>
|
||||
<div class="controls gl-display-flex">
|
||||
<span v-if="isJiraIssue" data-testid="issuable-status">{{ issuable.status }}</span>
|
||||
<span v-else-if="isClosed" class="issuable-status">{{ __('CLOSED') }}</span>
|
||||
|
||||
<issue-assignees
|
||||
:assignees="issuable.assignees"
|
||||
class="align-items-center d-flex ml-2"
|
||||
class="gl-align-items-center gl-display-flex gl-ml-3"
|
||||
:icon-size="16"
|
||||
img-css-classes="mr-1"
|
||||
img-css-classes="gl-mr-2!"
|
||||
:max-visible="4"
|
||||
/>
|
||||
|
||||
<template v-for="meta in issuableMeta">
|
||||
<span
|
||||
v-if="meta.value"
|
||||
v-if="meta.visible"
|
||||
:key="meta.key"
|
||||
v-gl-tooltip
|
||||
:class="['d-none d-sm-inline-block ml-2 vertical-align-middle', meta.class]"
|
||||
class="gl-display-none gl-display-sm-flex gl-align-items-center gl-ml-3"
|
||||
:class="meta.class"
|
||||
:data-testid="meta.dataTestId"
|
||||
:title="meta.title"
|
||||
>
|
||||
<gl-icon v-if="meta.icon" :name="meta.icon" />
|
||||
{{ meta.value }}
|
||||
<component :is="issuableMetaComponent(meta.href)" :href="meta.href">
|
||||
<gl-icon v-if="meta.icon" :name="meta.icon" />
|
||||
{{ meta.value }}
|
||||
</component>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<gl-link
|
||||
v-if="!isJiraIssue"
|
||||
v-gl-tooltip
|
||||
class="ml-2 js-notes"
|
||||
:href="`${issuable.web_url}#notes`"
|
||||
:title="__('Comments')"
|
||||
:class="{ 'no-comments': hasNoComments }"
|
||||
>
|
||||
<gl-icon name="comments" class="gl-vertical-align-text-bottom" />
|
||||
{{ userNotesCount }}
|
||||
</gl-link>
|
||||
</div>
|
||||
<div v-gl-tooltip class="issuable-updated-at" :title="updatedDateString">
|
||||
{{ updatedDateAgo }}
|
||||
|
|
|
@ -247,6 +247,3 @@ export const triggerManualJob = ({ state }, variables) => {
|
|||
})
|
||||
.catch(() => flash(__('An error occurred while triggering the job.')));
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -46,6 +46,3 @@ export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceCo
|
|||
|
||||
export const hasRunnersForProject = state =>
|
||||
state.job.runners.available && !state.job.runners.online;
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -200,6 +200,3 @@ export const dismissRequestLogsError = ({ commit }) => {
|
|||
export const dismissInvalidTimeRangeWarning = ({ commit }) => {
|
||||
commit(types.HIDE_TIME_RANGE_INVALID_WARNING);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -36,7 +36,7 @@ export default {
|
|||
);
|
||||
},
|
||||
xAxisName() {
|
||||
return this.graphData.x_label || '';
|
||||
return this.graphData.xLabel || '';
|
||||
},
|
||||
yAxisName() {
|
||||
return this.graphData.y_label || '';
|
||||
|
|
|
@ -30,6 +30,7 @@ import MonitorStackedColumnChart from './charts/stacked_column.vue';
|
|||
import TrackEventDirective from '~/vue_shared/directives/track_event';
|
||||
import AlertWidget from './alert_widget.vue';
|
||||
import { timeRangeToUrl, downloadCSVOptions, generateLinkToChartOptions } from '../utils';
|
||||
import { graphDataToCsv } from '../csv_export';
|
||||
|
||||
const events = {
|
||||
timeRangeZoom: 'timerangezoom',
|
||||
|
@ -148,13 +149,10 @@ export default {
|
|||
return null;
|
||||
},
|
||||
csvText() {
|
||||
const chartData = this.graphData?.metrics[0].result[0].values || [];
|
||||
const yLabel = this.graphData.y_label;
|
||||
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/require-i18n-strings
|
||||
return chartData.reduce((csv, data) => {
|
||||
const row = data.join(',');
|
||||
return `${csv}${row}\r\n`;
|
||||
}, header);
|
||||
if (this.graphData) {
|
||||
return graphDataToCsv(this.graphData);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
downloadCsv() {
|
||||
const data = new Blob([this.csvText], { type: 'text/plain' });
|
||||
|
|
147
app/assets/javascripts/monitoring/csv_export.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
import { getSeriesLabel } from '~/helpers/monitor_helper';
|
||||
|
||||
/**
|
||||
* Returns a label for a header of the csv.
|
||||
*
|
||||
* Includes double quotes ("") in case the header includes commas or other separator.
|
||||
*
|
||||
* @param {String} axisLabel
|
||||
* @param {String} metricLabel
|
||||
* @param {Object} metricAttributes
|
||||
*/
|
||||
const csvHeader = (axisLabel, metricLabel, metricAttributes = {}) =>
|
||||
`${axisLabel} > ${getSeriesLabel(metricLabel, metricAttributes)}`;
|
||||
|
||||
/**
|
||||
* Returns an array with the header labels given a list of metrics
|
||||
*
|
||||
* ```
|
||||
* metrics = [
|
||||
* {
|
||||
* label: "..." // user-defined label
|
||||
* result: [
|
||||
* {
|
||||
* metric: { ... } // metricAttributes
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* When metrics have a `label` or `metricAttributes`, they are
|
||||
* used to generate the column name.
|
||||
*
|
||||
* @param {String} axisLabel - Main label
|
||||
* @param {Array} metrics - Metrics with results
|
||||
*/
|
||||
const csvMetricHeaders = (axisLabel, metrics) =>
|
||||
metrics.flatMap(({ label, result }) =>
|
||||
// The `metric` in a `result` is a map of `metricAttributes`
|
||||
// contains key-values to identify the series, rename it
|
||||
// here for clarity.
|
||||
result.map(({ metric: metricAttributes }) => {
|
||||
return csvHeader(axisLabel, label, metricAttributes);
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns a (flat) array with all the values arrays in each
|
||||
* metric and series
|
||||
*
|
||||
* ```
|
||||
* metrics = [
|
||||
* {
|
||||
* result: [
|
||||
* {
|
||||
* values: [ ... ] // `values`
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param {Array} metrics - Metrics with results
|
||||
*/
|
||||
const csvMetricValues = metrics =>
|
||||
metrics.flatMap(({ result }) => result.map(res => res.values || []));
|
||||
|
||||
/**
|
||||
* Returns headers and rows for csv, sorted by their timestamp.
|
||||
*
|
||||
* {
|
||||
* headers: ["timestamp", "<col_1_name>", "col_2_name"],
|
||||
* rows: [
|
||||
* [ <timestamp>, <col_1_value>, <col_2_value> ],
|
||||
* [ <timestamp>, <col_1_value>, <col_2_value> ]
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @param {Array} metricHeaders
|
||||
* @param {Array} metricValues
|
||||
*/
|
||||
const csvData = (metricHeaders, metricValues) => {
|
||||
const rowsByTimestamp = {};
|
||||
|
||||
metricValues.forEach((values, colIndex) => {
|
||||
values.forEach(([timestamp, value]) => {
|
||||
if (!rowsByTimestamp[timestamp]) {
|
||||
rowsByTimestamp[timestamp] = [];
|
||||
}
|
||||
// `value` should be in the right column
|
||||
rowsByTimestamp[timestamp][colIndex] = value;
|
||||
});
|
||||
});
|
||||
|
||||
const rows = Object.keys(rowsByTimestamp)
|
||||
.sort()
|
||||
.map(timestamp => {
|
||||
// force each row to have the same number of entries
|
||||
rowsByTimestamp[timestamp].length = metricHeaders.length;
|
||||
// add timestamp as the first entry
|
||||
return [timestamp, ...rowsByTimestamp[timestamp]];
|
||||
});
|
||||
|
||||
// Escape double quotes and enclose headers:
|
||||
// "If double-quotes are used to enclose fields, then a double-quote
|
||||
// appearing inside a field must be escaped by preceding it with
|
||||
// another double quote."
|
||||
// https://tools.ietf.org/html/rfc4180#page-2
|
||||
const headers = metricHeaders.map(header => `"${header.replace(/"/g, '""')}"`);
|
||||
|
||||
return {
|
||||
headers: ['timestamp', ...headers],
|
||||
rows,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns dashboard panel's data in a string in CSV format
|
||||
*
|
||||
* @param {Object} graphData - Panel contents
|
||||
* @returns {String}
|
||||
*/
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const graphDataToCsv = graphData => {
|
||||
const delimiter = ',';
|
||||
const br = '\r\n';
|
||||
const { metrics = [], y_label: axisLabel } = graphData;
|
||||
|
||||
const metricsWithResults = metrics.filter(metric => metric.result);
|
||||
const metricHeaders = csvMetricHeaders(axisLabel, metricsWithResults);
|
||||
const metricValues = csvMetricValues(metricsWithResults);
|
||||
const { headers, rows } = csvData(metricHeaders, metricValues);
|
||||
|
||||
if (rows.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const headerLine = headers.join(delimiter) + br;
|
||||
const lines = rows.map(row => row.join(delimiter));
|
||||
|
||||
return headerLine + lines.join(br) + br;
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const addModule = ({ commit }, data) => commit(types.ADD_MODULE, data);
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const metricsWithData = (state, getters, rootState, rootGetters) =>
|
||||
state.modules.map(module => rootGetters[`${module}/metricsWithData`]().length);
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const ADD_MODULE = 'ADD_MODULE';
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -682,6 +682,3 @@ export const receiveDeleteDescriptionVersionError = ({ commit }, error) => {
|
|||
export const updateAssignees = ({ commit }, assignees) => {
|
||||
commit(types.UPDATE_ASSIGNEES, assignees);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -229,6 +229,3 @@ export const getDiscussion = state => discussionId =>
|
|||
state.discussions.find(discussion => discussion.id === discussionId);
|
||||
|
||||
export const commentsDisabled = state => state.commentsDisabled;
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -37,6 +37,3 @@ export const receiveSaveChangesError = (_, error) => {
|
|||
|
||||
createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -52,6 +52,3 @@ export const setSelectedSuiteIndex = ({ commit }, data) =>
|
|||
export const removeSelectedSuiteIndex = ({ commit }) =>
|
||||
commit(types.SET_SELECTED_SUITE_INDEX, null);
|
||||
export const toggleLoading = ({ commit }) => commit(types.TOGGLE_LOADING);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -16,6 +16,3 @@ export const getSuiteTests = state => {
|
|||
const { test_cases: testCases = [] } = getSelectedSuite(state);
|
||||
return testCases.sort(sortTestCases).map(addIconStatus);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { pickBy } from 'lodash';
|
||||
import { SUPPORTED_FILTER_PARAMETERS } from './constants';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const validateParams = params => {
|
||||
return pickBy(params, (val, key) => SUPPORTED_FILTER_PARAMETERS.includes(key) && val);
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -102,5 +102,3 @@ export const requestDeleteImage = ({ commit }, image) => {
|
|||
commit(types.SET_MAIN_LOADING, false);
|
||||
});
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -28,6 +28,3 @@ export const saveSettings = ({ dispatch, state }) => {
|
|||
)
|
||||
.finally(() => dispatch('toggleLoading'));
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -32,6 +32,3 @@ export const fetchMergeRequests = ({ state, dispatch }) => {
|
|||
createFlash(s__('Something went wrong while fetching related merge requests.'));
|
||||
});
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -43,6 +43,3 @@ export const receiveReleasesError = ({ commit }) => {
|
|||
commit(types.RECEIVE_RELEASES_ERROR);
|
||||
createFlash(__('An error occurred while fetching the releases. Please try again.'));
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -74,6 +74,3 @@ export const receiveReportError = ({ commit, dispatch }) => {
|
|||
commit(types.RECEIVE_REPORT_ERROR);
|
||||
dispatch('stopPolling');
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -43,6 +43,3 @@ export const shouldRenderIssuesList = state =>
|
|||
export const unresolvedIssues = state => state.report.existing_errors;
|
||||
export const resolvedIssues = state => state.report.resolved_errors;
|
||||
export const newIssues = state => state.report.new_errors;
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -85,6 +85,3 @@ export const openModal = ({ dispatch }, payload) => {
|
|||
};
|
||||
|
||||
export const setModalData = ({ commit }, payload) => commit(types.SET_ISSUE_MODAL_DATA, payload);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { LOADING, ERROR, SUCCESS, STATUS_FAILED } from '../constants';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const summaryStatus = state => {
|
||||
if (state.isLoading) {
|
||||
return LOADING;
|
||||
|
@ -11,6 +12,3 @@ export const summaryStatus = state => {
|
|||
|
||||
return SUCCESS;
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -123,6 +123,3 @@ export const fetchMetrics = ({ dispatch }, { metricsPath, hasPrometheus }) => {
|
|||
createFlash(error);
|
||||
});
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -5,6 +5,3 @@ export const hasPrometheusMissingData = state => state.hasPrometheus && !state.h
|
|||
// Convert the function list into a k/v grouping based on the environment scope
|
||||
|
||||
export const getFunctions = state => translate(state.functions);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -18,6 +18,3 @@ export const translate = functions =>
|
|||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -38,5 +38,3 @@ export const SnippetShowInit = () => {
|
|||
export const SnippetEditInit = () => {
|
||||
appFactory(document.getElementById('js-snippet-edit'), SnippetsEdit);
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -2,6 +2,7 @@ import GetSnippetQuery from '../queries/snippet.query.graphql';
|
|||
|
||||
const blobsDefault = [];
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const getSnippetMixin = {
|
||||
apollo: {
|
||||
snippet: {
|
||||
|
@ -39,5 +40,3 @@ export const getSnippetMixin = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -69,6 +69,3 @@ export const receiveArtifactsSuccess = ({ commit }, response) => {
|
|||
};
|
||||
|
||||
export const receiveArtifactsError = ({ commit }) => commit(types.RECEIVE_ARTIFACTS_ERROR);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { s__, n__ } from '~/locale';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const title = state => {
|
||||
if (state.isLoading) {
|
||||
return s__('BuildArtifacts|Loading artifacts');
|
||||
|
@ -11,6 +12,3 @@ export const title = state => {
|
|||
|
||||
return n__('View exposed artifact', 'View %d exposed artifacts', state.artifacts.length);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -56,6 +56,3 @@ export const createLabel = ({ state, dispatch }, label) => {
|
|||
|
||||
export const updateSelectedLabels = ({ commit }, labels) =>
|
||||
commit(types.UPDATE_SELECTED_LABELS, { labels });
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -50,6 +50,3 @@ export const isDropdownVariantStandalone = state => state.variant === DropdownVa
|
|||
* @param {object} state
|
||||
*/
|
||||
export const isDropdownVariantEmbedded = state => state.variant === DropdownVariant.Embedded;
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* @param {string} root - the key of the state where to search fo they keys described in list
|
||||
* @returns {Object} a dictionary with all the computed properties generated
|
||||
*/
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const mapComputed = (list, defaultUpdateFn, root) => {
|
||||
const result = {};
|
||||
list.forEach(item => {
|
||||
|
@ -32,5 +33,3 @@ export const mapComputed = (list, defaultUpdateFn, root) => {
|
|||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -15,6 +15,3 @@ export const show = ({ commit }) => {
|
|||
export const hide = ({ commit }) => {
|
||||
commit(types.HIDE);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
.designs-root {
|
||||
border: 2px dashed transparent;
|
||||
transition: border $gl-transition-duration-medium $general-hover-transition-curve;
|
||||
|
||||
&:hover {
|
||||
border-color: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
.design-list-item {
|
||||
height: 280px;
|
||||
text-decoration: none;
|
||||
|
|
|
@ -45,7 +45,7 @@ module Projects
|
|||
|
||||
if result[:status] == :success
|
||||
pagerduty_token = project.incident_management_setting&.pagerduty_token
|
||||
webhook_url = project_incidents_pagerduty_url(project, token: pagerduty_token)
|
||||
webhook_url = project_incidents_integrations_pagerduty_url(project, token: pagerduty_token)
|
||||
|
||||
render json: { pagerduty_webhook_url: webhook_url, pagerduty_token: pagerduty_token }
|
||||
else
|
||||
|
|
|
@ -45,7 +45,7 @@ module OperationsHelper
|
|||
send_email: setting.send_email.to_s,
|
||||
pagerduty_active: setting.pagerduty_active.to_s,
|
||||
pagerduty_token: setting.pagerduty_token.to_s,
|
||||
pagerduty_webhook_url: project_incidents_pagerduty_url(@project, token: setting.pagerduty_token),
|
||||
pagerduty_webhook_url: project_incidents_integrations_pagerduty_url(@project, token: setting.pagerduty_token),
|
||||
pagerduty_reset_key_path: reset_pagerduty_token_project_settings_operations_path(@project)
|
||||
}
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class ApplicationSetting < ApplicationRecord
|
|||
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
|
||||
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
|
||||
|
||||
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
|
||||
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required }
|
||||
add_authentication_token_field :health_check_access_token
|
||||
add_authentication_token_field :static_objects_external_storage_auth_token
|
||||
|
||||
|
|
|
@ -351,7 +351,7 @@ module Ci
|
|||
after_transition any => [:failed] do |build|
|
||||
next unless build.project
|
||||
|
||||
if build.retry_failure?
|
||||
if build.auto_retry_allowed?
|
||||
begin
|
||||
Ci::Build.retry(build, build.user)
|
||||
rescue Gitlab::Access::AccessDeniedError => ex
|
||||
|
@ -373,6 +373,10 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def auto_retry_allowed?
|
||||
auto_retry.allowed?
|
||||
end
|
||||
|
||||
def detailed_status(current_user)
|
||||
Gitlab::Ci::Status::Build::Factory
|
||||
.new(self, current_user)
|
||||
|
@ -439,27 +443,6 @@ module Ci
|
|||
pipeline.builds.retried.where(name: self.name).count
|
||||
end
|
||||
|
||||
def retry_failure?
|
||||
max_allowed_retries = nil
|
||||
max_allowed_retries ||= options_retry_max if retry_on_reason_or_always?
|
||||
max_allowed_retries ||= DEFAULT_RETRIES.fetch(failure_reason.to_sym, 0)
|
||||
|
||||
max_allowed_retries > 0 && retries_count < max_allowed_retries
|
||||
end
|
||||
|
||||
def options_retry_max
|
||||
options_retry[:max]
|
||||
end
|
||||
|
||||
def options_retry_when
|
||||
options_retry.fetch(:when, ['always'])
|
||||
end
|
||||
|
||||
def retry_on_reason_or_always?
|
||||
options_retry_when.include?(failure_reason.to_s) ||
|
||||
options_retry_when.include?('always')
|
||||
end
|
||||
|
||||
def any_unmet_prerequisites?
|
||||
prerequisites.present?
|
||||
end
|
||||
|
@ -962,6 +945,12 @@ module Ci
|
|||
|
||||
private
|
||||
|
||||
def auto_retry
|
||||
strong_memoize(:auto_retry) do
|
||||
Gitlab::Ci::Build::AutoRetry.new(self)
|
||||
end
|
||||
end
|
||||
|
||||
def dependencies
|
||||
strong_memoize(:dependencies) do
|
||||
Ci::BuildDependencies.new(self)
|
||||
|
@ -1017,19 +1006,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
# The format of the retry option changed in GitLab 11.5: Before it was
|
||||
# integer only, after it is a hash. New builds are created with the new
|
||||
# format, but builds created before GitLab 11.5 and saved in database still
|
||||
# have the old integer only format. This method returns the retry option
|
||||
# normalized as a hash in 11.5+ format.
|
||||
def options_retry
|
||||
strong_memoize(:options_retry) do
|
||||
value = options&.dig(:retry)
|
||||
value = value.is_a?(Integer) ? { max: value } : value.to_h
|
||||
value.with_indifferent_access
|
||||
end
|
||||
end
|
||||
|
||||
def has_expiring_artifacts?
|
||||
artifacts_expire_at.present? && artifacts_expire_at > Time.current
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ module Ci
|
|||
include TokenAuthenticatable
|
||||
include IgnorableColumns
|
||||
|
||||
add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
|
||||
add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption) ? :optional : :required }
|
||||
|
||||
enum access_level: {
|
||||
not_protected: 0,
|
||||
|
|
|
@ -13,4 +13,8 @@ module ApprovableBase
|
|||
|
||||
approved_by_users.include?(user)
|
||||
end
|
||||
|
||||
def can_be_approved_by?(user)
|
||||
user && !approved_by?(user) && user.can?(:approve_merge_request, self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,12 +43,12 @@ class Suggestion < ApplicationRecord
|
|||
|
||||
def inapplicable_reason(cached: true)
|
||||
strong_memoize("inapplicable_reason_#{cached}") do
|
||||
next :applied if applied?
|
||||
next :merge_request_merged if noteable.merged?
|
||||
next :merge_request_closed if noteable.closed?
|
||||
next :source_branch_deleted unless noteable.source_branch_exists?
|
||||
next :outdated if outdated?(cached: cached) || !note.active?
|
||||
next :same_content unless different_content?
|
||||
next _("Can't apply this suggestion.") if applied?
|
||||
next _("This merge request was merged. To apply this suggestion, edit this file directly.") if noteable.merged?
|
||||
next _("This merge request is closed. To apply this suggestion, edit this file directly.") if noteable.closed?
|
||||
next _("Can't apply as the source branch was deleted.") unless noteable.source_branch_exists?
|
||||
next outdated_reason if outdated?(cached: cached) || !note.active?
|
||||
next _("This suggestion already matches its content.") unless different_content?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -73,4 +73,12 @@ class Suggestion < ApplicationRecord
|
|||
def different_content?
|
||||
from_content != to_content
|
||||
end
|
||||
|
||||
def outdated_reason
|
||||
if single_line?
|
||||
_("Can't apply as this line was changed in a more recent version.")
|
||||
else
|
||||
_("Can't apply as these lines were changed in a more recent version.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,24 +16,8 @@ class SuggestionEntity < API::Entities::Suggestion
|
|||
|
||||
expose :inapplicable_reason do |suggestion|
|
||||
next _("You don't have write access to the source branch.") unless can_apply?(suggestion)
|
||||
next if suggestion.appliable?
|
||||
|
||||
case suggestion.inapplicable_reason
|
||||
when :merge_request_merged
|
||||
_("This merge request was merged. To apply this suggestion, edit this file directly.")
|
||||
when :merge_request_closed
|
||||
_("This merge request is closed. To apply this suggestion, edit this file directly.")
|
||||
when :source_branch_deleted
|
||||
_("Can't apply as the source branch was deleted.")
|
||||
when :outdated
|
||||
phrase = suggestion.single_line? ? 'this line was' : 'these lines were'
|
||||
|
||||
_("Can't apply as %{phrase} changed in a more recent version.") % { phrase: phrase }
|
||||
when :same_content
|
||||
_("This suggestion already matches its content.")
|
||||
else
|
||||
_("Can't apply this suggestion.")
|
||||
end
|
||||
suggestion.inapplicable_reason
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
- viewer.errors.messages.each do |error|
|
||||
%li= error.join(': ')
|
||||
|
||||
= link_to _('Learn more'), help_page_path('operations/metrics/dashboards/index.md', anchor: 'defining-custom-dashboards-per-project')
|
||||
= link_to _('Learn more'), help_page_path('operations/metrics/dashboards/index.md')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Disable application_settings_tokens_optional_encryption feature flag.
|
||||
merge_request: 31798
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Disable ci_runners_tokens_optional_encryption feature flag.
|
||||
merge_request: 31800
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix CSV downloads for multiple series in the same chart
|
||||
merge_request: 36556
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Resolve Pasting an image into a comment also uploads design
|
||||
merge_request: 37171
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change PagerDuty webhook URL
|
||||
merge_request: 37321
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/28167-dockerfile-template-rust.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Add Rust Dockerfile to GitLab templates"
|
||||
merge_request: 28167
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix merge request approvals for EE without a license
|
||||
merge_request: 37246
|
||||
author:
|
||||
type: fixed
|
|
@ -53,7 +53,7 @@ en:
|
|||
errors:
|
||||
messages:
|
||||
already_confirmed: "was already confirmed, please try signing in"
|
||||
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
|
||||
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one below"
|
||||
expired: "has expired, please request a new one"
|
||||
not_found: "not found"
|
||||
not_locked: "was not locked"
|
||||
|
|
|
@ -291,6 +291,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
get 'details', on: :member
|
||||
end
|
||||
|
||||
post 'incidents/integrations/pagerduty', to: 'incident_management/pager_duty_incidents#create'
|
||||
|
||||
namespace :error_tracking do
|
||||
resources :projects, only: :index
|
||||
end
|
||||
|
@ -407,8 +409,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
|
||||
post 'alerts/notify', to: 'alerting/notifications#create'
|
||||
|
||||
post 'incidents/pagerduty', to: 'incident_management/pager_duty_incidents#create'
|
||||
|
||||
draw :legacy_builds
|
||||
|
||||
resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope
|
||||
|
|
|
@ -167,6 +167,11 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
|
|||
corresponding to the given address. See [the PostgreSQL documentation](https://www.postgresql.org/docs/11/runtime-config-connection.html)
|
||||
for more details.
|
||||
|
||||
NOTE: **Note:**
|
||||
If you need to use `0.0.0.0` or `*` as the listen_address, you will also need to add
|
||||
`127.0.0.1/32` to the `postgresql['md5_auth_cidr_addresses']` setting, to allow Rails to connect through
|
||||
`127.0.0.1`. For more information, see [omnibus-5258](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5258).
|
||||
|
||||
Depending on your network configuration, the suggested addresses may not
|
||||
be correct. If your **primary** node and **secondary** nodes connect over a local
|
||||
area network, or a virtual network connecting availability zones like
|
||||
|
|
|
@ -58,7 +58,7 @@ The dashboard uses metrics available in
|
|||
![GitLab self monitoring default dashboard](img/self_monitoring_default_dashboard.png)
|
||||
|
||||
You can also
|
||||
[create your own dashboards](../../../operations/metrics/dashboards/index.md#defining-custom-dashboards-per-project).
|
||||
[create your own dashboards](../../../operations/metrics/dashboards/index.md).
|
||||
|
||||
## Connection to Prometheus
|
||||
|
||||
|
@ -83,8 +83,8 @@ Once the webhook is setup, you can
|
|||
|
||||
You can add custom metrics in the self monitoring project by:
|
||||
|
||||
1. [Duplicating](../../../operations/metrics/dashboards/index.md#duplicating-a-gitlab-defined-dashboard) the default dashboard.
|
||||
1. [Editing](../../../operations/metrics/dashboards/index.md#view-and-edit-the-source-file-of-a-custom-dashboard) the newly created dashboard file and configuring it with [dashboard YAML properties](../../../operations/metrics/dashboards/yaml.md).
|
||||
1. [Duplicating](../../../operations/metrics/dashboards/index.md#duplicate-a-gitlab-defined-dashboard) the default dashboard.
|
||||
1. [Editing](../../../operations/metrics/index.md) the newly created dashboard file and configuring it with [dashboard YAML properties](../../../operations/metrics/dashboards/yaml.md).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
@ -755,11 +755,8 @@ the `weight` parameter:
|
|||
|
||||
## Rate limits
|
||||
|
||||
To help avoid abuse, users are limited to:
|
||||
|
||||
| Request Type | Limit |
|
||||
| ---------------- | --------------------------- |
|
||||
| Create | 300 issues per minute |
|
||||
To help avoid abuse, users can be limited to a specific number of `Create` requests per minute.
|
||||
See [Issues rate limits](../user/admin_area/settings/rate_limit_on_issues_creation.md).
|
||||
|
||||
## Edit issue
|
||||
|
||||
|
|
|
@ -371,6 +371,6 @@ are listed in the descriptions of the relevant settings.
|
|||
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
|
||||
| `web_ide_clientside_preview_enabled` | boolean | no | Live Preview (allow live previews of JavaScript projects in the Web IDE using CodeSandbox Live Preview). |
|
||||
| `snippet_size_limit` | integer | no | Max snippet content size in **bytes**. Default: 52428800 Bytes (50MB).|
|
||||
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Default: 300. To disable throttling set to 0.|
|
||||
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
|
||||
| `raw_blob_request_limit` | integer | no | Max number of requests per minute for each raw path. Default: 300. To disable throttling set to 0.|
|
||||
| `wiki_page_max_content_bytes` | integer | no | Max wiki page content size in **bytes**. Default: 52428800 Bytes (50MB).|
|
||||
|
|
|
@ -92,6 +92,10 @@ Example response:
|
|||
"key": "Ruby-alpine",
|
||||
"name": "Ruby-alpine"
|
||||
},
|
||||
{
|
||||
"key": "Rust",
|
||||
"name": "Rust"
|
||||
},
|
||||
{
|
||||
"key": "Swift",
|
||||
"name": "Swift"
|
||||
|
|
|
@ -23,6 +23,14 @@ Classes in [`utilities.scss`](https://gitlab.com/gitlab-org/gitlab/blob/master/a
|
|||
|
||||
Avoid [Bootstrap's Utility Classes](https://getbootstrap.com/docs/4.3/utilities/).
|
||||
|
||||
NOTE: **Note:**
|
||||
While migrating [Bootstrap's Utility Classes](https://getbootstrap.com/docs/4.3/utilities/)
|
||||
to the [GitLab UI](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/master/doc/css.md#utilities)
|
||||
utility classes, note both the classes for margin and padding differ. The size scale used at
|
||||
GitLab differs from the scale used in the Bootstrap library. For a Bootstrap padding or margin
|
||||
utility, you may need to double the size of the applied utility to achieve the same visual
|
||||
result (such as `ml-1` becoming `gl-ml-2`).
|
||||
|
||||
#### Where should I put new utility classes?
|
||||
|
||||
If a class you need has not been added to GitLab UI, you get to add it! Follow the naming patterns documented in the [utility files](https://gitlab.com/gitlab-org/gitlab-ui/-/tree/master/src/scss/utility-mixins) and refer to [GitLab UI's CSS documentation](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/master/doc/contributing/adding_css.md#adding-utility-mixins) for more details, especially about adding responsive and stateful rules.
|
||||
|
|
|
@ -180,6 +180,11 @@ For example, to add support for files referenced by a `Widget` model with a
|
|||
|
||||
mount_uploader :file, WidgetUploader
|
||||
|
||||
def local?
|
||||
# Must to be implemented, Check the uploader's storage types
|
||||
file_store == ObjectStorage::Store::LOCAL
|
||||
end
|
||||
|
||||
def self.replicables_for_geo_node
|
||||
# Should be implemented. The idea of the method is to restrict
|
||||
# the set of synced items depending on synchronization settings
|
||||
|
@ -227,7 +232,7 @@ For example, to add support for files referenced by a `Widget` model with a
|
|||
```
|
||||
|
||||
1. Create the `widget_registry` table so Geo secondaries can track the sync and
|
||||
verification state of each Widget's file:
|
||||
verification state of each Widget's file. This migration belongs in `ee/db/geo/migrate`:
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
@ -286,6 +291,8 @@ For example, to add support for files referenced by a `Widget` model with a
|
|||
|
||||
1. Update `REGISTRY_CLASSES` in `ee/app/workers/geo/secondary/registry_consistency_worker.rb`.
|
||||
|
||||
1. Add `widget_registry` to `ActiveSupport::Inflector.inflections` in `config/initializers_before_autoloader/000_inflections.rb`.
|
||||
|
||||
1. Create `ee/spec/factories/geo/widget_registry.rb`:
|
||||
|
||||
```ruby
|
||||
|
|
|
@ -771,13 +771,37 @@ yarn karma -f 'spec/javascripts/ide/**/file_spec.js'
|
|||
|
||||
## Frontend test fixtures
|
||||
|
||||
Code that is added to HAML templates (in `app/views/`) or makes Ajax requests to the backend has tests that require HTML or JSON from the backend.
|
||||
Fixtures for these tests are located at:
|
||||
Frontend fixtures are files containing responses from backend controllers. These responses can be either HTML
|
||||
generated from haml templates or JSON payloads. Frontend tests that rely on these responses are
|
||||
often using fixtures to validate correct integration with the backend code.
|
||||
|
||||
### Generate fixtures
|
||||
|
||||
You can find code to generate test fixtures in:
|
||||
|
||||
- `spec/frontend/fixtures/`, for running tests in CE.
|
||||
- `ee/spec/frontend/fixtures/`, for running tests in EE.
|
||||
|
||||
Fixture files in:
|
||||
You can generate fixtures by running:
|
||||
|
||||
- `bin/rake frontend:fixtures` to generate all fixtures
|
||||
- `bin/rspec spec/frontend/fixtures/merge_requests.rb` to generate specific fixtures (in this case for `merge_request.rb`)
|
||||
|
||||
You can find generated fixtures are in `tmp/tests/frontend/fixtures-ee`.
|
||||
|
||||
#### Creating new fixtures
|
||||
|
||||
For each fixture, you can find the content of the `response` variable in the output file.
|
||||
For example, test named `"merge_requests/diff_discussion.json"` in `spec/frontend/fixtures/merge_requests.rb`
|
||||
will produce output file `tmp/tests/frontend/fixtures-ee/merge_requests/diff_discussion.json`.
|
||||
The `response` variable gets automatically set if the test is marked as `type: :request` or `type: :controller`.
|
||||
|
||||
When creating a new fixture, it often makes sense to take a look at the corresponding tests for the
|
||||
endpoint in `(ee/)spec/controllers/` or `(ee/)spec/requests/`.
|
||||
|
||||
### Use fixtures
|
||||
|
||||
Jest and Karma test suites import fixtures in different ways:
|
||||
|
||||
- The Karma test suite are served by [jasmine-jquery](https://github.com/velesin/jasmine-jquery).
|
||||
- Jest use `spec/frontend/helpers/fixtures.js`.
|
||||
|
@ -803,14 +827,6 @@ it('uses some HTML element', () => {
|
|||
});
|
||||
```
|
||||
|
||||
HTML and JSON fixtures are generated from backend views and controllers using RSpec (see `spec/frontend/fixtures/*.rb`).
|
||||
|
||||
For each fixture, the content of the `response` variable is stored in the output file.
|
||||
This variable gets automatically set if the test is marked as `type: :request` or `type: :controller`.
|
||||
Fixtures are regenerated using the `bin/rake frontend:fixtures` command but you can also generate them individually,
|
||||
for example `bin/rspec spec/frontend/fixtures/merge_requests.rb`.
|
||||
When creating a new fixture, it often makes sense to take a look at the corresponding tests for the endpoint in `(ee/)spec/controllers/` or `(ee/)spec/requests/`.
|
||||
|
||||
## Data-driven tests
|
||||
|
||||
Similar to [RSpec's parameterized tests](best_practices.md#table-based--parameterized-tests),
|
||||
|
|
|
@ -4,7 +4,96 @@ group: APM
|
|||
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
|
||||
---
|
||||
|
||||
# Using the Metrics Dashboard
|
||||
# Custom dashboards
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/59974) in GitLab 12.1.
|
||||
|
||||
By default, all projects include a GitLab-defined Prometheus dashboard, which
|
||||
includes a few key metrics, but you can also define your own custom dashboards.
|
||||
|
||||
You may create a [new dashboard from scratch](#add-a-new-dashboard-to-your-project)
|
||||
or [duplicate a GitLab-defined Prometheus dashboard](#duplicate-a-gitlab-defined-dashboard).
|
||||
|
||||
NOTE: **Note:**
|
||||
The metrics as defined below do not support alerts, unlike
|
||||
[custom metrics](../index.md#adding-custom-metrics).
|
||||
|
||||
## Add a new dashboard to your project
|
||||
|
||||
> UI option [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223204) in GitLab 13.2.
|
||||
|
||||
You can configure a custom dashboard by adding a new YAML file into your project's
|
||||
`.gitlab/dashboards/` directory. For the dashboard to display on your project's
|
||||
**{cloud-gear}** **Operations > Metrics** page, the files must have a `.yml`
|
||||
extension and be present in your project's **default** branch.
|
||||
|
||||
To create a new dashboard from the GitLab user interface:
|
||||
|
||||
1. Sign in to GitLab as a user with Maintainer or Owner
|
||||
[permissions](../../../user/permissions.md#project-members-permissions).
|
||||
1. Navigate to your dashboard at **{cloud-gear}** **Operations > Metrics**.
|
||||
1. In the top-right corner of your dashboard, click the **{file-addition-solid}** **Actions** menu,
|
||||
and select **Create new**:
|
||||
![Monitoring Dashboard actions menu with create new item](../../../user/project/integrations/img/actions_menu_create_new_dashboard_v13_2.png)
|
||||
1. In the modal window, click **Open Repository**, then follow the instructions
|
||||
for creating a new dashboard from the command line.
|
||||
|
||||
To create a new dashboard from the command line:
|
||||
|
||||
1. Create `.gitlab/dashboards/prom_alerts.yml` under your repository's root
|
||||
directory. Each YAML file should define the layout of the dashboard and the
|
||||
Prometheus queries used to populate data. This example dashboard displays a
|
||||
single area chart:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- type: area-chart
|
||||
title: "Chart Title"
|
||||
y_label: "Y-Axis"
|
||||
y_axis:
|
||||
format: number
|
||||
precision: 0
|
||||
metrics:
|
||||
- id: my_metric_id
|
||||
query_range: 'http_requests_total'
|
||||
label: "Instance: {{instance}}, method: {{method}}"
|
||||
unit: "count"
|
||||
```
|
||||
|
||||
1. Save the file, commit, and push to your repository. The file must be present in your **default** branch.
|
||||
1. Navigate to your project's **Operations > Metrics** and choose the custom
|
||||
dashboard from the dropdown.
|
||||
|
||||
Your custom dashboard is available at `https://example.com/project/-/metrics/custom_dashboard_name.yml`.
|
||||
|
||||
NOTE: **Note:**
|
||||
Configuration files nested under subdirectories of `.gitlab/dashboards` are not
|
||||
supported and won't be available in the UI.
|
||||
|
||||
## Duplicate a GitLab-defined dashboard
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/37238) in GitLab 12.7.
|
||||
> - From [GitLab 12.8 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/39505), custom metrics are also duplicated when you duplicate a dashboard.
|
||||
|
||||
You can save a complete copy of a GitLab defined dashboard along with all custom metrics added to it.
|
||||
The resulting `.yml` file can be customized and adapted to your project.
|
||||
You can decide to save the dashboard `.yml` file in the project's **default** branch or in a
|
||||
new branch.
|
||||
|
||||
1. Click **Duplicate dashboard** in the dashboard dropdown or in the actions menu.
|
||||
|
||||
NOTE: **Note:**
|
||||
You can duplicate only GitLab-defined dashboards.
|
||||
|
||||
1. Enter the filename and other information, such as the new commit's message, and click **Duplicate**.
|
||||
1. Select a branch to add your dashboard to:
|
||||
- *If you select your **default** branch,* the new dashboard becomes immediately available.
|
||||
- *If you select another branch,* this branch should be merged to your **default** branch first.
|
||||
|
||||
Your custom dashboard is available at `https://example.com/project/-/metrics/custom_dashboard_name.yml`.
|
||||
|
||||
## Manage the metrics dashboard settings
|
||||
|
||||
|
@ -21,45 +110,24 @@ To manage the settings for your metrics dashboard:
|
|||
|
||||
## Chart Context Menu
|
||||
|
||||
From each of the panels in the dashboard, you can access the context menu by clicking the **{ellipsis_v}** **More actions** dropdown box above the upper right corner of the panel to take actions related to the chart's data.
|
||||
You can take action related to a chart's data by clicking the
|
||||
**{ellipsis_v}** **More actions** dropdown box above the upper right corner of
|
||||
any chart on a dashboard:
|
||||
|
||||
![Context Menu](../../../user/project/integrations/img/panel_context_menu_v13_0.png)
|
||||
|
||||
The options are:
|
||||
|
||||
- [Expand panel](#expand-panel)
|
||||
- [View logs](#view-logs-ultimate)
|
||||
- [Download CSV](#downloading-data-as-csv)
|
||||
- **Expand panel** - Displays a larger version of a visualization. To return to
|
||||
the dashboard, click the **Back** button in your browser, or press the <kbd>Esc</kbd> key.
|
||||
([Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3100) in GitLab 13.0.)
|
||||
- **View logs** **(ULTIMATE)** - Displays [Logs](../../../user/project/clusters/kubernetes_pod_logs.md),
|
||||
if they are enabled. If used in conjunction with the [timeline zoom](#timeline-zoom-and-url-sharing)
|
||||
feature, logs narrow down to the selected time range. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/122013) in GitLab 12.8.)
|
||||
- **Download CSV** - Data from Prometheus charts on the metrics dashboard can be downloaded as CSV.
|
||||
- [Copy link to chart](../embed.md#embedding-gitlab-managed-kubernetes-metrics)
|
||||
- [Alerts](../alerts.md)
|
||||
|
||||
### View and edit the source file of a custom dashboard
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34779) in GitLab 12.5.
|
||||
|
||||
When viewing a custom dashboard of a project, you can view the original
|
||||
`.yml` file by clicking on the **Edit dashboard** button.
|
||||
|
||||
### Expand panel
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3100) in GitLab 13.0.
|
||||
|
||||
To view a larger version of a visualization, expand the panel by clicking the
|
||||
**{ellipsis_v}** **More actions** icon and selecting **Expand panel**.
|
||||
|
||||
To return to the metrics dashboard, click the **Back** button in your
|
||||
browser, or pressing the <kbd>Esc</kbd> key.
|
||||
|
||||
### View Logs **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/122013) in GitLab 12.8.
|
||||
|
||||
If you have [Logs](../../../user/project/clusters/kubernetes_pod_logs.md) enabled,
|
||||
you can navigate from the charts in the dashboard to view Logs by
|
||||
clicking on the context menu in the upper-right corner.
|
||||
|
||||
If you use the **Timeline zoom** function at the bottom of the chart, logs will narrow down to the time range you selected.
|
||||
|
||||
### Timeline zoom and URL sharing
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198910) in GitLab 12.8.
|
||||
|
@ -69,10 +137,6 @@ on a date and time of your choice. When you click and drag the sliders to select
|
|||
a different beginning or end date of data to display, GitLab adds your selected start
|
||||
and end times to the URL, enabling you to share specific timeframes more easily.
|
||||
|
||||
### Downloading data as CSV
|
||||
|
||||
Data from Prometheus charts on the metrics dashboard can be downloaded as CSV.
|
||||
|
||||
## Dashboard Annotations
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211330) in GitLab 12.10 (enabled by feature flag `metrics_dashboard_annotations`).
|
||||
|
@ -90,7 +154,7 @@ You can create annotations by making requests to the
|
|||
|
||||
![Annotations UI](../../../user/project/integrations/img/metrics_dashboard_annotations_ui_v13.0.png)
|
||||
|
||||
### Retention policy
|
||||
### Annotation retention policy
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211433) in GitLab 13.01.
|
||||
|
||||
|
@ -139,99 +203,6 @@ links:
|
|||
type: grafana
|
||||
```
|
||||
|
||||
## Defining custom dashboards per project
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/59974) in GitLab 12.1.
|
||||
|
||||
By default, all projects include a GitLab-defined Prometheus dashboard, which
|
||||
includes a few key metrics, but you can also define your own custom dashboards.
|
||||
|
||||
You may create a new file from scratch or duplicate a GitLab-defined Prometheus
|
||||
dashboard.
|
||||
|
||||
NOTE: **Note:**
|
||||
The metrics as defined below do not support alerts, unlike
|
||||
[custom metrics](../index.md#adding-custom-metrics).
|
||||
|
||||
### Adding a new dashboard to your project
|
||||
|
||||
> UI option [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223204) in GitLab 13.2.
|
||||
|
||||
You can configure a custom dashboard by adding a new YAML file into your project's
|
||||
`.gitlab/dashboards/` directory. In order for the dashboards to be displayed on
|
||||
the project's **{cloud-gear}** **Operations > Metrics** page, the files must have a `.yml`
|
||||
extension and should be present in the project's **default** branch.
|
||||
|
||||
To create a new dashboard from the GitLab user interface:
|
||||
|
||||
1. Sign in to GitLab as a user with Maintainer or Owner
|
||||
[permissions](../../../user/permissions.md#project-members-permissions).
|
||||
1. Navigate to your dashboard at **{cloud-gear}** **Operations > Metrics**.
|
||||
1. In the top-right corner of your dashboard, click the **{file-addition-solid}** **Actions** menu,
|
||||
and select **Create new**:
|
||||
![Monitoring Dashboard actions menu with create new item](../../../user/project/integrations/img/actions_menu_create_new_dashboard_v13_2.png)
|
||||
1. In the modal window, click **Open Repository**, then follow the instructions
|
||||
for creating a new dashboard from the command line.
|
||||
|
||||
To create a new dashboard from the command line:
|
||||
|
||||
1. Create `.gitlab/dashboards/prom_alerts.yml` under your repository's root
|
||||
directory. Each YAML file should define the layout of the dashboard and the
|
||||
Prometheus queries used to populate data. This example dashboard displays a
|
||||
single area chart:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- type: area-chart
|
||||
title: "Chart Title"
|
||||
y_label: "Y-Axis"
|
||||
y_axis:
|
||||
format: number
|
||||
precision: 0
|
||||
metrics:
|
||||
- id: my_metric_id
|
||||
query_range: 'http_requests_total'
|
||||
label: "Instance: {{instance}}, method: {{method}}"
|
||||
unit: "count"
|
||||
```
|
||||
|
||||
1. Save the file, commit, and push to your repository. The file must be present in your **default** branch.
|
||||
1. Navigate to your project's **Operations > Metrics** and choose the custom
|
||||
dashboard from the dropdown.
|
||||
|
||||
NOTE: **Note:**
|
||||
Configuration files nested under subdirectories of `.gitlab/dashboards` are not
|
||||
supported and will not be available in the UI.
|
||||
|
||||
### Navigating to a custom dashboard
|
||||
|
||||
Custom dashboards are uniquely identified by their filenames. In order to quickly view the custom dashboard,
|
||||
just use the dashboard filename in the URL this way:
|
||||
`https://gitlab-instance.example.com/project/-/metrics/custom_dashboard_name.yml`.
|
||||
|
||||
### Duplicating a GitLab-defined dashboard
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/37238) in GitLab 12.7.
|
||||
> - From [GitLab 12.8 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/39505), custom metrics are also duplicated when you duplicate a dashboard.
|
||||
|
||||
You can save a complete copy of a GitLab defined dashboard along with all custom metrics added to it.
|
||||
Resulting `.yml` file can be customized and adapted to your project.
|
||||
You can decide to save the dashboard `.yml` file in the project's **default** branch or in a
|
||||
new branch.
|
||||
|
||||
1. Click **Duplicate dashboard** in the dashboard dropdown or in the actions menu.
|
||||
|
||||
NOTE: **Note:**
|
||||
You can duplicate only GitLab-defined dashboards.
|
||||
|
||||
1. Enter the file name and other information, such as the new commit's message, and click **Duplicate**.
|
||||
|
||||
If you select your **default** branch, the new dashboard becomes immediately available.
|
||||
If you select another branch, this branch should be merged to your **default** branch first.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
When troubleshooting issues with a managed Prometheus app, it is often useful to
|
||||
|
|
|
@ -53,7 +53,7 @@ Metric charts may also be hidden:
|
|||
![Show Hide](../../user/project/integrations/img/hide_embedded_metrics_v12_10.png)
|
||||
|
||||
You can open the link directly into your browser for a
|
||||
[detailed view of the data](dashboards/index.md#expand-panel).
|
||||
[detailed view of the data](dashboards/index.md#chart-context-menu).
|
||||
|
||||
## Embedding metrics in issue templates
|
||||
|
||||
|
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
|
@ -41,8 +41,11 @@ navigation bar contains:
|
|||
Starred dashboards display a solid star **{star}** button, and display first
|
||||
in the **Dashboard** dropdown list.
|
||||
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214582) in GitLab 13.0.)
|
||||
- **Edit dashboard** - Edit the source YAML file of a custom dashboard. Only available on
|
||||
[custom dashboards](dashboards/index.md).
|
||||
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34779) in GitLab 12.5.)
|
||||
- **Create dashboard** **{file-addition-solid}** - Create a
|
||||
[new custom dashboard for your project](dashboards/index.md#adding-a-new-dashboard-to-your-project).
|
||||
[new custom dashboard for your project](dashboards/index.md#add-a-new-dashboard-to-your-project).
|
||||
- **Metrics settings** **{settings}** - Configure the
|
||||
[settings for this dashboard](dashboards/index.md#manage-the-metrics-dashboard-settings).
|
||||
|
||||
|
|
Before Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 4 KiB |