Commit 4672e98f authored by Dhiraj Bodicherla's avatar Dhiraj Bodicherla

Update annotations fetch graphql query

This MR updates annotations fetch graphql
query on the FE and the response parsing
logic. This MR also updates all the specs
parent ee73f5f7
......@@ -45,9 +45,9 @@ export const annotationsYAxis = {
* Fetched list of annotations are parsed into a
* format the eCharts accepts to draw markLines
*
* If Annotation is a single line, the `starting_at` property
* has a value and the `ending_at` is null. Because annotations
* only supports lines the `ending_at` value does not exist yet.
* If Annotation is a single line, the `startingAt` property
* has a value and the `endingAt` is null. Because annotations
* only supports lines the `endingAt` value does not exist yet.
*
* @param {Object} annotation object
* @returns {Object} markLine object
......@@ -56,7 +56,7 @@ export const parseAnnotations = annotations =>
annotations.reduce(
(acc, annotation) => {
acc.lines.push({
xAxis: annotation.starting_at,
xAxis: annotation.startingAt,
lineStyle: {
color: colorValues.primaryColor,
},
......@@ -64,10 +64,10 @@ export const parseAnnotations = annotations =>
acc.points.push({
name: 'annotations',
xAxis: annotation.starting_at,
xAxis: annotation.startingAt,
yAxis: annotationsYAxisCoords.min,
tooltipData: {
title: annotation.starting_at,
title: annotation.startingAt,
content: annotation.description,
},
});
......
......@@ -131,3 +131,15 @@ export const ENVIRONMENT_AVAILABLE_STATE = 'available';
* https://gitlab.com/gitlab-org/gitlab/-/issues/214540
*/
export const annotationsSymbolIcon = 'path://m5 229 5 8h-10z';
/**
* As of %12.10, dashboard path is required to create annotation.
* The FE gets the dashboard name from the URL params. It is not
* ideal to store the path this way but there is no other way to
* get this path unless annotations fetch is delayed. This could
* potentially be removed and have the backend send this to the FE.
*
* This technical debt is being tracked here
* https://gitlab.com/gitlab-org/gitlab/-/issues/214671
*/
export const DEFAULT_DASHBOARD_PATH = 'config/prometheus/common_metrics.yml';
query getAnnotations($projectPath: ID!) {
environment(name: $environmentName) {
metricDashboard(id: $dashboardId) {
annotations: nodes {
query getAnnotations(
$projectPath: ID!
$environmentName: String
$dashboardPath: String!
$startingFrom: Time!
) {
project(fullPath: $projectPath) {
environments(name: $environmentName) {
nodes {
id
description
starting_at
ending_at
panelId
name
metricsDashboard(path: $dashboardPath) {
annotations(from: $startingFrom) {
nodes {
id
description
startingAt
endingAt
panelId
}
}
}
}
}
}
......
......@@ -3,7 +3,12 @@ import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils';
import {
gqClient,
parseEnvironmentsResponse,
parseAnnotationsResponse,
removeLeadingSlash,
} from './utils';
import trackDashboardLoad from '../monitoring_tracking_helper';
import getEnvironments from '../queries/getEnvironments.query.graphql';
import getAnnotations from '../queries/getAnnotations.query.graphql';
......@@ -15,7 +20,11 @@ import {
} from '../../lib/utils/common_utils';
import { s__, sprintf } from '../../locale';
import { PROMETHEUS_TIMEOUT, ENVIRONMENT_AVAILABLE_STATE } from '../constants';
import {
PROMETHEUS_TIMEOUT,
ENVIRONMENT_AVAILABLE_STATE,
DEFAULT_DASHBOARD_PATH,
} from '../constants';
function prometheusMetricQueryParams(timeRange) {
const { start, end } = convertToFixedRange(timeRange);
......@@ -283,16 +292,21 @@ export const receiveEnvironmentsDataFailure = ({ commit }) => {
};
export const fetchAnnotations = ({ state, dispatch }) => {
const { start } = convertToFixedRange(state.timeRange);
const dashboardPath =
state.currentDashboard === '' ? DEFAULT_DASHBOARD_PATH : state.currentDashboard;
return gqClient
.mutate({
mutation: getAnnotations,
variables: {
projectPath: removeLeadingSlash(state.projectPath),
dashboardId: state.currentDashboard,
environmentName: state.currentEnvironmentName,
dashboardPath,
startingFrom: start,
},
})
.then(resp => resp.data?.project?.environment?.metricDashboard?.annotations)
.then(resp => resp.data?.project?.environments?.nodes?.[0].metricsDashboard?.annotations.nodes)
.then(parseAnnotationsResponse)
.then(annotations => {
if (!annotations) {
createFlash(s__('Metrics|There was an error fetching annotations. Please try again.'));
......
......@@ -57,6 +57,31 @@ export const parseEnvironmentsResponse = (response = [], projectPath) =>
};
});
/**
* Annotation API returns time in UTC. This method
* converts time to local time.
*
* startingAt always exists but endingAt does not.
* If endingAt does not exist, a threshold line is
* drawn.
*
* If endingAt exists, a threshold range is drawn.
* But this is not supported as of %12.10
*
* @param {Array} response annotations response
* @returns {Array} parsed responses
*/
export const parseAnnotationsResponse = response => {
if (!response) {
return [];
}
return response.map(annotation => ({
...annotation,
startingAt: new Date(annotation.startingAt),
endingAt: annotation.endingAt ? new Date(annotation.endingAt) : null,
}));
};
/**
* Maps metrics to its view model
*
......
......@@ -247,23 +247,23 @@ export const deploymentData = [
export const annotationsData = [
{
id: 'gid://gitlab/Metrics::Dashboard::Annotation/1',
starting_at: '2020-04-01T12:51:58.373Z',
ending_at: null,
startingAt: '2020-04-12 12:51:53 UTC',
endingAt: null,
panelId: null,
description: 'This is a test annotation',
},
{
id: 'gid://gitlab/Metrics::Dashboard::Annotation/2',
description: 'test annotation 2',
starting_at: '2020-04-02T12:51:58.373Z',
ending_at: null,
startingAt: '2020-04-13 12:51:53 UTC',
endingAt: null,
panelId: null,
},
{
id: 'gid://gitlab/Metrics::Dashboard::Annotation/3',
description: 'test annotation 3',
starting_at: '2020-04-04T12:51:58.373Z',
ending_at: null,
startingAt: '2020-04-16 12:51:53 UTC',
endingAt: null,
panelId: null,
},
];
......
......@@ -23,7 +23,11 @@ import {
setGettingStartedEmptyState,
duplicateSystemDashboard,
} from '~/monitoring/stores/actions';
import { gqClient, parseEnvironmentsResponse } from '~/monitoring/stores/utils';
import {
gqClient,
parseEnvironmentsResponse,
parseAnnotationsResponse,
} from '~/monitoring/stores/utils';
import getEnvironments from '~/monitoring/queries/getEnvironments.query.graphql';
import getAnnotations from '~/monitoring/queries/getAnnotations.query.graphql';
import storeState from '~/monitoring/stores/state';
......@@ -224,6 +228,10 @@ describe('Monitoring store actions', () => {
describe('fetchAnnotations', () => {
const { state } = store;
state.timeRange = {
start: '2020-04-15T12:54:32.137Z',
end: '2020-08-15T12:54:32.137Z',
};
state.projectPath = 'gitlab-org/gitlab-test';
state.currentEnvironmentName = 'production';
state.currentDashboard = '.gitlab/dashboards/custom_dashboard.yml';
......@@ -239,17 +247,25 @@ describe('Monitoring store actions', () => {
variables: {
projectPath: state.projectPath,
environmentName: state.currentEnvironmentName,
dashboardId: state.currentDashboard,
dashboardPath: state.currentDashboard,
startingFrom: state.timeRange.start,
},
};
const parsedResponse = parseAnnotationsResponse(annotationsData);
mockMutate.mockResolvedValue({
data: {
project: {
environment: {
metricDashboard: {
annotations: annotationsData,
},
environments: {
nodes: [
{
metricsDashboard: {
annotations: {
nodes: parsedResponse,
},
},
},
],
},
},
},
......@@ -260,7 +276,7 @@ describe('Monitoring store actions', () => {
null,
state,
[],
[{ type: 'receiveAnnotationsSuccess', payload: annotationsData }],
[{ type: 'receiveAnnotationsSuccess', payload: parsedResponse }],
() => {
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
},
......@@ -274,7 +290,8 @@ describe('Monitoring store actions', () => {
variables: {
projectPath: state.projectPath,
environmentName: state.currentEnvironmentName,
dashboardId: state.currentDashboard,
dashboardPath: state.currentDashboard,
startingFrom: state.timeRange.start,
},
};
......
......@@ -2,9 +2,11 @@ import { SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
import {
uniqMetricsId,
parseEnvironmentsResponse,
parseAnnotationsResponse,
removeLeadingSlash,
mapToDashboardViewModel,
} from '~/monitoring/stores/utils';
import { annotationsData } from '../mock_data';
import { NOT_IN_DB_PREFIX } from '~/monitoring/constants';
const projectPath = 'gitlab-org/gitlab-test';
......@@ -376,6 +378,27 @@ describe('parseEnvironmentsResponse', () => {
});
});
describe('parseAnnotationsResponse', () => {
const parsedAnnotationResponse = [
{
description: 'This is a test annotation',
endingAt: null,
id: 'gid://gitlab/Metrics::Dashboard::Annotation/1',
panelId: null,
startingAt: new Date('2020-04-12T12:51:53.000Z'),
},
];
it.each`
case | input | expected
${'Returns empty array for null input'} | ${null} | ${[]}
${'Returns empty array for undefined input'} | ${undefined} | ${[]}
${'Returns empty array for empty input'} | ${[]} | ${[]}
${'Returns parsed responses for annotations data'} | ${[annotationsData[0]]} | ${parsedAnnotationResponse}
`('$case', ({ input, expected }) => {
expect(parseAnnotationsResponse(input)).toEqual(expected);
});
});
describe('removeLeadingSlash', () => {
[
{ input: null, output: '' },
......
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