Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-07-20 18:09:27 +00:00
parent dd2da214c9
commit bf213f07c8
131 changed files with 1023 additions and 802 deletions

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -28,6 +28,3 @@ export const parsedData = state => {
byAuthorEmail,
};
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};

View file

@ -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 () => {};

View file

@ -246,28 +246,28 @@ export default {
this.onUploadDesign([newFile]);
}
},
toggleOnPasteListener(route) {
if (route === DESIGNS_ROUTE_NAME) {
toggleOnPasteListener() {
document.addEventListener('paste', this.onDesignPaste);
} else {
},
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>

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -34,5 +34,3 @@ export const updateIgnoreStatus = ({ commit, dispatch }, params) => {
commit(types.SET_UPDATING_IGNORE_STATUS, false);
});
};
export default () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -102,5 +102,3 @@ export const fetchPaginatedResults = ({ commit, dispatch }, cursor) => {
export const removeIgnoredResolvedErrors = ({ commit }, error) => {
commit(types.REMOVE_IGNORED_RESOLVED_ERRORS, error);
};
export default () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -14,5 +14,3 @@ export const transformFrontendSettings = ({ apiHost, enabled, token, selectedPro
};
export const getDisplayName = project => `${project.organizationName} | ${project.slug}`;
export default () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 {

View file

@ -32,5 +32,3 @@ export const fetchBranches = ({ dispatch, rootGetters }, { search = '' }) => {
};
export const resetBranches = ({ commit }) => commit(types.RESET_BRANCHES);
export default () => {};

View file

@ -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 () => {};

View file

@ -41,5 +41,3 @@ export const fetchMergeRequests = (
};
export const resetMergeRequests = ({ commit }) => commit(types.RESET_MERGE_REQUESTS);
export default () => {};

View file

@ -149,5 +149,3 @@ export const resetLatestPipeline = ({ commit }) => {
commit(types.RECEIVE_LASTEST_PIPELINE_SUCCESS, null);
commit(types.SET_DETAIL_JOB, null);
};
export default () => {};

View file

@ -20,5 +20,3 @@ export const failedJobsCount = state =>
);
export const jobsCount = state => state.stages.reduce((acc, stage) => acc + stage.jobs.length, 0);
export default () => {};

View file

@ -2,4 +2,3 @@ export * from './setup';
export * from './checks';
export * from './session_controls';
export * from './session_status';
export default () => {};

View file

@ -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 () => {};

View file

@ -128,6 +128,3 @@ export const fetchJobs = ({ state, commit, dispatch }) => {
}
});
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};

View file

@ -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">
&middot;
<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"
>
<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 }}

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -36,7 +36,7 @@ export default {
);
},
xAxisName() {
return this.graphData.x_label || '';
return this.graphData.xLabel || '';
},
yAxisName() {
return this.graphData.y_label || '';

View file

@ -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' });

View 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;
};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -1,3 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export const ADD_MODULE = 'ADD_MODULE';
export default () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -102,5 +102,3 @@ export const requestDeleteImage = ({ commit }, image) => {
commit(types.SET_MAIN_LOADING, false);
});
};
export default () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -18,6 +18,3 @@ export const translate = functions =>
}),
{},
);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};

View file

@ -38,5 +38,3 @@ export const SnippetShowInit = () => {
export const SnippetEditInit = () => {
appFactory(document.getElementById('js-snippet-edit'), SnippetsEdit);
};
export default () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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 () => {};

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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')

View file

@ -0,0 +1,5 @@
---
title: Disable application_settings_tokens_optional_encryption feature flag.
merge_request: 31798
author: Gilang Gumilar
type: changed

View file

@ -0,0 +1,5 @@
---
title: Disable ci_runners_tokens_optional_encryption feature flag.
merge_request: 31800
author: Gilang Gumilar
type: changed

View file

@ -0,0 +1,5 @@
---
title: Fix CSV downloads for multiple series in the same chart
merge_request: 36556
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Resolve Pasting an image into a comment also uploads design
merge_request: 37171
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Change PagerDuty webhook URL
merge_request: 37321
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: "Add Rust Dockerfile to GitLab templates"
merge_request: 28167
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Fix merge request approvals for EE without a license
merge_request: 37246
author:
type: fixed

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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).|

View file

@ -92,6 +92,10 @@ Example response:
"key": "Ruby-alpine",
"name": "Ruby-alpine"
},
{
"key": "Rust",
"name": "Rust"
},
{
"key": "Swift",
"name": "Swift"

View file

@ -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.

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -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).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

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