diff options
Diffstat (limited to 'app/assets/javascripts/projects/pipelines')
-rw-r--r-- | app/assets/javascripts/projects/pipelines/charts/components/app.vue | 233 | ||||
-rw-r--r-- | app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue (renamed from app/assets/javascripts/projects/pipelines/charts/components/pipelines_area_chart.vue) | 15 | ||||
-rw-r--r-- | app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue (renamed from app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue) | 121 | ||||
-rw-r--r-- | app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue | 8 | ||||
-rw-r--r-- | app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql | 1 | ||||
-rw-r--r-- | app/assets/javascripts/projects/pipelines/charts/index.js | 102 |
6 files changed, 164 insertions, 316 deletions
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app.vue b/app/assets/javascripts/projects/pipelines/charts/components/app.vue index 4bf837faed1..7bb62cf4a73 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/app.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/app.vue @@ -1,37 +1,18 @@ <script> -import dateFormat from 'dateformat'; -import { GlColumnChart } from '@gitlab/ui/dist/charts'; -import { GlAlert, GlSkeletonLoader } from '@gitlab/ui'; -import { __, s__, sprintf } from '~/locale'; -import { getDateInPast } from '~/lib/utils/datetime_utility'; +import { GlAlert, GlTabs, GlTab } from '@gitlab/ui'; +import { s__ } from '~/locale'; import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql'; import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql'; -import StatisticsList from './statistics_list.vue'; -import PipelinesAreaChart from './pipelines_area_chart.vue'; +import PipelineCharts from './pipeline_charts.vue'; + import { - CHART_CONTAINER_HEIGHT, - CHART_DATE_FORMAT, DEFAULT, - INNER_CHART_HEIGHT, LOAD_ANALYTICS_FAILURE, LOAD_PIPELINES_FAILURE, - ONE_WEEK_AGO_DAYS, - ONE_MONTH_AGO_DAYS, PARSE_FAILURE, UNSUPPORTED_DATA, - X_AXIS_LABEL_ROTATION, - X_AXIS_TITLE_OFFSET, } from '../constants'; -const defaultCountValues = { - totalPipelines: { - count: 0, - }, - successfulPipelines: { - count: 0, - }, -}; - const defaultAnalyticsValues = { weekPipelinesTotals: [], weekPipelinesLabels: [], @@ -46,15 +27,29 @@ const defaultAnalyticsValues = { pipelineTimesValues: [], }; +const defaultCountValues = { + totalPipelines: { + count: 0, + }, + successfulPipelines: { + count: 0, + }, +}; + export default { components: { GlAlert, - GlColumnChart, - GlSkeletonLoader, - StatisticsList, - PipelinesAreaChart, + GlTabs, + GlTab, + PipelineCharts, + DeploymentFrequencyCharts: () => + import('ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue'), }, inject: { + shouldRenderDeploymentFrequencyCharts: { + type: Boolean, + default: false, + }, projectPath: { type: String, default: '', @@ -62,14 +57,10 @@ export default { }, data() { return { - counts: { - ...defaultCountValues, - }, - analytics: { - ...defaultAnalyticsValues, - }, showFailureAlert: false, failureType: null, + analytics: { ...defaultAnalyticsValues }, + counts: { ...defaultCountValues }, }; }, apollo: { @@ -127,47 +118,6 @@ export default { }; } }, - successRatio() { - const { successfulPipelines, failedPipelines } = this.counts; - const successfulCount = successfulPipelines?.count; - const failedCount = failedPipelines?.count; - const ratio = (successfulCount / (successfulCount + failedCount)) * 100; - - return failedCount === 0 ? 100 : ratio; - }, - formattedCounts() { - const { - totalPipelines, - successfulPipelines, - failedPipelines, - totalPipelineDuration, - } = this.counts; - - return { - total: totalPipelines?.count, - success: successfulPipelines?.count, - failed: failedPipelines?.count, - successRatio: this.successRatio, - totalDuration: totalPipelineDuration, - }; - }, - areaCharts() { - const { lastWeek, lastMonth, lastYear } = this.$options.chartTitles; - let areaChartsData = []; - - try { - areaChartsData = [ - this.buildAreaChartData(lastWeek, this.lastWeekChartData), - this.buildAreaChartData(lastMonth, this.lastMonthChartData), - this.buildAreaChartData(lastYear, this.lastYearChartData), - ]; - } catch { - areaChartsData = []; - this.reportFailure(PARSE_FAILURE); - } - - return areaChartsData; - }, lastWeekChartData() { return { labels: this.analytics.weekPipelinesLabels, @@ -189,39 +139,32 @@ export default { success: this.analytics.yearPipelinesSuccessful, }; }, - timesChartTransformedData() { - return [ - { - name: 'full', - data: this.mergeLabelsAndValues( - this.analytics.pipelineTimesLabels, - this.analytics.pipelineTimesValues, - ), - }, - ]; + timesChartData() { + return { + labels: this.analytics.pipelineTimesLabels, + values: this.analytics.pipelineTimesValues, + }; }, - }, - methods: { - mergeLabelsAndValues(labels, values) { - return labels.map((label, index) => [label, values[index]]); + successRatio() { + const { successfulPipelines, failedPipelines } = this.counts; + const successfulCount = successfulPipelines?.count; + const failedCount = failedPipelines?.count; + const ratio = (successfulCount / (successfulCount + failedCount)) * 100; + + return failedCount === 0 ? 100 : ratio; }, - buildAreaChartData(title, data) { - const { labels, totals, success } = data; + formattedCounts() { + const { totalPipelines, successfulPipelines, failedPipelines } = this.counts; return { - title, - data: [ - { - name: 'all', - data: this.mergeLabelsAndValues(labels, totals), - }, - { - name: 'success', - data: this.mergeLabelsAndValues(labels, success), - }, - ], + total: totalPipelines?.count, + success: successfulPipelines?.count, + failed: failedPipelines?.count, + successRatio: this.successRatio, }; }, + }, + methods: { hideAlert() { this.showFailureAlert = false; }, @@ -230,16 +173,6 @@ export default { this.failureType = type; }, }, - chartContainerHeight: CHART_CONTAINER_HEIGHT, - timesChartOptions: { - height: INNER_CHART_HEIGHT, - xAxis: { - axisLabel: { - rotate: X_AXIS_LABEL_ROTATION, - }, - nameGap: X_AXIS_TITLE_OFFSET, - }, - }, errorTexts: { [LOAD_ANALYTICS_FAILURE]: s__( 'PipelineCharts|An error has ocurred when retrieving the analytics data', @@ -250,60 +183,38 @@ export default { [PARSE_FAILURE]: s__('PipelineCharts|There was an error parsing the data for the charts.'), [DEFAULT]: s__('PipelineCharts|An unknown error occurred while processing CI/CD analytics.'), }, - get chartTitles() { - const today = dateFormat(new Date(), CHART_DATE_FORMAT); - const pastDate = timeScale => - dateFormat(getDateInPast(new Date(), timeScale), CHART_DATE_FORMAT); - return { - lastWeek: sprintf(__('Pipelines for last week (%{oneWeekAgo} - %{today})'), { - oneWeekAgo: pastDate(ONE_WEEK_AGO_DAYS), - today, - }), - lastMonth: sprintf(__('Pipelines for last month (%{oneMonthAgo} - %{today})'), { - oneMonthAgo: pastDate(ONE_MONTH_AGO_DAYS), - today, - }), - lastYear: __('Pipelines for last year'), - }; - }, }; </script> <template> <div> - <gl-alert v-if="showFailureAlert" :variant="failure.variant" @dismiss="hideAlert"> - {{ failure.text }} - </gl-alert> - <div class="gl-mb-3"> - <h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3> - </div> - <h4 class="gl-my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4> - <div class="row"> - <div class="col-md-6"> - <gl-skeleton-loader v-if="$apollo.queries.counts.loading" :lines="5" /> - <statistics-list v-else :counts="formattedCounts" /> - </div> - <div class="col-md-6"> - <strong> - {{ __('Duration for the last 30 commits') }} - </strong> - <gl-column-chart - :height="$options.chartContainerHeight" - :option="$options.timesChartOptions" - :bars="timesChartTransformedData" - :y-axis-title="__('Minutes')" - :x-axis-title="__('Commit')" - x-axis-type="category" + <gl-alert v-if="showFailureAlert" :variant="failure.variant" @dismiss="hideAlert">{{ + failure.text + }}</gl-alert> + <gl-tabs v-if="shouldRenderDeploymentFrequencyCharts"> + <gl-tab :title="__('Pipelines')"> + <pipeline-charts + :counts="formattedCounts" + :last-week="lastWeekChartData" + :last-month="lastMonthChartData" + :last-year="lastYearChartData" + :times-chart="timesChartData" + :loading="$apollo.queries.counts.loading" + @report-failure="reportFailure" /> - </div> - </div> - <hr /> - <h4 class="gl-my-4">{{ __('Pipelines charts') }}</h4> - <pipelines-area-chart - v-for="(chart, index) in areaCharts" - :key="index" - :chart-data="chart.data" - > - {{ chart.title }} - </pipelines-area-chart> + </gl-tab> + <gl-tab :title="__('Deployments')"> + <deployment-frequency-charts /> + </gl-tab> + </gl-tabs> + <pipeline-charts + v-else + :counts="formattedCounts" + :last-week="lastWeekChartData" + :last-month="lastMonthChartData" + :last-year="lastYearChartData" + :times-chart="timesChartData" + :loading="$apollo.queries.counts.loading" + @report-failure="reportFailure" + /> </div> </template> diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipelines_area_chart.vue b/app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue index d726196aadf..3590e2c4632 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/pipelines_area_chart.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue @@ -1,10 +1,10 @@ <script> import { GlAreaChart } from '@gitlab/ui/dist/charts'; -import { s__ } from '~/locale'; import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue'; import { CHART_CONTAINER_HEIGHT } from '../constants'; export default { + name: 'CiCdAnalyticsAreaChart', components: { GlAreaChart, ResizableChartContainer, @@ -14,14 +14,9 @@ export default { type: Array, required: true, }, - }, - areaChartOptions: { - xAxis: { - name: s__('Pipeline|Date'), - type: 'category', - }, - yAxis: { - name: s__('Pipeline|Pipelines'), + areaChartOptions: { + type: Object, + required: true, }, }, chartContainerHeight: CHART_CONTAINER_HEIGHT, @@ -39,7 +34,7 @@ export default { :height="$options.chartContainerHeight" :data="chartData" :include-legend-avg-max="false" - :option="$options.areaChartOptions" + :option="areaChartOptions" /> </resizable-chart-container> </div> diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue index c6e2b2e1140..bec4ab407f0 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue @@ -1,66 +1,81 @@ <script> import dateFormat from 'dateformat'; import { GlColumnChart } from '@gitlab/ui/dist/charts'; -import { __, sprintf } from '~/locale'; +import { GlSkeletonLoader } from '@gitlab/ui'; +import { __, s__, sprintf } from '~/locale'; import { getDateInPast } from '~/lib/utils/datetime_utility'; -import StatisticsList from './statistics_list.vue'; -import PipelinesAreaChart from './pipelines_area_chart.vue'; import { CHART_CONTAINER_HEIGHT, - INNER_CHART_HEIGHT, - X_AXIS_LABEL_ROTATION, - X_AXIS_TITLE_OFFSET, CHART_DATE_FORMAT, + INNER_CHART_HEIGHT, ONE_WEEK_AGO_DAYS, ONE_MONTH_AGO_DAYS, + X_AXIS_LABEL_ROTATION, + X_AXIS_TITLE_OFFSET, + PARSE_FAILURE, } from '../constants'; +import StatisticsList from './statistics_list.vue'; +import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue'; export default { components: { - StatisticsList, GlColumnChart, - PipelinesAreaChart, + GlSkeletonLoader, + StatisticsList, + CiCdAnalyticsAreaChart, }, props: { counts: { - type: Object, required: true, - }, - timesChartData: { type: Object, - required: true, }, - lastWeekChartData: { - type: Object, - required: true, + loading: { + required: false, + default: false, + type: Boolean, }, - lastMonthChartData: { + lastWeek: { + required: true, type: Object, + }, + lastMonth: { required: true, + type: Object, }, - lastYearChartData: { + lastYear: { + required: true, type: Object, + }, + timesChart: { required: true, + type: Object, }, }, - data() { - return { - timesChartTransformedData: [ - { - name: 'full', - data: this.mergeLabelsAndValues(this.timesChartData.labels, this.timesChartData.values), - }, - ], - }; - }, computed: { areaCharts() { const { lastWeek, lastMonth, lastYear } = this.$options.chartTitles; + const charts = [ + { title: lastWeek, data: this.lastWeek }, + { title: lastMonth, data: this.lastMonth }, + { title: lastYear, data: this.lastYear }, + ]; + let areaChartsData = []; + + try { + areaChartsData = charts.map(this.buildAreaChartData); + } catch { + areaChartsData = []; + this.vm.$emit('report-failure', PARSE_FAILURE); + } + return areaChartsData; + }, + timesChartTransformedData() { return [ - this.buildAreaChartData(lastWeek, this.lastWeekChartData), - this.buildAreaChartData(lastMonth, this.lastMonthChartData), - this.buildAreaChartData(lastYear, this.lastYearChartData), + { + name: 'full', + data: this.mergeLabelsAndValues(this.timesChart.labels, this.timesChart.values), + }, ]; }, }, @@ -68,7 +83,7 @@ export default { mergeLabelsAndValues(labels, values) { return labels.map((label, index) => [label, values[index]]); }, - buildAreaChartData(title, data) { + buildAreaChartData({ title, data }) { const { labels, totals, success } = data; return { @@ -96,9 +111,18 @@ export default { nameGap: X_AXIS_TITLE_OFFSET, }, }, + areaChartOptions: { + xAxis: { + name: s__('Pipeline|Date'), + type: 'category', + }, + yAxis: { + name: s__('Pipeline|Pipelines'), + }, + }, get chartTitles() { const today = dateFormat(new Date(), CHART_DATE_FORMAT); - const pastDate = timeScale => + const pastDate = (timeScale) => dateFormat(getDateInPast(new Date(), timeScale), CHART_DATE_FORMAT); return { lastWeek: sprintf(__('Pipelines for last week (%{oneWeekAgo} - %{today})'), { @@ -116,18 +140,17 @@ export default { </script> <template> <div> - <div class="mb-3"> + <div class="gl-mb-3"> <h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3> </div> - <h4 class="my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4> + <h4 class="gl-my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4> <div class="row"> <div class="col-md-6"> - <statistics-list :counts="counts" /> + <gl-skeleton-loader v-if="loading" :lines="5" /> + <statistics-list v-else :counts="counts" /> </div> - <div class="col-md-6"> - <strong> - {{ __('Duration for the last 30 commits') }} - </strong> + <div v-if="!loading" class="col-md-6"> + <strong>{{ __('Duration for the last 30 commits') }}</strong> <gl-column-chart :height="$options.chartContainerHeight" :option="$options.timesChartOptions" @@ -138,14 +161,16 @@ export default { /> </div> </div> - <hr /> - <h4 class="my-4">{{ __('Pipelines charts') }}</h4> - <pipelines-area-chart - v-for="(chart, index) in areaCharts" - :key="index" - :chart-data="chart.data" - > - {{ chart.title }} - </pipelines-area-chart> + <template v-if="!loading"> + <hr /> + <h4 class="gl-my-4">{{ __('Pipelines charts') }}</h4> + <ci-cd-analytics-area-chart + v-for="(chart, index) in areaCharts" + :key="index" + :chart-data="chart.data" + :area-chart-options="$options.areaChartOptions" + >{{ chart.title }}</ci-cd-analytics-area-chart + > + </template> </div> </template> diff --git a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue index 94cecd2e479..7bc3b787f75 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue @@ -1,5 +1,4 @@ <script> -import { formatTime } from '~/lib/utils/datetime_utility'; import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format'; import { s__, n__ } from '~/locale'; @@ -13,9 +12,6 @@ export default { }, }, computed: { - totalDuration() { - return formatTime(this.counts.totalDuration); - }, statistics() { const formatter = getFormatter(SUPPORTED_FORMATS.percentHundred); @@ -36,10 +32,6 @@ export default { title: s__('PipelineCharts|Success ratio:'), value: formatter(this.counts.successRatio, defaultPrecision), }, - { - title: s__('PipelineCharts|Total duration:'), - value: this.totalDuration, - }, ]; }, }, diff --git a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql index eb0dbf8dd16..d68df689f5f 100644 --- a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql +++ b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql @@ -9,6 +9,5 @@ query getPipelineCountByStatus($projectPath: ID!) { failedPipelines: pipelines(status: FAILED) { count } - totalPipelineDuration } } diff --git a/app/assets/javascripts/projects/pipelines/charts/index.js b/app/assets/javascripts/projects/pipelines/charts/index.js index f6e79f0ab51..7e746423b6a 100644 --- a/app/assets/javascripts/projects/pipelines/charts/index.js +++ b/app/assets/javascripts/projects/pipelines/charts/index.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import ProjectPipelinesChartsLegacy from './components/app_legacy.vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; import ProjectPipelinesCharts from './components/app.vue'; Vue.use(VueApollo); @@ -10,99 +10,25 @@ const apolloProvider = new VueApollo({ defaultClient: createDefaultClient(), }); -const mountPipelineChartsApp = el => { - // Not all of the values will be defined since some them will be - // empty depending on the value of the graphql_pipeline_analytics - // feature flag, once the rollout of the feature flag is completed - // the undefined values will be deleted - const { - countsFailed, - countsSuccess, - countsTotal, - countsTotalDuration, - successRatio, - timesChartLabels, - timesChartValues, - lastWeekChartLabels, - lastWeekChartTotals, - lastWeekChartSuccess, - lastMonthChartLabels, - lastMonthChartTotals, - lastMonthChartSuccess, - lastYearChartLabels, - lastYearChartTotals, - lastYearChartSuccess, - projectPath, - } = el.dataset; +const mountPipelineChartsApp = (el) => { + const { projectPath } = el.dataset; - const parseAreaChartData = (labels, totals, success) => { - let parsedData = {}; - - try { - parsedData = { - labels: JSON.parse(labels), - totals: JSON.parse(totals), - success: JSON.parse(success), - }; - } catch { - parsedData = {}; - } - - return parsedData; - }; - - if (gon?.features?.graphqlPipelineAnalytics) { - return new Vue({ - el, - name: 'ProjectPipelinesChartsApp', - components: { - ProjectPipelinesCharts, - }, - apolloProvider, - provide: { - projectPath, - }, - render: createElement => createElement(ProjectPipelinesCharts, {}), - }); - } + const shouldRenderDeploymentFrequencyCharts = parseBoolean( + el.dataset.shouldRenderDeploymentFrequencyCharts, + ); return new Vue({ el, - name: 'ProjectPipelinesChartsAppLegacy', + name: 'ProjectPipelinesChartsApp', components: { - ProjectPipelinesChartsLegacy, + ProjectPipelinesCharts, + }, + apolloProvider, + provide: { + projectPath, + shouldRenderDeploymentFrequencyCharts, }, - render: createElement => - createElement(ProjectPipelinesChartsLegacy, { - props: { - counts: { - failed: countsFailed, - success: countsSuccess, - total: countsTotal, - successRatio, - totalDuration: countsTotalDuration, - }, - timesChartData: { - labels: JSON.parse(timesChartLabels), - values: JSON.parse(timesChartValues), - }, - lastWeekChartData: parseAreaChartData( - lastWeekChartLabels, - lastWeekChartTotals, - lastWeekChartSuccess, - ), - lastMonthChartData: parseAreaChartData( - lastMonthChartLabels, - lastMonthChartTotals, - lastMonthChartSuccess, - ), - lastYearChartData: parseAreaChartData( - lastYearChartLabels, - lastYearChartTotals, - lastYearChartSuccess, - ), - }, - }), + render: (createElement) => createElement(ProjectPipelinesCharts, {}), }); }; |