Commit fc323cc3 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch...

Merge branch 'nfriend-297376-performance-improvements-from-retrieving-data-for-deployment-frequency-charts' into 'master'

Deployment frequency graph restructuring follow-up

See merge request gitlab-org/gitlab!52134
parents 5999400b 397d1ec6
import { s__ } from '~/locale';
/* eslint-disable @gitlab/require-i18n-strings */
export const LAST_WEEK = 'LAST_WEEK';
export const LAST_MONTH = 'LAST_MONTH';
export const LAST_90_DAYS = 'LAST_90_DAYS';
/* eslint-enable @gitlab/require-i18n-strings */
export const CHART_TITLE = s__('DeploymentFrequencyCharts|Deployments');
<script> <script>
import dateFormat from 'dateformat';
import Api from 'ee/api'; import Api from 'ee/api';
import { s__, sprintf } from '~/locale'; import { s__ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import * as Sentry from '~/sentry/wrapper'; import * as Sentry from '~/sentry/wrapper';
import { nDaysBefore, nMonthsBefore, getDatesInRange } from '~/lib/utils/datetime_utility';
import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue'; import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
import { allChartDefinitions, areaChartOptions } from './static_data';
import { apiDataToChartSeries } from './util';
import { LAST_WEEK, LAST_MONTH, LAST_90_DAYS } from './constants';
export default { export default {
name: 'DeploymentFrequencyCharts', name: 'DeploymentFrequencyCharts',
...@@ -19,97 +20,20 @@ export default { ...@@ -19,97 +20,20 @@ export default {
}, },
}, },
data() { data() {
// Compute all relative dates based on the _beginning_ of today
const startOfToday = new Date(new Date().setHours(0, 0, 0, 0));
const lastWeek = new Date(nDaysBefore(startOfToday, 7));
const lastMonth = new Date(nMonthsBefore(startOfToday, 1));
const last90Days = new Date(nDaysBefore(startOfToday, 90));
const apiDateFormatString = 'isoDateTime';
const titleDateFormatString = 'mmm d';
const sharedRequestParams = {
environment: 'production',
interval: 'daily',
// We will never have more than 91 records (1 record per day), so we
// don't have to worry about making multiple requests to get all the results
per_page: 100,
};
return { return {
charts: [ chartData: {
{ [LAST_WEEK]: [],
title: sprintf( [LAST_MONTH]: [],
s__( [LAST_90_DAYS]: [],
'DeploymentFrequencyCharts|Deployments to production for last week (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(lastWeek, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
},
),
startDate: lastWeek,
requestParams: {
...sharedRequestParams,
from: dateFormat(lastWeek, apiDateFormatString),
},
isLoading: true,
data: [],
},
{
title: sprintf(
s__(
'DeploymentFrequencyCharts|Deployments to production for last month (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(lastMonth, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
},
),
startDate: lastMonth,
requestParams: {
...sharedRequestParams,
from: dateFormat(lastMonth, apiDateFormatString),
},
isLoading: true,
data: [],
},
{
title: sprintf(
s__(
'DeploymentFrequencyCharts|Deployments to production for the last 90 days (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(last90Days, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
},
),
startDate: last90Days,
requestParams: {
...sharedRequestParams,
from: dateFormat(last90Days, apiDateFormatString),
},
isLoading: true,
data: [],
}, },
],
}; };
}, },
async mounted() { async mounted() {
const results = await Promise.allSettled( const results = await Promise.allSettled(
this.charts.map(async (c) => { allChartDefinitions.map(async ({ id, requestParams, startDate }) => {
const chart = c; const { data: apiData } = await Api.deploymentFrequencies(this.projectPath, requestParams);
chart.isLoading = true;
try {
const { data: apiData } = await Api.deploymentFrequencies(
this.projectPath,
chart.requestParams,
);
chart.data = this.apiDataToChartSeries(apiData, chart.startDate); this.chartData[id] = apiDataToChartSeries(apiData, startDate);
} finally {
chart.isLoading = false;
}
}), }),
); );
...@@ -130,59 +54,17 @@ export default { ...@@ -130,59 +54,17 @@ export default {
); );
} }
}, },
methods: { allChartDefinitions,
/** areaChartOptions,
* Converts the raw data fetched from the
* [Deployment Frequency API](https://docs.gitlab.com/ee/api/project_analytics.html#list-project-deployment-frequencies)
* into series data consumable by
* [GlAreaChart](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/charts-area-chart--default)
*
* @param apiData The raw JSON data from the API request
* @param startDate The first day that should be rendered on the graph
*/
apiDataToChartSeries(apiData, startDate) {
// Get a list of dates (formatted identically to the dates in the API response),
// one date per day in the graph's date range
const dates = getDatesInRange(startDate, new Date(), (date) =>
dateFormat(date, 'yyyy-mm-dd'),
);
// Fill in the API data (the API data doesn't included data points for
// days with 0 deployments) and transform it for use in the graph
const data = dates.map((date) => {
const value = apiData.find((dataPoint) => dataPoint.from === date)?.value || 0;
const formattedDate = dateFormat(new Date(date), 'mmm d');
return [formattedDate, value];
});
return [
{
name: s__('DeploymentFrequencyCharts|Deployments'),
data,
},
];
},
},
areaChartOptions: {
xAxis: {
name: s__('DeploymentFrequencyCharts|Date'),
type: 'category',
},
yAxis: {
name: s__('DeploymentFrequencyCharts|Deployments'),
type: 'value',
minInterval: 1,
},
},
}; };
</script> </script>
<template> <template>
<div> <div>
<h4 class="gl-my-4">{{ s__('DeploymentFrequencyCharts|Deployments charts') }}</h4> <h4 class="gl-my-4">{{ s__('DeploymentFrequencyCharts|Deployments charts') }}</h4>
<ci-cd-analytics-area-chart <ci-cd-analytics-area-chart
v-for="(chart, index) in charts" v-for="chart of $options.allChartDefinitions"
:key="index" :key="chart.id"
:chart-data="chart.data" :chart-data="chartData[chart.id]"
:area-chart-options="$options.areaChartOptions" :area-chart-options="$options.areaChartOptions"
> >
{{ chart.title }} {{ chart.title }}
......
import dateFormat from 'dateformat';
import { s__, sprintf } from '~/locale';
import { nDaysBefore, nMonthsBefore } from '~/lib/utils/datetime_utility';
import { LAST_WEEK, LAST_MONTH, LAST_90_DAYS } from './constants';
// Compute all relative dates based on the _beginning_ of today
const startOfToday = new Date(new Date().setHours(0, 0, 0, 0));
const lastWeek = new Date(nDaysBefore(startOfToday, 7));
const lastMonth = new Date(nMonthsBefore(startOfToday, 1));
const last90Days = new Date(nDaysBefore(startOfToday, 90));
const apiDateFormatString = 'isoDateTime';
const titleDateFormatString = 'mmm d';
const sharedRequestParams = {
environment: 'production',
interval: 'daily',
// We will never have more than 91 records (1 record per day), so we
// don't have to worry about making multiple requests to get all the results
per_page: 100,
};
export const allChartDefinitions = [
{
id: LAST_WEEK,
title: sprintf(
s__(
'DeploymentFrequencyCharts|Deployments to production for last week (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(lastWeek, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
},
),
startDate: lastWeek,
requestParams: {
...sharedRequestParams,
from: dateFormat(lastWeek, apiDateFormatString),
},
},
{
id: LAST_MONTH,
title: sprintf(
s__(
'DeploymentFrequencyCharts|Deployments to production for last month (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(lastMonth, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
},
),
startDate: lastMonth,
requestParams: {
...sharedRequestParams,
from: dateFormat(lastMonth, apiDateFormatString),
},
},
{
id: LAST_90_DAYS,
title: sprintf(
s__(
'DeploymentFrequencyCharts|Deployments to production for the last 90 days (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(last90Days, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
},
),
startDate: last90Days,
requestParams: {
...sharedRequestParams,
from: dateFormat(last90Days, apiDateFormatString),
},
},
];
export const areaChartOptions = {
xAxis: {
name: s__('DeploymentFrequencyCharts|Date'),
type: 'category',
},
yAxis: {
name: s__('DeploymentFrequencyCharts|Deployments'),
type: 'value',
minInterval: 1,
},
};
import dateFormat from 'dateformat';
import { getDatesInRange } from '~/lib/utils/datetime_utility';
import { CHART_TITLE } from './constants';
/**
* Converts the raw data fetched from the
* [Deployment Frequency API](https://docs.gitlab.com/ee/api/project_analytics.html#list-project-deployment-frequencies)
* into series data consumable by
* [GlAreaChart](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/charts-area-chart--default)
*
* @param apiData The raw JSON data from the API request
* @param startDate The first day that should be rendered on the graph
*/
export const apiDataToChartSeries = (apiData, startDate) => {
// Get a list of dates (formatted identically to the dates in the API response),
// one date per day in the graph's date range
const dates = getDatesInRange(startDate, new Date(), (date) => dateFormat(date, 'yyyy-mm-dd'));
// Fill in the API data (the API data doesn't included data points for
// days with 0 deployments) and transform it for use in the graph
const data = dates.map((date) => {
const value = apiData.find((dataPoint) => dataPoint.from === date)?.value || 0;
const formattedDate = dateFormat(new Date(date), 'mmm d');
return [formattedDate, value];
});
return [
{
name: CHART_TITLE,
data,
},
];
};
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { useFakeDate } from 'helpers/fake_date'; import { useFakeDate } from 'helpers/fake_date';
import DeploymentFrequencyCharts from 'ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue';
import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue'; import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
...@@ -25,6 +24,18 @@ describe('ee_component/projects/pipelines/charts/components/deployment_frequency ...@@ -25,6 +24,18 @@ describe('ee_component/projects/pipelines/charts/components/deployment_frequency
// Set the current Date to the same value that is used when generating the fixtures // Set the current Date to the same value that is used when generating the fixtures
useFakeDate(2015, 6, 3, 10); useFakeDate(2015, 6, 3, 10);
let DeploymentFrequencyCharts;
// Import the component _after_ the date has been set using `useFakeDate`, so
// that any calls to `new Date()` during module initialization use the fake date
beforeAll(async () => {
DeploymentFrequencyCharts = (
await import(
'ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue'
)
).default;
});
let wrapper; let wrapper;
let mock; let mock;
......
import { useFakeDate } from 'helpers/fake_date';
import { apiDataToChartSeries } from 'ee/projects/pipelines/charts/components/util';
describe('ee/projects/pipelines/charts/components/util.js', () => {
useFakeDate(2015, 6, 3, 10);
describe('apiDataToChartSeries', () => {
it('transforms the data from the API into data the chart component can use', () => {
const apiData = [
{ value: 5, from: '2015-06-28', to: '2015-06-29' },
{ value: 1, from: '2015-06-29', to: '2015-06-30' },
{ value: 8, from: '2015-07-01', to: '2015-07-02' },
];
const startDate = new Date(2015, 5, 26, 10);
const expected = [
{
name: 'Deployments',
data: [
['Jun 26', 0],
['Jun 27', 0],
['Jun 28', 5],
['Jun 29', 1],
['Jun 30', 0],
['Jul 1', 8],
['Jul 2', 0],
['Jul 3', 0],
],
},
];
expect(apiDataToChartSeries(apiData, startDate)).toEqual(expected);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment