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 {
'medians',
'isLoadingValueStreams',
'selectedStageError',
'selectedValueStream',
]),
// 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
......@@ -106,6 +107,7 @@ export default {
const selectedProjectIds = this.selectedProjectIds?.length ? this.selectedProjectIds : null;
return {
value_stream_id: this.selectedValueStream?.id || null,
project_ids: selectedProjectIds,
created_after: toYmd(this.startDate),
created_before: toYmd(this.endDate),
......
......@@ -129,8 +129,8 @@ export default {
isSelected(id) {
return Boolean(this.selectedValueStreamId && this.selectedValueStreamId === id);
},
onSelect(id) {
this.setSelectedValueStream(id);
onSelect(selectedId) {
this.setSelectedValueStream(this.data.find(({ id }) => id === selectedId));
},
onDelete() {
const name = this.selectedValueStreamName;
......
......@@ -61,3 +61,5 @@ export const OVERVIEW_METRICS = {
TIME_SUMMARY: 'TIME_SUMMARY',
RECENT_ACTIVITY: 'RECENT_ACTIVITY',
};
export const FETCH_VALUE_STREAM_DATA = 'fetchValueStreamData';
......@@ -3,6 +3,7 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __, sprintf } from '~/locale';
import httpStatus from '~/lib/utils/http_status';
import * as types from './mutation_types';
import { FETCH_VALUE_STREAM_DATA } from '../constants';
import {
removeFlash,
throwIfUserForbidden,
......@@ -373,16 +374,19 @@ export const fetchValueStreamData = ({ dispatch }) =>
export const setSelectedValueStream = ({ commit, dispatch }, 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);
if (data.length) {
if (!selectedValueStream && data.length) {
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 }) => {
......@@ -404,7 +408,7 @@ export const fetchValueStreams = ({ commit, dispatch, getters, state }) => {
throw error;
});
}
return dispatch('fetchValueStreamData');
return dispatch(FETCH_VALUE_STREAM_DATA);
};
export const setFilters = ({ dispatch }) => {
......
......@@ -91,11 +91,13 @@ export default {
createdAfter: startDate = null,
createdBefore: endDate = null,
selectedProjects = [],
selectedValueStream = {},
} = {},
) {
state.isLoading = true;
state.currentGroup = group;
state.selectedProjects = selectedProjects;
state.selectedValueStream = selectedValueStream;
state.startDate = startDate;
state.endDate = endDate;
},
......@@ -138,8 +140,8 @@ export default {
state.isDeletingValueStream = false;
state.deleteValueStreamError = null;
},
[types.SET_SELECTED_VALUE_STREAM](state, streamId) {
state.selectedValueStream = state.valueStreams?.find(({ id }) => id === streamId) || null;
[types.SET_SELECTED_VALUE_STREAM](state, valueStream) {
state.selectedValueStream = valueStream;
},
[types.REQUEST_VALUE_STREAMS](state) {
state.isLoadingValueStreams = true;
......
......@@ -10,6 +10,17 @@ export default {
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.
*
......@@ -71,6 +82,7 @@ const buildProjectsFromJSON = (projects = []) => {
* @returns {Object} - The initial data to load the app with
*/
export const buildCycleAnalyticsInitialData = ({
valueStream = null,
groupId = null,
createdBefore = null,
createdAfter = null,
......@@ -82,6 +94,7 @@ export const buildCycleAnalyticsInitialData = ({
labelsPath = '',
milestonesPath = '',
} = {}) => ({
selectedValueStream: buildValueStreamFromJson(valueStream),
group: groupId
? convertObjectPropsToCamelCase(
buildGroupFromDataset({
......
......@@ -69,7 +69,7 @@ module Gitlab
def to_data_attributes
{}.tap do |attrs|
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_before] = created_before.to_date.iso8601
attrs[:projects] = group_projects(project_ids) if group && project_ids.present?
......@@ -94,7 +94,9 @@ module Gitlab
def value_stream_data_attributes
{
id: value_stream.id
id: value_stream.id,
name: value_stream.name,
is_custom: value_stream.custom?
}
end
......
......@@ -52,7 +52,10 @@ const defaultFeatureFlags = {
hasCreateMultipleValueStreams: false,
};
const [selectedValueStream] = mockData.valueStreams;
const initialCycleAnalyticsState = {
selectedValueStream,
createdAfter: mockData.startDate,
createdBefore: mockData.endDate,
group: currentGroup,
......@@ -627,6 +630,7 @@ describe('Cycle Analytics component', () => {
describe('Url parameters', () => {
const defaultParams = {
value_stream_id: selectedValueStream.id,
created_after: toYmd(mockData.startDate),
created_before: toYmd(mockData.endDate),
project_ids: null,
......@@ -640,9 +644,6 @@ describe('Cycle Analytics component', () => {
mock = new MockAdapter(axios);
mockRequiredRoutes(mock);
wrapper = await createComponent();
await store.dispatch('initializeCycleAnalytics', initialCycleAnalyticsState);
});
afterEach(() => {
......@@ -651,12 +652,41 @@ describe('Cycle Analytics component', () => {
wrapper = null;
});
it('sets the created_after and created_before url parameters', async () => {
await shouldMergeUrlParams(wrapper, defaultParams);
describe('with minimal parameters set set', () => {
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', () => {
beforeEach(async () => {
wrapper = await createComponent();
store.dispatch('setSelectedProjects', mockData.selectedProjects);
await wrapper.vm.$nextTick();
});
......
......@@ -1047,7 +1047,7 @@ describe('Cycle analytics actions', () => {
});
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(
actions.receiveValueStreamsSuccess,
valueStreams,
......@@ -1058,7 +1058,25 @@ describe('Cycle analytics actions', () => {
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', () => {
describe('with value streams available', () => {
it.each`
mutation | payload | expectedState
${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1].id} | ${{ selectedValueStream: valueStreams[1] }}
${types.SET_SELECTED_VALUE_STREAM} | ${'fake-id'} | ${{ selectedValueStream: {} }}
mutation | payload | expectedState
${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1]} | ${{ selectedValueStream: valueStreams[1] }}
${types.SET_SELECTED_VALUE_STREAM} | ${'fake-id'} | ${{ selectedValueStream: {} }}
`(
'$mutation with payload $payload will update state with $expectedState',
({ mutation, payload, expectedState }) => {
......
......@@ -5,6 +5,12 @@ import {
filterBySearchTerm,
} from 'ee/analytics/shared/utils';
const rawValueStream = `{
"id": 1,
"name": "Custom value stream 1",
"is_custom": true
}`;
const groupDataset = {
groupId: '1',
groupName: 'My Group',
......@@ -27,13 +33,13 @@ const projectDataset = {
projectPathWithNamespace: 'my-group/my-project',
};
const rawProjects = JSON.stringify([
const rawProjects = `[
{
project_id: '1',
project_name: 'My Project',
project_path_with_namespace: 'my-group/my-project',
},
]);
"project_id": "1",
"project_name": "My Project",
"project_path_with_namespace": "my-group/my-project"
}
]`;
describe('buildGroupFromDataset', () => {
it('returns null if groupId is missing', () => {
......@@ -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', () => {
it("will be set given a valid 'groupId' and all group parameters", () => {
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