From 192bc8bd3109f30e957bf30a0139ae27fefd7936 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Jul 2020 21:09:23 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/assets/javascripts/api.js | 11 + .../javascripts/helpers/monitor_helper.js | 3 +- .../monitoring/components/dashboard_panel.vue | 12 +- .../javascripts/monitoring/csv_export.js | 147 ----------- .../pages/projects/pipelines/new/index.js | 17 +- .../components/pipeline_new_form.vue | 247 ++++++++++++++++++ .../javascripts/pipeline_new/constants.js | 2 + app/assets/javascripts/pipeline_new/index.js | 36 +++ .../javascripts/snippets/components/edit.vue | 2 +- .../vue_shared/components/markdown/header.vue | 13 +- .../build_html_to_markdown_renderer.js | 18 +- .../projects/pipelines_controller.rb | 1 + app/models/releases/link.rb | 2 +- app/views/projects/pipelines/new.html.haml | 66 ++--- .../214627-fix-incorrect-csv-export.yml | 5 - .../227598-make-ordered-list-configurable.yml | 5 + ... pagerduty_incidents_integration_13_3.png} | Bin doc/user/incident_management/index.md | 4 +- doc/user/project/integrations/bamboo.md | 6 + doc/user/project/integrations/bugzilla.md | 6 + .../integrations/custom_issue_tracker.md | 6 + .../integrations/discord_notifications.md | 6 + .../project/integrations/emails_on_push.md | 6 + doc/user/project/integrations/github.md | 6 + .../integrations/gitlab_slack_application.md | 6 + .../project/integrations/hangouts_chat.md | 6 + doc/user/project/integrations/hipchat.md | 6 + doc/user/project/integrations/index.md | 6 + doc/user/project/integrations/irker.md | 6 + doc/user/project/integrations/jira.md | 6 + .../integrations/jira_cloud_configuration.md | 6 + .../integrations/jira_server_configuration.md | 6 + doc/user/project/integrations/mattermost.md | 6 + .../integrations/mattermost_slash_commands.md | 6 + .../project/integrations/microsoft_teams.md | 6 + doc/user/project/integrations/mock_ci.md | 6 + doc/user/project/integrations/overview.md | 6 + doc/user/project/integrations/redmine.md | 6 + .../integrations/services_templates.md | 6 + doc/user/project/integrations/slack.md | 6 + .../integrations/slack_slash_commands.md | 6 + .../project/integrations/unify_circuit.md | 6 + doc/user/project/integrations/webex_teams.md | 6 + doc/user/project/integrations/webhooks.md | 6 + doc/user/project/integrations/youtrack.md | 6 + locale/gitlab.pot | 18 ++ ...late_new_pipeline_vars_with_params_spec.rb | 1 + .../projects/pipelines/pipelines_spec.rb | 2 + spec/fixtures/api/schemas/release/link.json | 1 - spec/frontend/api_spec.js | 30 +++ spec/frontend/helpers/monitor_helper_spec.js | 58 ++-- .../components/dashboard_panel_spec.js | 2 +- spec/frontend/monitoring/csv_export_spec.js | 126 --------- spec/frontend/monitoring/graph_data.js | 4 +- .../components/pipeline_new_form_spec.js | 108 ++++++++ spec/frontend/pipeline_new/mock_data.js | 21 ++ .../frontend/snippets/components/edit_spec.js | 69 ++--- .../build_html_to_markdown_renderer_spec.js | 22 ++ 58 files changed, 810 insertions(+), 405 deletions(-) delete mode 100644 app/assets/javascripts/monitoring/csv_export.js create mode 100644 app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue create mode 100644 app/assets/javascripts/pipeline_new/constants.js create mode 100644 app/assets/javascripts/pipeline_new/index.js delete mode 100644 changelogs/unreleased/214627-fix-incorrect-csv-export.yml create mode 100644 changelogs/unreleased/227598-make-ordered-list-configurable.yml rename doc/user/incident_management/img/{pagerduty_incidents_integration_13_2.png => pagerduty_incidents_integration_13_3.png} (100%) delete mode 100644 spec/frontend/monitoring/csv_export_spec.js create mode 100644 spec/frontend/pipeline_new/components/pipeline_new_form_spec.js create mode 100644 spec/frontend/pipeline_new/mock_data.js diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 246231d969b..64b55b4d12f 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -55,6 +55,7 @@ const Api = { adminStatisticsPath: '/api/:version/application/statistics', pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id', pipelinesPath: '/api/:version/projects/:id/pipelines/', + createPipelinePath: '/api/:version/projects/:id/pipeline', environmentsPath: '/api/:version/projects/:id/environments', rawFilePath: '/api/:version/projects/:id/repository/files/:path/raw', issuePath: '/api/:version/projects/:id/issues/:issue_iid', @@ -576,6 +577,16 @@ const Api = { }); }, + createPipeline(id, data) { + const url = Api.buildUrl(this.createPipelinePath).replace(':id', encodeURIComponent(id)); + + return axios.post(url, data, { + headers: { + 'Content-Type': 'application/json', + }, + }); + }, + environments(id) { const url = Api.buildUrl(this.environmentsPath).replace(':id', encodeURIComponent(id)); return axios.get(url); diff --git a/app/assets/javascripts/helpers/monitor_helper.js b/app/assets/javascripts/helpers/monitor_helper.js index 5e345321013..5f85ee58779 100644 --- a/app/assets/javascripts/helpers/monitor_helper.js +++ b/app/assets/javascripts/helpers/monitor_helper.js @@ -49,7 +49,7 @@ const multiMetricLabel = metricAttributes => { * @param {Object} metricAttributes - Default metric attribute values (e.g. method, instance) * @returns {String} The formatted query label */ -export const getSeriesLabel = (queryLabel, metricAttributes) => { +const getSeriesLabel = (queryLabel, metricAttributes) => { return ( singleAttributeLabel(queryLabel, metricAttributes) || templatedLabel(queryLabel, metricAttributes) || @@ -63,6 +63,7 @@ export 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 { diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue index 610bef37fdb..3e3c8408de3 100644 --- a/app/assets/javascripts/monitoring/components/dashboard_panel.vue +++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue @@ -30,7 +30,6 @@ 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', @@ -149,10 +148,13 @@ export default { return null; }, csvText() { - if (this.graphData) { - return graphDataToCsv(this.graphData); - } - return null; + 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); }, downloadCsv() { const data = new Blob([this.csvText], { type: 'text/plain' }); diff --git a/app/assets/javascripts/monitoring/csv_export.js b/app/assets/javascripts/monitoring/csv_export.js deleted file mode 100644 index 734e8dc07a7..00000000000 --- a/app/assets/javascripts/monitoring/csv_export.js +++ /dev/null @@ -1,147 +0,0 @@ -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_2_name"], - * rows: [ - * [ , , ], - * [ , , ] - * ... - * ] - * } - * - * @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; -}; diff --git a/app/assets/javascripts/pages/projects/pipelines/new/index.js b/app/assets/javascripts/pages/projects/pipelines/new/index.js index b0b077a5e4c..d5563143f0c 100644 --- a/app/assets/javascripts/pages/projects/pipelines/new/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/new/index.js @@ -1,12 +1,19 @@ import $ from 'jquery'; import NewBranchForm from '~/new_branch_form'; import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list'; +import initNewPipeline from '~/pipeline_new/index'; document.addEventListener('DOMContentLoaded', () => { - new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new + const el = document.getElementById('js-new-pipeline'); - setupNativeFormVariableList({ - container: $('.js-ci-variable-list-section'), - formField: 'variables_attributes', - }); + if (el) { + initNewPipeline(); + } else { + new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new + + setupNativeFormVariableList({ + container: $('.js-ci-variable-list-section'), + formField: 'variables_attributes', + }); + } }); diff --git a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue new file mode 100644 index 00000000000..c2c5e58eedd --- /dev/null +++ b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue @@ -0,0 +1,247 @@ + + + diff --git a/app/assets/javascripts/pipeline_new/constants.js b/app/assets/javascripts/pipeline_new/constants.js new file mode 100644 index 00000000000..b4ab1143f60 --- /dev/null +++ b/app/assets/javascripts/pipeline_new/constants.js @@ -0,0 +1,2 @@ +export const VARIABLE_TYPE = 'env_var'; +export const FILE_TYPE = 'file'; diff --git a/app/assets/javascripts/pipeline_new/index.js b/app/assets/javascripts/pipeline_new/index.js new file mode 100644 index 00000000000..1c4812c2e0e --- /dev/null +++ b/app/assets/javascripts/pipeline_new/index.js @@ -0,0 +1,36 @@ +import Vue from 'vue'; +import PipelineNewForm from './components/pipeline_new_form.vue'; + +export default () => { + const el = document.getElementById('js-new-pipeline'); + const { + projectId, + pipelinesPath, + refParam, + varParam, + fileParam, + refNames, + settingsLink, + } = el?.dataset; + + const variableParams = JSON.parse(varParam); + const fileParams = JSON.parse(fileParam); + const refs = JSON.parse(refNames); + + return new Vue({ + el, + render(createElement) { + return createElement(PipelineNewForm, { + props: { + projectId, + pipelinesPath, + refParam, + variableParams, + fileParams, + refs, + settingsLink, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue index 69593dc77f8..71ba4e0c183 100644 --- a/app/assets/javascripts/snippets/components/edit.vue +++ b/app/assets/javascripts/snippets/components/edit.vue @@ -116,7 +116,7 @@ export default { onBeforeUnload(e = {}) { const returnValue = __('Are you sure you want to lose unsaved changes?'); - if (!this.allBlobChangesRegistered) return undefined; + if (!this.allBlobChangesRegistered || this.isUpdating) return undefined; Object.assign(e, { returnValue }); return returnValue; diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 049f5e71849..dfa4730d4fa 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -1,6 +1,6 @@