Commit bb444daf authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '34021-environments-dropdown-add-backend-support' into 'master'

Use GraphQL endpoint for environments dropdown in monitoring dashboard 2/4

See merge request gitlab-org/gitlab!23632
parents 5e1a685d b6392023
/**
* Ids generated by GraphQL endpoints are usually in the format
* gid://gitlab/Environments/123. This method extracts Id number
* from the Id path
*
* @param {String} gid GraphQL global ID
* @returns {Number}
*/
export const getIdFromGraphQLId = (gid = '') =>
parseInt((gid || '').replace(/gid:\/\/gitlab\/.*\//g, ''), 10) || null;
export default {};
query getEnvironments($projectPath: ID!, $search: String) {
project(fullPath: $projectPath) {
data: environments(search: $search) {
environments: nodes {
name
id
}
}
}
}
import * as types from './mutation_types'; import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils';
import trackDashboardLoad from '../monitoring_tracking_helper'; import trackDashboardLoad from '../monitoring_tracking_helper';
import getEnvironments from '../queries/getEnvironments.query.graphql';
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils'; import { backOff } from '../../lib/utils/common_utils';
import { s__, sprintf } from '../../locale'; import { s__, sprintf } from '../../locale';
...@@ -187,26 +189,30 @@ export const fetchDeploymentsData = ({ state, dispatch }) => { ...@@ -187,26 +189,30 @@ export const fetchDeploymentsData = ({ state, dispatch }) => {
}); });
}; };
export const fetchEnvironmentsData = ({ state, dispatch }) => { export const fetchEnvironmentsData = ({ state, dispatch }) =>
if (!state.environmentsEndpoint) { gqClient
return Promise.resolve([]); .mutate({
} mutation: getEnvironments,
return axios variables: {
.get(state.environmentsEndpoint) projectPath: removeLeadingSlash(state.projectPath),
.then(resp => resp.data) search: state.environmentsSearchTerm,
.then(response => { },
if (!response || !response.environments) { })
.then(resp =>
parseEnvironmentsResponse(resp.data?.project?.data?.environments, state.projectPath),
)
.then(environments => {
if (!environments) {
createFlash( createFlash(
s__('Metrics|There was an error fetching the environments data, please try again'), s__('Metrics|There was an error fetching the environments data, please try again'),
); );
} }
dispatch('receiveEnvironmentsDataSuccess', response.environments); dispatch('receiveEnvironmentsDataSuccess', environments);
}) })
.catch(() => { .catch(() => {
dispatch('receiveEnvironmentsDataFailure'); dispatch('receiveEnvironmentsDataFailure');
createFlash(s__('Metrics|There was an error getting environments information.')); createFlash(s__('Metrics|There was an error getting environments information.'));
}); });
};
/** /**
* Set a new array of metrics to a panel group * Set a new array of metrics to a panel group
......
import { omit } from 'lodash'; import { omit } from 'lodash';
import createGqClient from '~/lib/graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
export const gqClient = createGqClient();
export const uniqMetricsId = metric => `${metric.metric_id}_${metric.id}`; export const uniqMetricsId = metric => `${metric.metric_id}_${metric.id}`;
/**
* Project path has a leading slash that doesn't work well
* with project full path resolver here
* https://gitlab.com/gitlab-org/gitlab/blob/5cad4bd721ab91305af4505b2abc92b36a56ad6b/app/graphql/resolvers/full_path_resolver.rb#L10
*
* @param {String} str String with leading slash
* @returns {String}
*/
export const removeLeadingSlash = str => (str || '').replace(/^\/+/, '');
/**
* GraphQL environments API returns only id and name.
* For the environments dropdown we need metrics_path.
* This method parses the results and add neccessart attrs
*
* @param {Array} response Environments API result
* @param {String} projectPath Current project path
* @returns {Array}
*/
export const parseEnvironmentsResponse = (response = [], projectPath) =>
(response || []).map(env => {
const id = getIdFromGraphQLId(env.id);
return {
...env,
id,
metrics_path: `${projectPath}/environments/${id}/metrics`,
};
});
/** /**
* Metrics loaded from project-defined dashboards do not have a metric_id. * Metrics loaded from project-defined dashboards do not have a metric_id.
* This method creates a unique ID combining metric_id and id, if either is present. * This method creates a unique ID combining metric_id and id, if either is present.
......
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
describe('getIdFromGraphQLId', () => {
[
{
input: '',
output: null,
},
{
input: null,
output: null,
},
{
input: 'gid://',
output: null,
},
{
input: 'gid://gitlab/',
output: null,
},
{
input: 'gid://gitlab/Environments',
output: null,
},
{
input: 'gid://gitlab/Environments/',
output: null,
},
{
input: 'gid://gitlab/Environments/123',
output: 123,
},
{
input: 'gid://gitlab/DesignManagement::Version/2',
output: 2,
},
].forEach(({ input, output }) => {
it(`getIdFromGraphQLId returns ${output} when passed ${input}`, () => {
expect(getIdFromGraphQLId(input)).toBe(output);
});
});
});
...@@ -332,7 +332,7 @@ export const mockedQueryResultPayloadCoresTotal = { ...@@ -332,7 +332,7 @@ export const mockedQueryResultPayloadCoresTotal = {
}; };
const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({ const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({
id: 136 + idx, id: `gid://gitlab/Environments/${150 + idx}`,
name: `no-deployment/noop-branch-${idx}`, name: `no-deployment/noop-branch-${idx}`,
state: 'available', state: 'available',
created_at: '2018-07-04T18:39:41.702Z', created_at: '2018-07-04T18:39:41.702Z',
...@@ -341,7 +341,7 @@ const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({ ...@@ -341,7 +341,7 @@ const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({
export const environmentData = [ export const environmentData = [
{ {
id: 34, id: 'gid://gitlab/Environments/34',
name: 'production', name: 'production',
state: 'available', state: 'available',
external_url: 'http://root-autodevops-deploy.my-fake-domain.com', external_url: 'http://root-autodevops-deploy.my-fake-domain.com',
...@@ -359,7 +359,7 @@ export const environmentData = [ ...@@ -359,7 +359,7 @@ export const environmentData = [
}, },
}, },
{ {
id: 35, id: 'gid://gitlab/Environments/35',
name: 'review/noop-branch', name: 'review/noop-branch',
state: 'available', state: 'available',
external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com', external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com',
......
...@@ -20,6 +20,7 @@ import { ...@@ -20,6 +20,7 @@ import {
setGettingStartedEmptyState, setGettingStartedEmptyState,
duplicateSystemDashboard, duplicateSystemDashboard,
} from '~/monitoring/stores/actions'; } from '~/monitoring/stores/actions';
import { gqClient, parseEnvironmentsResponse } from '~/monitoring/stores/utils';
import storeState from '~/monitoring/stores/state'; import storeState from '~/monitoring/stores/state';
import { import {
deploymentData, deploymentData,
...@@ -105,37 +106,46 @@ describe('Monitoring store actions', () => { ...@@ -105,37 +106,46 @@ describe('Monitoring store actions', () => {
}); });
}); });
describe('fetchEnvironmentsData', () => { describe('fetchEnvironmentsData', () => {
it('commits RECEIVE_ENVIRONMENTS_DATA_SUCCESS on error', done => { it('commits RECEIVE_ENVIRONMENTS_DATA_SUCCESS on error', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const { state } = store; const { state } = store;
state.environmentsEndpoint = '/success'; state.projectPath = '/gitlab-org/gitlab-test';
mock.onGet(state.environmentsEndpoint).reply(200, {
jest.spyOn(gqClient, 'mutate').mockReturnValue(
Promise.resolve({
data: {
project: {
data: {
environments: environmentData, environments: environmentData,
}); },
fetchEnvironmentsData({ },
},
}),
);
return fetchEnvironmentsData({
state, state,
dispatch, dispatch,
}) }).then(() => {
.then(() => { expect(dispatch).toHaveBeenCalledWith(
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataSuccess', environmentData); 'receiveEnvironmentsDataSuccess',
done(); parseEnvironmentsResponse(environmentData, state.projectPath),
}) );
.catch(done.fail); });
}); });
it('commits RECEIVE_ENVIRONMENTS_DATA_FAILURE on error', done => {
it('commits RECEIVE_ENVIRONMENTS_DATA_FAILURE on error', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
const { state } = store; const { state } = store;
state.environmentsEndpoint = '/error'; state.projectPath = '/gitlab-org/gitlab-test';
mock.onGet(state.environmentsEndpoint).reply(500); jest.spyOn(gqClient, 'mutate').mockReturnValue(Promise.reject());
fetchEnvironmentsData({
return fetchEnvironmentsData({
state, state,
dispatch, dispatch,
}) }).then(() => {
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataFailure'); expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataFailure');
done(); });
})
.catch(done.fail);
}); });
}); });
describe('Set endpoints', () => { describe('Set endpoints', () => {
......
import { normalizeMetric, uniqMetricsId } from '~/monitoring/stores/utils'; import {
normalizeMetric,
uniqMetricsId,
parseEnvironmentsResponse,
removeLeadingSlash,
} from '~/monitoring/stores/utils';
const projectPath = 'gitlab-org/gitlab-test';
describe('normalizeMetric', () => { describe('normalizeMetric', () => {
[ [
...@@ -32,3 +39,71 @@ describe('uniqMetricsId', () => { ...@@ -32,3 +39,71 @@ describe('uniqMetricsId', () => {
}); });
}); });
}); });
describe('parseEnvironmentsResponse', () => {
[
{
input: null,
output: [],
},
{
input: undefined,
output: [],
},
{
input: [],
output: [],
},
{
input: [
{
id: '1',
name: 'env-1',
},
],
output: [
{
id: 1,
name: 'env-1',
metrics_path: `${projectPath}/environments/1/metrics`,
},
],
},
{
input: [
{
id: 'gid://gitlab/Environment/12',
name: 'env-12',
},
],
output: [
{
id: 12,
name: 'env-12',
metrics_path: `${projectPath}/environments/12/metrics`,
},
],
},
].forEach(({ input, output }) => {
it(`parseEnvironmentsResponse returns ${JSON.stringify(output)} with input ${JSON.stringify(
input,
)}`, () => {
expect(parseEnvironmentsResponse(input, projectPath)).toEqual(output);
});
});
});
describe('removeLeadingSlash', () => {
[
{ input: null, output: '' },
{ input: '', output: '' },
{ input: 'gitlab-org', output: 'gitlab-org' },
{ input: 'gitlab-org/gitlab', output: 'gitlab-org/gitlab' },
{ input: '/gitlab-org/gitlab', output: 'gitlab-org/gitlab' },
{ input: '////gitlab-org/gitlab', output: 'gitlab-org/gitlab' },
].forEach(({ input, output }) => {
it(`removeLeadingSlash returns ${output} with input ${input}`, () => {
expect(removeLeadingSlash(input)).toEqual(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