Commit 4ca4b22e authored by Nathan Friend's avatar Nathan Friend

Merge branch 'update-annotations-graphql-query' into 'master'

Update annotations graphql query

See merge request gitlab-org/gitlab!29676
parents f62f3979 4672e98f
......@@ -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