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 axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils';
import trackDashboardLoad from '../monitoring_tracking_helper';
import getEnvironments from '../queries/getEnvironments.query.graphql';
import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils';
import { s__, sprintf } from '../../locale';
......@@ -187,26 +189,30 @@ export const fetchDeploymentsData = ({ state, dispatch }) => {
});
};
export const fetchEnvironmentsData = ({ state, dispatch }) => {
if (!state.environmentsEndpoint) {
return Promise.resolve([]);
}
return axios
.get(state.environmentsEndpoint)
.then(resp => resp.data)
.then(response => {
if (!response || !response.environments) {
export const fetchEnvironmentsData = ({ state, dispatch }) =>
gqClient
.mutate({
mutation: getEnvironments,
variables: {
projectPath: removeLeadingSlash(state.projectPath),
search: state.environmentsSearchTerm,
},
})
.then(resp =>
parseEnvironmentsResponse(resp.data?.project?.data?.environments, state.projectPath),
)
.then(environments => {
if (!environments) {
createFlash(
s__('Metrics|There was an error fetching the environments data, please try again'),
);
}
dispatch('receiveEnvironmentsDataSuccess', response.environments);
dispatch('receiveEnvironmentsDataSuccess', environments);
})
.catch(() => {
dispatch('receiveEnvironmentsDataFailure');
createFlash(s__('Metrics|There was an error getting environments information.'));
});
};
/**
* Set a new array of metrics to a panel group
......
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}`;
/**
* 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.
* 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 = {
};
const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({
id: 136 + idx,
id: `gid://gitlab/Environments/${150 + idx}`,
name: `no-deployment/noop-branch-${idx}`,
state: 'available',
created_at: '2018-07-04T18:39:41.702Z',
......@@ -341,7 +341,7 @@ const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({
export const environmentData = [
{
id: 34,
id: 'gid://gitlab/Environments/34',
name: 'production',
state: 'available',
external_url: 'http://root-autodevops-deploy.my-fake-domain.com',
......@@ -359,7 +359,7 @@ export const environmentData = [
},
},
{
id: 35,
id: 'gid://gitlab/Environments/35',
name: 'review/noop-branch',
state: 'available',
external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com',
......
......@@ -20,6 +20,7 @@ import {
setGettingStartedEmptyState,
duplicateSystemDashboard,
} from '~/monitoring/stores/actions';
import { gqClient, parseEnvironmentsResponse } from '~/monitoring/stores/utils';
import storeState from '~/monitoring/stores/state';
import {
deploymentData,
......@@ -105,37 +106,46 @@ describe('Monitoring store actions', () => {
});
});
describe('fetchEnvironmentsData', () => {
it('commits RECEIVE_ENVIRONMENTS_DATA_SUCCESS on error', done => {
it('commits RECEIVE_ENVIRONMENTS_DATA_SUCCESS on error', () => {
const dispatch = jest.fn();
const { state } = store;
state.environmentsEndpoint = '/success';
mock.onGet(state.environmentsEndpoint).reply(200, {
environments: environmentData,
});
fetchEnvironmentsData({
state.projectPath = '/gitlab-org/gitlab-test';
jest.spyOn(gqClient, 'mutate').mockReturnValue(
Promise.resolve({
data: {
project: {
data: {
environments: environmentData,
},
},
},
}),
);
return fetchEnvironmentsData({
state,
dispatch,
})
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataSuccess', environmentData);
done();
})
.catch(done.fail);
}).then(() => {
expect(dispatch).toHaveBeenCalledWith(
'receiveEnvironmentsDataSuccess',
parseEnvironmentsResponse(environmentData, state.projectPath),
);
});
});
it('commits RECEIVE_ENVIRONMENTS_DATA_FAILURE on error', done => {
it('commits RECEIVE_ENVIRONMENTS_DATA_FAILURE on error', () => {
const dispatch = jest.fn();
const { state } = store;
state.environmentsEndpoint = '/error';
mock.onGet(state.environmentsEndpoint).reply(500);
fetchEnvironmentsData({
state.projectPath = '/gitlab-org/gitlab-test';
jest.spyOn(gqClient, 'mutate').mockReturnValue(Promise.reject());
return fetchEnvironmentsData({
state,
dispatch,
})
.then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataFailure');
done();
})
.catch(done.fail);
}).then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataFailure');
});
});
});
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', () => {
[
......@@ -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