Commit 306c153f authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Jacques Erasmus

Retrieve selected value stream from BE

Builds the selectedValueStream data from
the BE initialized data
parent 8c66149b
...@@ -65,6 +65,7 @@ export default { ...@@ -65,6 +65,7 @@ export default {
'medians', 'medians',
'isLoadingValueStreams', 'isLoadingValueStreams',
'selectedStageError', 'selectedStageError',
'selectedValueStream',
]), ]),
// NOTE: formEvents are fetched in the same request as the list of stages (fetchGroupStagesAndEvents) // NOTE: formEvents are fetched in the same request as the list of stages (fetchGroupStagesAndEvents)
// so i think its ok to bind formEvents here even though its only used as a prop to the custom-stage-form // so i think its ok to bind formEvents here even though its only used as a prop to the custom-stage-form
...@@ -106,6 +107,7 @@ export default { ...@@ -106,6 +107,7 @@ export default {
const selectedProjectIds = this.selectedProjectIds?.length ? this.selectedProjectIds : null; const selectedProjectIds = this.selectedProjectIds?.length ? this.selectedProjectIds : null;
return { return {
value_stream_id: this.selectedValueStream?.id || null,
project_ids: selectedProjectIds, project_ids: selectedProjectIds,
created_after: toYmd(this.startDate), created_after: toYmd(this.startDate),
created_before: toYmd(this.endDate), created_before: toYmd(this.endDate),
......
...@@ -129,8 +129,8 @@ export default { ...@@ -129,8 +129,8 @@ export default {
isSelected(id) { isSelected(id) {
return Boolean(this.selectedValueStreamId && this.selectedValueStreamId === id); return Boolean(this.selectedValueStreamId && this.selectedValueStreamId === id);
}, },
onSelect(id) { onSelect(selectedId) {
this.setSelectedValueStream(id); this.setSelectedValueStream(this.data.find(({ id }) => id === selectedId));
}, },
onDelete() { onDelete() {
const name = this.selectedValueStreamName; const name = this.selectedValueStreamName;
......
...@@ -61,3 +61,5 @@ export const OVERVIEW_METRICS = { ...@@ -61,3 +61,5 @@ export const OVERVIEW_METRICS = {
TIME_SUMMARY: 'TIME_SUMMARY', TIME_SUMMARY: 'TIME_SUMMARY',
RECENT_ACTIVITY: 'RECENT_ACTIVITY', RECENT_ACTIVITY: 'RECENT_ACTIVITY',
}; };
export const FETCH_VALUE_STREAM_DATA = 'fetchValueStreamData';
...@@ -3,6 +3,7 @@ import { deprecatedCreateFlash as createFlash } from '~/flash'; ...@@ -3,6 +3,7 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { FETCH_VALUE_STREAM_DATA } from '../constants';
import { import {
removeFlash, removeFlash,
throwIfUserForbidden, throwIfUserForbidden,
...@@ -373,16 +374,19 @@ export const fetchValueStreamData = ({ dispatch }) => ...@@ -373,16 +374,19 @@ export const fetchValueStreamData = ({ dispatch }) =>
export const setSelectedValueStream = ({ commit, dispatch }, streamId) => { export const setSelectedValueStream = ({ commit, dispatch }, streamId) => {
commit(types.SET_SELECTED_VALUE_STREAM, streamId); commit(types.SET_SELECTED_VALUE_STREAM, streamId);
return dispatch('fetchValueStreamData'); return dispatch(FETCH_VALUE_STREAM_DATA);
}; };
export const receiveValueStreamsSuccess = ({ commit, dispatch }, data = []) => { export const receiveValueStreamsSuccess = (
{ state: { selectedValueStream = null }, commit, dispatch },
data = [],
) => {
commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data); commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data);
if (data.length) { if (!selectedValueStream && data.length) {
const [firstStream] = data; const [firstStream] = data;
return dispatch('setSelectedValueStream', firstStream.id); return dispatch('setSelectedValueStream', firstStream);
} }
return Promise.resolve(); return dispatch(FETCH_VALUE_STREAM_DATA);
}; };
export const fetchValueStreams = ({ commit, dispatch, getters, state }) => { export const fetchValueStreams = ({ commit, dispatch, getters, state }) => {
...@@ -404,7 +408,7 @@ export const fetchValueStreams = ({ commit, dispatch, getters, state }) => { ...@@ -404,7 +408,7 @@ export const fetchValueStreams = ({ commit, dispatch, getters, state }) => {
throw error; throw error;
}); });
} }
return dispatch('fetchValueStreamData'); return dispatch(FETCH_VALUE_STREAM_DATA);
}; };
export const setFilters = ({ dispatch }) => { export const setFilters = ({ dispatch }) => {
......
...@@ -91,11 +91,13 @@ export default { ...@@ -91,11 +91,13 @@ export default {
createdAfter: startDate = null, createdAfter: startDate = null,
createdBefore: endDate = null, createdBefore: endDate = null,
selectedProjects = [], selectedProjects = [],
selectedValueStream = {},
} = {}, } = {},
) { ) {
state.isLoading = true; state.isLoading = true;
state.currentGroup = group; state.currentGroup = group;
state.selectedProjects = selectedProjects; state.selectedProjects = selectedProjects;
state.selectedValueStream = selectedValueStream;
state.startDate = startDate; state.startDate = startDate;
state.endDate = endDate; state.endDate = endDate;
}, },
...@@ -138,8 +140,8 @@ export default { ...@@ -138,8 +140,8 @@ export default {
state.isDeletingValueStream = false; state.isDeletingValueStream = false;
state.deleteValueStreamError = null; state.deleteValueStreamError = null;
}, },
[types.SET_SELECTED_VALUE_STREAM](state, streamId) { [types.SET_SELECTED_VALUE_STREAM](state, valueStream) {
state.selectedValueStream = state.valueStreams?.find(({ id }) => id === streamId) || null; state.selectedValueStream = valueStream;
}, },
[types.REQUEST_VALUE_STREAMS](state) { [types.REQUEST_VALUE_STREAMS](state) {
state.isLoadingValueStreams = true; state.isLoadingValueStreams = true;
......
...@@ -10,6 +10,17 @@ export default { ...@@ -10,6 +10,17 @@ export default {
export const formattedDate = d => dateFormat(d, dateFormats.defaultDate); export const formattedDate = d => dateFormat(d, dateFormats.defaultDate);
/**
* Creates a value stream object from a dataset. Returns null if no valueStreamId is present.
*
* @param {Object} dataset - The raw value stream object
* @returns {Object} - A value stream object
*/
export const buildValueStreamFromJson = valueStream => {
const { id, name, is_custom: isCustom } = valueStream ? JSON.parse(valueStream) : {};
return id ? { id, name, isCustom } : null;
};
/** /**
* Creates a group object from a dataset. Returns null if no groupId is present. * Creates a group object from a dataset. Returns null if no groupId is present.
* *
...@@ -71,6 +82,7 @@ const buildProjectsFromJSON = (projects = []) => { ...@@ -71,6 +82,7 @@ const buildProjectsFromJSON = (projects = []) => {
* @returns {Object} - The initial data to load the app with * @returns {Object} - The initial data to load the app with
*/ */
export const buildCycleAnalyticsInitialData = ({ export const buildCycleAnalyticsInitialData = ({
valueStream = null,
groupId = null, groupId = null,
createdBefore = null, createdBefore = null,
createdAfter = null, createdAfter = null,
...@@ -82,6 +94,7 @@ export const buildCycleAnalyticsInitialData = ({ ...@@ -82,6 +94,7 @@ export const buildCycleAnalyticsInitialData = ({
labelsPath = '', labelsPath = '',
milestonesPath = '', milestonesPath = '',
} = {}) => ({ } = {}) => ({
selectedValueStream: buildValueStreamFromJson(valueStream),
group: groupId group: groupId
? convertObjectPropsToCamelCase( ? convertObjectPropsToCamelCase(
buildGroupFromDataset({ buildGroupFromDataset({
......
...@@ -69,7 +69,7 @@ module Gitlab ...@@ -69,7 +69,7 @@ module Gitlab
def to_data_attributes def to_data_attributes
{}.tap do |attrs| {}.tap do |attrs|
attrs[:group] = group_data_attributes if group attrs[:group] = group_data_attributes if group
attrs[:value_stream] = value_stream_data_attributes if value_stream attrs[:value_stream] = value_stream_data_attributes.to_json if value_stream
attrs[:created_after] = created_after.to_date.iso8601 attrs[:created_after] = created_after.to_date.iso8601
attrs[:created_before] = created_before.to_date.iso8601 attrs[:created_before] = created_before.to_date.iso8601
attrs[:projects] = group_projects(project_ids) if group && project_ids.present? attrs[:projects] = group_projects(project_ids) if group && project_ids.present?
...@@ -94,7 +94,9 @@ module Gitlab ...@@ -94,7 +94,9 @@ module Gitlab
def value_stream_data_attributes def value_stream_data_attributes
{ {
id: value_stream.id id: value_stream.id,
name: value_stream.name,
is_custom: value_stream.custom?
} }
end end
......
...@@ -52,7 +52,10 @@ const defaultFeatureFlags = { ...@@ -52,7 +52,10 @@ const defaultFeatureFlags = {
hasCreateMultipleValueStreams: false, hasCreateMultipleValueStreams: false,
}; };
const [selectedValueStream] = mockData.valueStreams;
const initialCycleAnalyticsState = { const initialCycleAnalyticsState = {
selectedValueStream,
createdAfter: mockData.startDate, createdAfter: mockData.startDate,
createdBefore: mockData.endDate, createdBefore: mockData.endDate,
group: currentGroup, group: currentGroup,
...@@ -627,6 +630,7 @@ describe('Cycle Analytics component', () => { ...@@ -627,6 +630,7 @@ describe('Cycle Analytics component', () => {
describe('Url parameters', () => { describe('Url parameters', () => {
const defaultParams = { const defaultParams = {
value_stream_id: selectedValueStream.id,
created_after: toYmd(mockData.startDate), created_after: toYmd(mockData.startDate),
created_before: toYmd(mockData.endDate), created_before: toYmd(mockData.endDate),
project_ids: null, project_ids: null,
...@@ -640,9 +644,6 @@ describe('Cycle Analytics component', () => { ...@@ -640,9 +644,6 @@ describe('Cycle Analytics component', () => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mockRequiredRoutes(mock); mockRequiredRoutes(mock);
wrapper = await createComponent();
await store.dispatch('initializeCycleAnalytics', initialCycleAnalyticsState);
}); });
afterEach(() => { afterEach(() => {
...@@ -651,12 +652,41 @@ describe('Cycle Analytics component', () => { ...@@ -651,12 +652,41 @@ describe('Cycle Analytics component', () => {
wrapper = null; wrapper = null;
}); });
it('sets the created_after and created_before url parameters', async () => { describe('with minimal parameters set set', () => {
await shouldMergeUrlParams(wrapper, defaultParams); beforeEach(async () => {
wrapper = await createComponent();
await store.dispatch('initializeCycleAnalytics', {
...initialCycleAnalyticsState,
selectedValueStream: null,
});
});
it('sets the created_after and created_before url parameters', async () => {
await shouldMergeUrlParams(wrapper, defaultParams);
});
});
describe('with selectedValueStream set', () => {
beforeEach(async () => {
wrapper = await createComponent();
await store.dispatch('initializeCycleAnalytics', initialCycleAnalyticsState);
await wrapper.vm.$nextTick();
});
it('sets the value_stream_id url parameter', async () => {
await shouldMergeUrlParams(wrapper, {
...defaultParams,
created_after: toYmd(mockData.startDate),
created_before: toYmd(mockData.endDate),
project_ids: null,
});
});
}); });
describe('with selectedProjectIds set', () => { describe('with selectedProjectIds set', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = await createComponent();
store.dispatch('setSelectedProjects', mockData.selectedProjects); store.dispatch('setSelectedProjects', mockData.selectedProjects);
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
}); });
......
...@@ -1047,7 +1047,7 @@ describe('Cycle analytics actions', () => { ...@@ -1047,7 +1047,7 @@ describe('Cycle analytics actions', () => {
}); });
describe('receiveValueStreamsSuccess', () => { describe('receiveValueStreamsSuccess', () => {
it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation`, () => { it(`with a selectedValueStream in state commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'fetchValueStreamData'`, () => {
return testAction( return testAction(
actions.receiveValueStreamsSuccess, actions.receiveValueStreamsSuccess,
valueStreams, valueStreams,
...@@ -1058,7 +1058,25 @@ describe('Cycle analytics actions', () => { ...@@ -1058,7 +1058,25 @@ describe('Cycle analytics actions', () => {
payload: valueStreams, payload: valueStreams,
}, },
], ],
[{ type: 'setSelectedValueStream', payload: selectedValueStream.id }], [{ type: 'fetchValueStreamData' }],
);
});
it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'setSelectedValueStream'`, () => {
return testAction(
actions.receiveValueStreamsSuccess,
valueStreams,
{
...state,
selectedValueStream: null,
},
[
{
type: types.RECEIVE_VALUE_STREAMS_SUCCESS,
payload: valueStreams,
},
],
[{ type: 'setSelectedValueStream', payload: selectedValueStream }],
); );
}); });
}); });
......
...@@ -96,9 +96,9 @@ describe('Cycle analytics mutations', () => { ...@@ -96,9 +96,9 @@ describe('Cycle analytics mutations', () => {
describe('with value streams available', () => { describe('with value streams available', () => {
it.each` it.each`
mutation | payload | expectedState mutation | payload | expectedState
${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1].id} | ${{ selectedValueStream: valueStreams[1] }} ${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1]} | ${{ selectedValueStream: valueStreams[1] }}
${types.SET_SELECTED_VALUE_STREAM} | ${'fake-id'} | ${{ selectedValueStream: {} }} ${types.SET_SELECTED_VALUE_STREAM} | ${'fake-id'} | ${{ selectedValueStream: {} }}
`( `(
'$mutation with payload $payload will update state with $expectedState', '$mutation with payload $payload will update state with $expectedState',
({ mutation, payload, expectedState }) => { ({ mutation, payload, expectedState }) => {
......
...@@ -5,6 +5,12 @@ import { ...@@ -5,6 +5,12 @@ import {
filterBySearchTerm, filterBySearchTerm,
} from 'ee/analytics/shared/utils'; } from 'ee/analytics/shared/utils';
const rawValueStream = `{
"id": 1,
"name": "Custom value stream 1",
"is_custom": true
}`;
const groupDataset = { const groupDataset = {
groupId: '1', groupId: '1',
groupName: 'My Group', groupName: 'My Group',
...@@ -27,13 +33,13 @@ const projectDataset = { ...@@ -27,13 +33,13 @@ const projectDataset = {
projectPathWithNamespace: 'my-group/my-project', projectPathWithNamespace: 'my-group/my-project',
}; };
const rawProjects = JSON.stringify([ const rawProjects = `[
{ {
project_id: '1', "project_id": "1",
project_name: 'My Project', "project_name": "My Project",
project_path_with_namespace: 'my-group/my-project', "project_path_with_namespace": "my-group/my-project"
}, }
]); ]`;
describe('buildGroupFromDataset', () => { describe('buildGroupFromDataset', () => {
it('returns null if groupId is missing', () => { it('returns null if groupId is missing', () => {
...@@ -90,6 +96,28 @@ describe('buildCycleAnalyticsInitialData', () => { ...@@ -90,6 +96,28 @@ describe('buildCycleAnalyticsInitialData', () => {
}); });
}); });
describe('value stream', () => {
it('will be set given an array of projects', () => {
expect(buildCycleAnalyticsInitialData({ valueStream: rawValueStream })).toMatchObject({
selectedValueStream: {
id: 1,
name: 'Custom value stream 1',
isCustom: true,
},
});
});
it.each`
value
${null}
${''}
`('will be null if given a value of `$value`', ({ value }) => {
expect(buildCycleAnalyticsInitialData({ valueStream: value })).toMatchObject({
selectedValueStream: null,
});
});
});
describe('group', () => { describe('group', () => {
it("will be set given a valid 'groupId' and all group parameters", () => { it("will be set given a valid 'groupId' and all group parameters", () => {
expect(buildCycleAnalyticsInitialData(groupDataset)).toMatchObject({ expect(buildCycleAnalyticsInitialData(groupDataset)).toMatchObject({
......
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