Commit 0f05882e authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch 'nfriend-update-deployment-frequency-frontend-date-format' into 'master'

Update deployment frequency graphs to use UTC dates

See merge request gitlab-org/gitlab!52919
parents 402059c7 fbc8a0c3
......@@ -38,10 +38,10 @@ export default {
},
async mounted() {
const results = await Promise.allSettled(
allChartDefinitions.map(async ({ id, requestParams, startDate }) => {
allChartDefinitions.map(async ({ id, requestParams, startDate, endDate }) => {
const { data: apiData } = await Api.deploymentFrequencies(this.projectPath, requestParams);
this.chartData[id] = apiDataToChartSeries(apiData, startDate);
this.chartData[id] = apiDataToChartSeries(apiData, startDate, endDate);
}),
);
......
import dateFormat from 'dateformat';
import { s__, sprintf } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import { nDaysBefore, nMonthsBefore } from '~/lib/utils/datetime_utility';
import { nDaysBefore, nMonthsBefore, getStartOfDay, dayAfter } 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 = nDaysBefore(startOfToday, 7);
const lastMonth = nMonthsBefore(startOfToday, 1);
const last90Days = nDaysBefore(startOfToday, 90);
// Compute all relative dates based on the _beginning_ of today.
// We use this date as the end date for the charts. This causes
// the current date to be the last day included in the graph.
const startOfToday = getStartOfDay(new Date(), { utc: true });
// We use this date as the "to" parameter for the API. This allows
// us to get deployment information about the current day.
const startOfTomorrow = dayAfter(startOfToday, { utc: true });
const lastWeek = nDaysBefore(startOfTomorrow, 7, { utc: true });
const lastMonth = nMonthsBefore(startOfTomorrow, 1, { utc: true });
const last90Days = nDaysBefore(startOfTomorrow, 90, { utc: true });
const apiDateFormatString = 'isoDateTime';
const titleDateFormatString = 'mmm d';
const sharedRequestParams = {
......@@ -28,14 +35,16 @@ export const allChartDefinitions = [
'DeploymentFrequencyCharts|Deployments to production for last week (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(lastWeek, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
startDate: dateFormat(lastWeek, titleDateFormatString, true),
endDate: dateFormat(startOfToday, titleDateFormatString, true),
},
),
startDate: lastWeek,
endDate: startOfTomorrow,
requestParams: {
...sharedRequestParams,
from: dateFormat(lastWeek, apiDateFormatString),
from: dateFormat(lastWeek, apiDateFormatString, true),
to: dateFormat(startOfTomorrow, apiDateFormatString, true),
},
},
{
......@@ -45,14 +54,16 @@ export const allChartDefinitions = [
'DeploymentFrequencyCharts|Deployments to production for last month (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(lastMonth, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
startDate: dateFormat(lastMonth, titleDateFormatString, true),
endDate: dateFormat(startOfToday, titleDateFormatString, true),
},
),
startDate: lastMonth,
endDate: startOfTomorrow,
requestParams: {
...sharedRequestParams,
from: dateFormat(lastMonth, apiDateFormatString),
from: dateFormat(lastMonth, apiDateFormatString, true),
to: dateFormat(startOfTomorrow, apiDateFormatString, true),
},
},
{
......@@ -62,14 +73,16 @@ export const allChartDefinitions = [
'DeploymentFrequencyCharts|Deployments to production for the last 90 days (%{startDate} - %{endDate})',
),
{
startDate: dateFormat(last90Days, titleDateFormatString),
endDate: dateFormat(startOfToday, titleDateFormatString),
startDate: dateFormat(last90Days, titleDateFormatString, true),
endDate: dateFormat(startOfToday, titleDateFormatString, true),
},
),
startDate: last90Days,
endDate: startOfTomorrow,
requestParams: {
...sharedRequestParams,
from: dateFormat(last90Days, apiDateFormatString),
from: dateFormat(last90Days, apiDateFormatString, true),
to: dateFormat(startOfTomorrow, apiDateFormatString, true),
},
},
];
......
import dateFormat from 'dateformat';
import { getDatesInRange } from '~/lib/utils/datetime_utility';
import { getDatesInRange, nDaysBefore, getStartOfDay } from '~/lib/utils/datetime_utility';
import { CHART_TITLE } from './constants';
/**
......@@ -8,20 +8,32 @@ import { CHART_TITLE } from './constants';
* 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
* @param {Array} apiData The raw JSON data from the API request
* @param {Date} startDate The first day (inclusive) of the graph's date range
* @param {Date} endDate The last day (exclusive) of the graph's date range
*/
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'));
export const apiDataToChartSeries = (apiData, startDate, endDate) => {
// Get a list of dates, one date per day in the graph's date range
const beginningOfStartDate = getStartOfDay(startDate, { utc: true });
const beginningOfEndDate = nDaysBefore(getStartOfDay(endDate, { utc: true }), 1, { utc: true });
const dates = getDatesInRange(beginningOfStartDate, beginningOfEndDate).map((d) =>
getStartOfDay(d, { utc: true }),
);
// Generate a map of API timestamps to its associated value.
// The timestamps are explicitly set to the _beginning_ of the day (in UTC)
// so that we can confidently compare dates by value below.
const timestampToApiValue = apiData.reduce((acc, curr) => {
const apiTimestamp = getStartOfDay(new Date(curr.from), { utc: true }).getTime();
acc[apiTimestamp] = curr.value;
return acc;
}, {});
// 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];
const formattedDate = dateFormat(date, 'mmm d', true);
return [formattedDate, timestampToApiValue[date.getTime()] || 0];
});
return [
......
---
title: Update Deployment Frequency graphs to always use UTC dates
merge_request: 52919
author:
type: changed
......@@ -44,7 +44,13 @@ RSpec.describe 'Project Analytics (JavaScript fixtures)' do
clean_frontend_fixtures('api/project_analytics/')
end
let(:shared_params) { { environment: environment.name, interval: 'daily' } }
let(:shared_params) do
{
environment: environment.name,
interval: 'daily',
to: Date.tomorrow.beginning_of_day
}
end
def make_request(additional_query_params:)
params = shared_params.merge(additional_query_params)
......
......@@ -2,10 +2,6 @@
exports[`ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue when there are no network errors converts the data from the API into data usable by the chart component 1`] = `
Array [
Array [
"Jun 26",
0,
],
Array [
"Jun 27",
0,
......@@ -34,19 +30,11 @@ Array [
"Jul 3",
1,
],
Array [
"Jul 4",
0,
],
]
`;
exports[`ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue when there are no network errors converts the data from the API into data usable by the chart component 2`] = `
Array [
Array [
"Jun 3",
0,
],
Array [
"Jun 4",
0,
......@@ -167,19 +155,11 @@ Array [
"Jul 3",
1,
],
Array [
"Jul 4",
0,
],
]
`;
exports[`ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue when there are no network errors converts the data from the API into data usable by the chart component 3`] = `
Array [
Array [
"Apr 4",
0,
],
Array [
"Apr 5",
0,
......@@ -540,9 +520,5 @@ Array [
"Jul 3",
1,
],
Array [
"Jul 4",
0,
],
]
`;
......@@ -58,6 +58,7 @@ describe('ee_component/projects/pipelines/charts/components/deployment_frequency
environment: 'production',
interval: 'daily',
per_page: 100,
to: '2015-07-04T00:00:00+0000',
from,
},
})
......@@ -77,9 +78,18 @@ describe('ee_component/projects/pipelines/charts/components/deployment_frequency
beforeEach(async () => {
mock = new MockAdapter(axios);
setUpMockDeploymentFrequencies({ from: '2015-06-26T00:00:00+0000', data: lastWeekData });
setUpMockDeploymentFrequencies({ from: '2015-06-03T00:00:00+0000', data: lastMonthData });
setUpMockDeploymentFrequencies({ from: '2015-04-04T00:00:00+0000', data: last90DaysData });
setUpMockDeploymentFrequencies({
from: '2015-06-27T00:00:00+0000',
data: lastWeekData,
});
setUpMockDeploymentFrequencies({
from: '2015-06-04T00:00:00+0000',
data: lastMonthData,
});
setUpMockDeploymentFrequencies({
from: '2015-04-05T00:00:00+0000',
data: last90DaysData,
});
createComponent();
......
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' },
// This is the date format we expect from the API
{ value: 5, from: '2015-06-28T00:00:00.000Z', to: '2015-06-29T00:00:00.000Z' },
// But we should support _any_ date format
{ value: 1, from: '2015-06-28T20:00:00.000-0400', to: '2015-06-19T20:00:00.000-0400' },
{ value: 8, from: '2015-07-01', to: '2015-07-02' },
];
const startDate = new Date(2015, 5, 26, 10);
const endDate = new Date(2015, 6, 4, 10);
const expected = [
{
......@@ -30,7 +31,7 @@ describe('ee/projects/pipelines/charts/components/util.js', () => {
},
];
expect(apiDataToChartSeries(apiData, startDate)).toEqual(expected);
expect(apiDataToChartSeries(apiData, startDate, endDate)).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