summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/projects/pipelines/charts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/projects/pipelines/charts')
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/app.vue233
-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.vue8
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql1
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/index.js102
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, {}),
});
};