Commit b8f5c54d authored by Miguel Rincon's avatar Miguel Rincon

Merge branch '221204-fix-custom-stages-and-charts-endpoints' into 'master'

[FE] Update vsa chart endpoints"

Closes #226996 and #221204

See merge request gitlab-org/gitlab!38029
parents f495e00a 15aaa722
...@@ -106,7 +106,7 @@ export default { ...@@ -106,7 +106,7 @@ export default {
shouldDisplayTypeOfWorkCharts() { shouldDisplayTypeOfWorkCharts() {
return !this.hasNoAccessError && !this.isLoading; return !this.hasNoAccessError && !this.isLoading;
}, },
shouldDsiplayPathNavigation() { shouldDisplayPathNavigation() {
return this.featureFlags.hasPathNavigation && !this.hasNoAccessError && this.selectedStage; return this.featureFlags.hasPathNavigation && !this.hasNoAccessError && this.selectedStage;
}, },
shouldDisplayFilterBar() { shouldDisplayFilterBar() {
...@@ -225,9 +225,9 @@ export default { ...@@ -225,9 +225,9 @@ export default {
class="gl-align-self-start gl-sm-align-self-start gl-mt-0 gl-sm-mt-5" class="gl-align-self-start gl-sm-align-self-start gl-mt-0 gl-sm-mt-5"
/> />
</div> </div>
<div class="mw-100"> <div class="gl-max-w-full">
<div class="mt-3 py-2 px-3 bg-gray-light border-top border-bottom"> <div class="gl-mt-3 gl-py-2 gl-px-3 bg-gray-light border-top border-bottom">
<div v-if="shouldDsiplayPathNavigation" class="w-100 pb-2"> <div v-if="shouldDisplayPathNavigation" class="gl-w-full gl-pb-2">
<path-navigation <path-navigation
class="js-path-navigation" class="js-path-navigation"
:loading="isLoading" :loading="isLoading"
......
...@@ -25,11 +25,8 @@ export default { ...@@ -25,11 +25,8 @@ export default {
return Boolean(this.durationChartPlottableData.length); return Boolean(this.durationChartPlottableData.length);
}, },
}, },
mounted() {
this.fetchDurationData();
},
methods: { methods: {
...mapActions('durationChart', ['fetchDurationData', 'updateSelectedDurationChartStages']), ...mapActions('durationChart', ['updateSelectedDurationChartStages']),
onDurationStageSelect(stages) { onDurationStageSelect(stages) {
this.updateSelectedDurationChartStages(stages); this.updateSelectedDurationChartStages(stages);
}, },
......
...@@ -42,15 +42,16 @@ export const receiveStageDataError = ({ commit }) => { ...@@ -42,15 +42,16 @@ export const receiveStageDataError = ({ commit }) => {
createFlash(__('There was an error fetching data for the selected stage')); createFlash(__('There was an error fetching data for the selected stage'));
}; };
export const fetchStageData = ({ state, dispatch, getters }, slug) => { export const fetchStageData = ({ dispatch, getters }, slug) => {
const { cycleAnalyticsRequestParams = {} } = getters; const { cycleAnalyticsRequestParams = {}, currentValueStreamId, currentGroupPath } = getters;
const {
selectedGroup: { fullPath },
} = state;
dispatch('requestStageData'); dispatch('requestStageData');
return Api.cycleAnalyticsStageEvents(fullPath, slug, cycleAnalyticsRequestParams) return Api.cycleAnalyticsStageEvents(
currentGroupPath,
currentValueStreamId,
slug,
cycleAnalyticsRequestParams,
)
.then(({ data }) => dispatch('receiveStageDataSuccess', data)) .then(({ data }) => dispatch('receiveStageDataSuccess', data))
.catch(error => dispatch('receiveStageDataError', error)); .catch(error => dispatch('receiveStageDataError', error));
}; };
...@@ -65,20 +66,32 @@ export const receiveStageMedianValuesError = ({ commit }) => { ...@@ -65,20 +66,32 @@ export const receiveStageMedianValuesError = ({ commit }) => {
createFlash(__('There was an error fetching median data for stages')); createFlash(__('There was an error fetching median data for stages'));
}; };
const fetchStageMedian = (currentGroupPath, stageId, params) => const fetchStageMedian = (currentGroupPath, currentValueStreamId, stageId, params) =>
Api.cycleAnalyticsStageMedian(currentGroupPath, stageId, params).then(({ data }) => ({ Api.cycleAnalyticsStageMedian(currentGroupPath, currentValueStreamId, stageId, params).then(
({ data }) => ({
id: stageId, id: stageId,
...data, ...data,
})); }),
);
export const fetchStageMedianValues = ({ dispatch, getters }) => { export const fetchStageMedianValues = ({ dispatch, getters }) => {
const { currentGroupPath, cycleAnalyticsRequestParams, activeStages } = getters; const {
currentGroupPath,
cycleAnalyticsRequestParams,
activeStages,
currentValueStreamId,
} = getters;
const stageIds = activeStages.map(s => s.slug); const stageIds = activeStages.map(s => s.slug);
dispatch('requestStageMedianValues'); dispatch('requestStageMedianValues');
return Promise.all( return Promise.all(
stageIds.map(stageId => stageIds.map(stageId =>
fetchStageMedian(currentGroupPath, stageId, cycleAnalyticsRequestParams), fetchStageMedian(
currentGroupPath,
currentValueStreamId,
stageId,
cycleAnalyticsRequestParams,
),
), ),
) )
.then(data => dispatch('receiveStageMedianValuesSuccess', data)) .then(data => dispatch('receiveStageMedianValuesSuccess', data))
...@@ -112,7 +125,6 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => { ...@@ -112,7 +125,6 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => {
return Promise.resolve() return Promise.resolve()
.then(() => dispatch('fetchValueStreams')) .then(() => dispatch('fetchValueStreams'))
.then(() => dispatch('fetchStageMedianValues'))
.then(() => dispatch('receiveCycleAnalyticsDataSuccess')) .then(() => dispatch('receiveCycleAnalyticsDataSuccess'))
.catch(error => dispatch('receiveCycleAnalyticsDataError', error)); .catch(error => dispatch('receiveCycleAnalyticsDataError', error));
}; };
...@@ -143,35 +155,19 @@ export const receiveGroupStagesSuccess = ({ commit, dispatch }, stages) => { ...@@ -143,35 +155,19 @@ export const receiveGroupStagesSuccess = ({ commit, dispatch }, stages) => {
return dispatch('setDefaultSelectedStage'); return dispatch('setDefaultSelectedStage');
}; };
export const fetchValueStreamStages = ({ export const fetchGroupStagesAndEvents = ({ dispatch, getters }) => {
hasCreateMultipleValueStreams,
valueStreamId,
groupId,
params,
}) => {
return hasCreateMultipleValueStreams
? Api.cycleAnalyticsValueStreamGroupStagesAndEvents(groupId, valueStreamId, params)
: Api.cycleAnalyticsGroupStagesAndEvents(groupId, params);
};
export const fetchGroupStagesAndEvents = ({ state, dispatch, getters }) => {
const {
featureFlags: { hasCreateMultipleValueStreams = false },
} = state;
const { const {
currentValueStreamId: valueStreamId, currentValueStreamId: valueStreamId,
currentGroupPath: groupId, currentGroupPath: groupId,
cycleAnalyticsRequestParams: { created_after, project_ids }, cycleAnalyticsRequestParams: { created_after, project_ids },
} = getters; } = getters;
dispatch('requestGroupStages'); dispatch('requestGroupStages');
dispatch('customStages/setStageEvents', []); dispatch('customStages/setStageEvents', []);
return fetchValueStreamStages({ return Api.cycleAnalyticsGroupStagesAndEvents(groupId, valueStreamId, {
hasCreateMultipleValueStreams, start_date: created_after,
groupId, project_ids,
valueStreamId,
params: { start_date: created_after, project_ids },
}) })
.then(({ data: { stages = [], events = [] } }) => { .then(({ data: { stages = [], events = [] } }) => {
dispatch('receiveGroupStagesSuccess', stages); dispatch('receiveGroupStagesSuccess', stages);
...@@ -213,18 +209,16 @@ export const receiveUpdateStageError = ( ...@@ -213,18 +209,16 @@ export const receiveUpdateStageError = (
return dispatch('customStages/setStageFormErrors', errors); return dispatch('customStages/setStageFormErrors', errors);
}; };
export const updateStage = ({ dispatch, state }, { id, ...rest }) => { export const updateStage = ({ dispatch, getters }, { id, ...params }) => {
const { const { currentGroupPath, currentValueStreamId } = getters;
selectedGroup: { fullPath },
} = state;
dispatch('requestUpdateStage'); dispatch('requestUpdateStage');
dispatch('customStages/setSavingCustomStage'); dispatch('customStages/setSavingCustomStage');
return Api.cycleAnalyticsUpdateStage(id, fullPath, { ...rest }) return Api.cycleAnalyticsUpdateStage(currentGroupPath, currentValueStreamId, id, params)
.then(({ data }) => dispatch('receiveUpdateStageSuccess', data)) .then(({ data }) => dispatch('receiveUpdateStageSuccess', data))
.catch(({ response: { status = 400, data: responseData } = {} }) => .catch(({ response: { status = httpStatus.BAD_REQUEST, data: responseData } = {} }) =>
dispatch('receiveUpdateStageError', { status, responseData, data: { id, ...rest } }), dispatch('receiveUpdateStageError', { status, responseData, data: { id, ...params } }),
); );
}; };
...@@ -240,14 +234,11 @@ export const receiveRemoveStageError = ({ commit }) => { ...@@ -240,14 +234,11 @@ export const receiveRemoveStageError = ({ commit }) => {
createFlash(__('There was an error removing your custom stage, please try again')); createFlash(__('There was an error removing your custom stage, please try again'));
}; };
export const removeStage = ({ dispatch, state }, stageId) => { export const removeStage = ({ dispatch, getters }, stageId) => {
const { const { currentGroupPath, currentValueStreamId } = getters;
selectedGroup: { fullPath },
} = state;
dispatch('requestRemoveStage'); dispatch('requestRemoveStage');
return Api.cycleAnalyticsRemoveStage(stageId, fullPath) return Api.cycleAnalyticsRemoveStage(currentGroupPath, currentValueStreamId, stageId)
.then(() => dispatch('receiveRemoveStageSuccess')) .then(() => dispatch('receiveRemoveStageSuccess'))
.catch(error => dispatch('receiveRemoveStageError', error)); .catch(error => dispatch('receiveRemoveStageError', error));
}; };
...@@ -289,19 +280,16 @@ export const receiveReorderStageError = ({ commit }) => { ...@@ -289,19 +280,16 @@ export const receiveReorderStageError = ({ commit }) => {
createFlash(__('There was an error updating the stage order. Please try reloading the page.')); createFlash(__('There was an error updating the stage order. Please try reloading the page.'));
}; };
export const reorderStage = ({ dispatch, state }, initialData) => { export const reorderStage = ({ dispatch, getters }, initialData) => {
dispatch('requestReorderStage'); dispatch('requestReorderStage');
const { currentGroupPath, currentValueStreamId } = getters;
const {
selectedGroup: { fullPath },
} = state;
const { id, moveAfterId, moveBeforeId } = initialData; const { id, moveAfterId, moveBeforeId } = initialData;
const params = moveAfterId ? { move_after_id: moveAfterId } : { move_before_id: moveBeforeId }; const params = moveAfterId ? { move_after_id: moveAfterId } : { move_before_id: moveBeforeId };
return Api.cycleAnalyticsUpdateStage(id, fullPath, params) return Api.cycleAnalyticsUpdateStage(currentGroupPath, currentValueStreamId, id, params)
.then(({ data }) => dispatch('receiveReorderStageSuccess', data)) .then(({ data }) => dispatch('receiveReorderStageSuccess', data))
.catch(({ response: { status = 400, data: responseData } = {} }) => .catch(({ response: { status = httpStatus.BAD_REQUEST, data: responseData } = {} }) =>
dispatch('receiveReorderStageError', { status, responseData }), dispatch('receiveReorderStageError', { status, responseData }),
); );
}; };
...@@ -311,14 +299,11 @@ export const receiveCreateValueStreamSuccess = ({ commit, dispatch }) => { ...@@ -311,14 +299,11 @@ export const receiveCreateValueStreamSuccess = ({ commit, dispatch }) => {
return dispatch('fetchValueStreams'); return dispatch('fetchValueStreams');
}; };
export const createValueStream = ({ commit, dispatch, rootState }, data) => { export const createValueStream = ({ commit, dispatch, getters }, data) => {
const { const { currentGroupPath } = getters;
selectedGroup: { fullPath },
} = rootState;
commit(types.REQUEST_CREATE_VALUE_STREAM); commit(types.REQUEST_CREATE_VALUE_STREAM);
return Api.cycleAnalyticsCreateValueStream(fullPath, data) return Api.cycleAnalyticsCreateValueStream(currentGroupPath, data)
.then(() => dispatch('receiveCreateValueStreamSuccess')) .then(() => dispatch('receiveCreateValueStreamSuccess'))
.catch(({ response } = {}) => { .catch(({ response } = {}) => {
const { data: { message, payload: { errors } } = null } = response; const { data: { message, payload: { errors } } = null } = response;
...@@ -328,7 +313,10 @@ export const createValueStream = ({ commit, dispatch, rootState }, data) => { ...@@ -328,7 +313,10 @@ export const createValueStream = ({ commit, dispatch, rootState }, data) => {
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('fetchGroupStagesAndEvents'); return Promise.resolve()
.then(() => dispatch('fetchGroupStagesAndEvents'))
.then(() => dispatch('fetchStageMedianValues'))
.then(() => dispatch('durationChart/fetchDurationData'));
}; };
export const receiveValueStreamsSuccess = ({ commit, dispatch }, data = []) => { export const receiveValueStreamsSuccess = ({ commit, dispatch }, data = []) => {
......
...@@ -65,17 +65,13 @@ export const receiveCreateStageError = ( ...@@ -65,17 +65,13 @@ export const receiveCreateStageError = (
return dispatch('setStageFormErrors', errors); return dispatch('setStageFormErrors', errors);
}; };
export const createStage = ({ dispatch, rootState, rootGetters }, data) => { export const createStage = ({ dispatch, rootGetters }, data) => {
const { const { currentGroupPath, currentValueStreamId } = rootGetters;
selectedGroup: { fullPath },
} = rootState;
const { currentValueStreamId } = rootGetters;
dispatch('clearFormErrors'); dispatch('clearFormErrors');
dispatch('setSavingCustomStage'); dispatch('setSavingCustomStage');
return Api.cycleAnalyticsCreateStage(fullPath, currentValueStreamId, data) return Api.cycleAnalyticsCreateStage(currentGroupPath, currentValueStreamId, data)
.then(response => { .then(response => {
const { status, data: responseData } = response; const { status, data: responseData } = response;
return dispatch('receiveCreateStageSuccess', { status, data: responseData }); return dispatch('receiveCreateStageSuccess', { status, data: responseData });
......
...@@ -22,14 +22,19 @@ export const receiveDurationDataError = ({ commit }) => { ...@@ -22,14 +22,19 @@ export const receiveDurationDataError = ({ commit }) => {
export const fetchDurationData = ({ dispatch, rootGetters }) => { export const fetchDurationData = ({ dispatch, rootGetters }) => {
dispatch('requestDurationData'); dispatch('requestDurationData');
const {
const { cycleAnalyticsRequestParams, activeStages, currentGroupPath } = rootGetters; cycleAnalyticsRequestParams,
activeStages,
currentGroupPath,
currentValueStreamId,
} = rootGetters;
return Promise.all( return Promise.all(
activeStages.map(stage => { activeStages.map(stage => {
const { slug } = stage; const { slug } = stage;
return Api.cycleAnalyticsDurationChart( return Api.cycleAnalyticsDurationChart(
currentGroupPath, currentGroupPath,
currentValueStreamId,
slug, slug,
cycleAnalyticsRequestParams, cycleAnalyticsRequestParams,
).then(({ data }) => ({ ).then(({ data }) => ({
...@@ -53,7 +58,12 @@ export const receiveDurationMedianDataError = ({ commit }) => { ...@@ -53,7 +58,12 @@ export const receiveDurationMedianDataError = ({ commit }) => {
export const fetchDurationMedianData = ({ dispatch, rootState, rootGetters }) => { export const fetchDurationMedianData = ({ dispatch, rootState, rootGetters }) => {
const { startDate, endDate } = rootState; const { startDate, endDate } = rootState;
const { cycleAnalyticsRequestParams, activeStages, currentGroupPath } = rootGetters; const {
cycleAnalyticsRequestParams,
activeStages,
currentGroupPath,
currentValueStreamId,
} = rootGetters;
const offsetValue = getDayDifference(new Date(startDate), new Date(endDate)); const offsetValue = getDayDifference(new Date(startDate), new Date(endDate));
const offsetCreatedAfter = getDateInPast(new Date(startDate), offsetValue); const offsetCreatedAfter = getDateInPast(new Date(startDate), offsetValue);
...@@ -63,7 +73,7 @@ export const fetchDurationMedianData = ({ dispatch, rootState, rootGetters }) => ...@@ -63,7 +73,7 @@ export const fetchDurationMedianData = ({ dispatch, rootState, rootGetters }) =>
activeStages.map(stage => { activeStages.map(stage => {
const { slug } = stage; const { slug } = stage;
return Api.cycleAnalyticsDurationChart(currentGroupPath, slug, { return Api.cycleAnalyticsDurationChart(currentGroupPath, currentValueStreamId, slug, {
...cycleAnalyticsRequestParams, ...cycleAnalyticsRequestParams,
created_after: dateFormat(offsetCreatedAfter, dateFormats.isoDate), created_after: dateFormat(offsetCreatedAfter, dateFormats.isoDate),
created_before: dateFormat(offsetCreatedBefore, dateFormats.isoDate), created_before: dateFormat(offsetCreatedBefore, dateFormats.isoDate),
......
...@@ -14,17 +14,17 @@ export default { ...@@ -14,17 +14,17 @@ export default {
cycleAnalyticsTopLabelsPath: '/groups/:id/-/analytics/type_of_work/tasks_by_type/top_labels', cycleAnalyticsTopLabelsPath: '/groups/:id/-/analytics/type_of_work/tasks_by_type/top_labels',
cycleAnalyticsSummaryDataPath: '/groups/:id/-/analytics/value_stream_analytics/summary', cycleAnalyticsSummaryDataPath: '/groups/:id/-/analytics/value_stream_analytics/summary',
cycleAnalyticsTimeSummaryDataPath: '/groups/:id/-/analytics/value_stream_analytics/time_summary', cycleAnalyticsTimeSummaryDataPath: '/groups/:id/-/analytics/value_stream_analytics/time_summary',
cycleAnalyticsGroupStagesAndEventsPath: '/groups/:id/-/analytics/value_stream_analytics/stages', cycleAnalyticsGroupStagesAndEventsPath:
cycleAnalyticsValueStreamGroupStagesAndEventsPath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages',
cycleAnalyticsValueStreamsPath: '/groups/:id/-/analytics/value_stream_analytics/value_streams', cycleAnalyticsValueStreamsPath: '/groups/:id/-/analytics/value_stream_analytics/value_streams',
cycleAnalyticsStageEventsPath: cycleAnalyticsStageEventsPath:
'/groups/:id/-/analytics/value_stream_analytics/stages/:stage_id/records', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/records',
cycleAnalyticsStageMedianPath: cycleAnalyticsStageMedianPath:
'/groups/:id/-/analytics/value_stream_analytics/stages/:stage_id/median', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/median',
cycleAnalyticsStagePath: '/groups/:id/-/analytics/value_stream_analytics/stages/:stage_id', cycleAnalyticsStagePath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id',
cycleAnalyticsDurationChartPath: cycleAnalyticsDurationChartPath:
'/groups/:id/-/analytics/value_stream_analytics/stages/:stage_id/duration_chart', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/duration_chart',
cycleAnalyticsGroupLabelsPath: '/groups/:namespace_path/-/labels.json', cycleAnalyticsGroupLabelsPath: '/groups/:namespace_path/-/labels.json',
codeReviewAnalyticsPath: '/api/:version/analytics/code_review', codeReviewAnalyticsPath: '/api/:version/analytics/code_review',
groupActivityIssuesPath: '/api/:version/analytics/group_activity/issues_count', groupActivityIssuesPath: '/api/:version/analytics/group_activity/issues_count',
...@@ -138,38 +138,34 @@ export default { ...@@ -138,38 +138,34 @@ export default {
return axios.get(url, { params }); return axios.get(url, { params });
}, },
cycleAnalyticsGroupStagesAndEvents(groupId, params = {}) { cycleAnalyticsGroupStagesAndEvents(groupId, valueStreamId, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsGroupStagesAndEventsPath).replace(':id', groupId); const url = Api.buildUrl(this.cycleAnalyticsGroupStagesAndEventsPath)
return axios.get(url, { params });
},
cycleAnalyticsValueStreamGroupStagesAndEvents(groupId, valueStreamId, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsValueStreamGroupStagesAndEventsPath)
.replace(':id', groupId) .replace(':id', groupId)
.replace(':value_stream_id', valueStreamId); .replace(':value_stream_id', valueStreamId);
return axios.get(url, { params }); return axios.get(url, { params });
}, },
cycleAnalyticsStageEvents(groupId, stageId, params = {}) { cycleAnalyticsStageEvents(groupId, valueStreamId, stageId, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsStageEventsPath) const url = Api.buildUrl(this.cycleAnalyticsStageEventsPath)
.replace(':id', groupId) .replace(':id', groupId)
.replace(':value_stream_id', valueStreamId)
.replace(':stage_id', stageId); .replace(':stage_id', stageId);
return axios.get(url, { params }); return axios.get(url, { params });
}, },
cycleAnalyticsStageMedian(groupId, stageId, params = {}) { cycleAnalyticsStageMedian(groupId, valueStreamId, stageId, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsStageMedianPath) const url = Api.buildUrl(this.cycleAnalyticsStageMedianPath)
.replace(':id', groupId) .replace(':id', groupId)
.replace(':value_stream_id', valueStreamId)
.replace(':stage_id', stageId); .replace(':stage_id', stageId);
return axios.get(url, { params: { ...params } }); return axios.get(url, { params: { ...params } });
}, },
cycleAnalyticsCreateStage(groupId, valueStreamId, data) { cycleAnalyticsCreateStage(groupId, valueStreamId, data) {
const url = Api.buildUrl(this.cycleAnalyticsValueStreamGroupStagesAndEventsPath) const url = Api.buildUrl(this.cycleAnalyticsGroupStagesAndEventsPath)
.replace(':id', groupId) .replace(':id', groupId)
.replace(':value_stream_id', valueStreamId); .replace(':value_stream_id', valueStreamId);
...@@ -186,27 +182,29 @@ export default { ...@@ -186,27 +182,29 @@ export default {
return axios.get(url, data); return axios.get(url, data);
}, },
cycleAnalyticsStageUrl(stageId, groupId) { cycleAnalyticsStageUrl(groupId, valueStreamId, stageId) {
return Api.buildUrl(this.cycleAnalyticsStagePath) return Api.buildUrl(this.cycleAnalyticsStagePath)
.replace(':id', groupId) .replace(':id', groupId)
.replace(':value_stream_id', valueStreamId)
.replace(':stage_id', stageId); .replace(':stage_id', stageId);
}, },
cycleAnalyticsUpdateStage(stageId, groupId, data) { cycleAnalyticsUpdateStage(groupId, valueStreamId, stageId, data) {
const url = this.cycleAnalyticsStageUrl(stageId, groupId); const url = this.cycleAnalyticsStageUrl(groupId, valueStreamId, stageId);
return axios.put(url, data); return axios.put(url, data);
}, },
cycleAnalyticsRemoveStage(stageId, groupId) { cycleAnalyticsRemoveStage(groupId, valueStreamId, stageId) {
const url = this.cycleAnalyticsStageUrl(stageId, groupId); const url = this.cycleAnalyticsStageUrl(groupId, valueStreamId, stageId);
return axios.delete(url); return axios.delete(url);
}, },
cycleAnalyticsDurationChart(groupId, stageSlug, params = {}) { cycleAnalyticsDurationChart(groupId, valueStreamId, stageSlug, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsDurationChartPath) const url = Api.buildUrl(this.cycleAnalyticsDurationChartPath)
.replace(':id', groupId) .replace(':id', groupId)
.replace(':value_stream_id', valueStreamId)
.replace(':stage_id', stageSlug); .replace(':stage_id', stageSlug);
return axios.get(url, { return axios.get(url, {
......
...@@ -78,6 +78,7 @@ function createComponent({ ...@@ -78,6 +78,7 @@ function createComponent({
}, },
shallow = true, shallow = true,
withStageSelected = false, withStageSelected = false,
withValueStreamSelected = true,
featureFlags = {}, featureFlags = {},
props = {}, props = {},
} = {}) { } = {}) {
...@@ -90,7 +91,6 @@ function createComponent({ ...@@ -90,7 +91,6 @@ function createComponent({
emptyStateSvgPath, emptyStateSvgPath,
noDataSvgPath, noDataSvgPath,
noAccessSvgPath, noAccessSvgPath,
baseStagesEndpoint: mockData.endpoints.baseStagesEndpoint,
hideGroupDropDown, hideGroupDropDown,
...props, ...props,
}, },
...@@ -107,6 +107,10 @@ function createComponent({ ...@@ -107,6 +107,10 @@ function createComponent({
}, },
}); });
if (withValueStreamSelected) {
comp.vm.$store.dispatch('receiveValueStreamsSuccess', mockData.valueStreams);
}
if (withStageSelected) { if (withStageSelected) {
comp.vm.$store.commit('SET_SELECTED_GROUP', { comp.vm.$store.commit('SET_SELECTED_GROUP', {
...selectedGroup, ...selectedGroup,
...@@ -449,6 +453,7 @@ describe('Cycle Analytics component', () => { ...@@ -449,6 +453,7 @@ describe('Cycle Analytics component', () => {
StageNavItem, StageNavItem,
}, },
}, },
withValueStreamSelected: false,
withStageSelected: true, withStageSelected: true,
}); });
}); });
...@@ -533,6 +538,7 @@ describe('Cycle Analytics component', () => { ...@@ -533,6 +538,7 @@ describe('Cycle Analytics component', () => {
describe('enabled', () => { describe('enabled', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
withValueStreamSelected: false,
withStageSelected: true, withStageSelected: true,
pathNavigationEnabled: true, pathNavigationEnabled: true,
}); });
...@@ -544,7 +550,7 @@ describe('Cycle Analytics component', () => { ...@@ -544,7 +550,7 @@ describe('Cycle Analytics component', () => {
return waitForPromises(); return waitForPromises();
}); });
it('displays the path navigation', () => { it('does not display the path navigation', () => {
displaysPathNavigation(false); displaysPathNavigation(false);
}); });
}); });
......
...@@ -29,10 +29,10 @@ export const endpoints = { ...@@ -29,10 +29,10 @@ export const endpoints = {
groupLabels: /groups\/[A-Z|a-z|\d|\-|_]+\/-\/labels.json/, groupLabels: /groups\/[A-Z|a-z|\d|\-|_]+\/-\/labels.json/,
recentActivityData: /analytics\/value_stream_analytics\/summary/, recentActivityData: /analytics\/value_stream_analytics\/summary/,
timeMetricsData: /analytics\/value_stream_analytics\/time_summary/, timeMetricsData: /analytics\/value_stream_analytics\/time_summary/,
durationData: /analytics\/value_stream_analytics\/stages\/\d+\/duration_chart/, durationData: /analytics\/value_stream_analytics\/value_streams\/\d+\/stages\/\d+\/duration_chart/,
stageData: /analytics\/value_stream_analytics\/stages\/\d+\/records/, stageData: /analytics\/value_stream_analytics\/value_streams\/\d+\/stages\/\d+\/records/,
stageMedian: /analytics\/value_stream_analytics\/stages\/\d+\/median/, stageMedian: /analytics\/value_stream_analytics\/value_streams\/\d+\/stages\/\d+\/median/,
baseStagesEndpoint: /analytics\/value_stream_analytics\/stages$/, baseStagesEndpoint: /analytics\/value_stream_analytics\/value_streams\/\d+\/stages$/,
tasksByTypeData: /analytics\/type_of_work\/tasks_by_type/, tasksByTypeData: /analytics\/type_of_work\/tasks_by_type/,
tasksByTypeTopLabelsData: /analytics\/type_of_work\/tasks_by_type\/top_labels/, tasksByTypeTopLabelsData: /analytics\/type_of_work\/tasks_by_type\/top_labels/,
valueStreamData: /analytics\/value_stream_analytics\/value_streams/, valueStreamData: /analytics\/value_stream_analytics\/value_streams/,
......
...@@ -28,8 +28,13 @@ const [selectedStage] = activeStages; ...@@ -28,8 +28,13 @@ const [selectedStage] = activeStages;
const selectedStageSlug = selectedStage.slug; const selectedStageSlug = selectedStage.slug;
const [selectedValueStream] = valueStreams; const [selectedValueStream] = valueStreams;
const mockGetters = {
currentGroupPath: () => selectedGroup.fullPath,
currentValueStreamId: () => selectedValueStream.id,
};
const stageEndpoint = ({ stageId }) => const stageEndpoint = ({ stageId }) =>
`/groups/${selectedGroup.fullPath}/-/analytics/value_stream_analytics/stages/${stageId}`; `/groups/${selectedGroup.fullPath}/-/analytics/value_stream_analytics/value_streams/${selectedValueStream.id}/stages/${stageId}`;
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -52,6 +57,8 @@ describe('Cycle analytics actions', () => { ...@@ -52,6 +57,8 @@ describe('Cycle analytics actions', () => {
hasDurationChartMedian: true, hasDurationChartMedian: true,
}, },
activeStages, activeStages,
selectedValueStream,
...mockGetters,
}; };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
}); });
...@@ -84,13 +91,17 @@ describe('Cycle analytics actions', () => { ...@@ -84,13 +91,17 @@ describe('Cycle analytics actions', () => {
describe('setSelectedValueStream', () => { describe('setSelectedValueStream', () => {
const vs = { id: 'vs-1', name: 'Value stream 1' }; const vs = { id: 'vs-1', name: 'Value stream 1' };
it('dispatches the fetchCycleAnalyticsData action', () => { it('refetches the cycle analytics data', () => {
return testAction( return testAction(
actions.setSelectedValueStream, actions.setSelectedValueStream,
vs, vs,
{ ...state, selectedValueStream: {} }, { ...state, selectedValueStream: {} },
[{ type: types.SET_SELECTED_VALUE_STREAM, payload: vs }], [{ type: types.SET_SELECTED_VALUE_STREAM, payload: vs }],
[{ type: 'fetchGroupStagesAndEvents' }], [
{ type: 'fetchGroupStagesAndEvents' },
{ type: 'fetchStageMedianValues' },
{ type: 'durationChart/fetchDurationData' },
],
); );
}); });
}); });
...@@ -269,7 +280,6 @@ describe('Cycle analytics actions', () => { ...@@ -269,7 +280,6 @@ describe('Cycle analytics actions', () => {
[ [
{ type: 'requestCycleAnalyticsData' }, { type: 'requestCycleAnalyticsData' },
{ type: 'fetchValueStreams' }, { type: 'fetchValueStreams' },
{ type: 'fetchStageMedianValues' },
{ type: 'receiveCycleAnalyticsDataSuccess' }, { type: 'receiveCycleAnalyticsDataSuccess' },
], ],
); );
...@@ -437,7 +447,6 @@ describe('Cycle analytics actions', () => { ...@@ -437,7 +447,6 @@ describe('Cycle analytics actions', () => {
beforeEach(() => { beforeEach(() => {
mock.onPut(stageEndpoint({ stageId }), payload).replyOnce(httpStatusCodes.OK, payload); mock.onPut(stageEndpoint({ stageId }), payload).replyOnce(httpStatusCodes.OK, payload);
state = { selectedGroup };
}); });
it('dispatches receiveUpdateStageSuccess and customStages/setSavingCustomStage', () => { it('dispatches receiveUpdateStageSuccess and customStages/setSavingCustomStage', () => {
...@@ -583,7 +592,6 @@ describe('Cycle analytics actions', () => { ...@@ -583,7 +592,6 @@ describe('Cycle analytics actions', () => {
beforeEach(() => { beforeEach(() => {
mock.onDelete(stageEndpoint({ stageId })).replyOnce(httpStatusCodes.OK); mock.onDelete(stageEndpoint({ stageId })).replyOnce(httpStatusCodes.OK);
state = { selectedGroup };
}); });
it('dispatches receiveRemoveStageSuccess with put request response data', () => { it('dispatches receiveRemoveStageSuccess with put request response data', () => {
...@@ -820,10 +828,6 @@ describe('Cycle analytics actions', () => { ...@@ -820,10 +828,6 @@ describe('Cycle analytics actions', () => {
const stageId = 'cool-stage'; const stageId = 'cool-stage';
const payload = { id: stageId, move_after_id: '2', move_before_id: '8' }; const payload = { id: stageId, move_after_id: '2', move_before_id: '8' };
beforeEach(() => {
state = { selectedGroup };
});
describe('with no errors', () => { describe('with no errors', () => {
beforeEach(() => { beforeEach(() => {
mock.onPut(stageEndpoint({ stageId })).replyOnce(httpStatusCodes.OK); mock.onPut(stageEndpoint({ stageId })).replyOnce(httpStatusCodes.OK);
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
transformedDurationData, transformedDurationData,
transformedDurationMedianData, transformedDurationMedianData,
endpoints, endpoints,
valueStreams,
} from '../../../mock_data'; } from '../../../mock_data';
import { shouldFlashAMessage } from '../../../helpers'; import { shouldFlashAMessage } from '../../../helpers';
...@@ -22,26 +23,30 @@ const selectedGroup = { fullPath: group.path }; ...@@ -22,26 +23,30 @@ const selectedGroup = { fullPath: group.path };
const [stage1, stage2] = stages; const [stage1, stage2] = stages;
const hiddenStage = { ...stage1, hidden: true, id: 3, slug: 3 }; const hiddenStage = { ...stage1, hidden: true, id: 3, slug: 3 };
const activeStages = [stage1, stage2]; const activeStages = [stage1, stage2];
const [selectedValueStream] = valueStreams;
const rootState = { const rootState = {
startDate, startDate,
endDate, endDate,
stages: [...activeStages, hiddenStage], stages: [...activeStages, hiddenStage],
selectedGroup, selectedGroup,
selectedValueStream,
featureFlags: { featureFlags: {
hasDurationChart: true, hasDurationChart: true,
hasDurationChartMedian: true, hasDurationChartMedian: true,
}, },
getters,
rootGetters: {
...rootGetters,
activeStages,
},
}; };
describe('DurationChart actions', () => { describe('DurationChart actions', () => {
let state;
let mock; let mock;
const state = {
...rootState,
...getters,
...rootGetters,
activeStages,
currentGroupPath: () => selectedGroup.fullPath,
currentValueStreamId: () => selectedValueStream.id,
};
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
...@@ -60,7 +65,7 @@ describe('DurationChart actions', () => { ...@@ -60,7 +65,7 @@ describe('DurationChart actions', () => {
return testAction( return testAction(
actions.fetchDurationData, actions.fetchDurationData,
null, null,
{ activeStages }, state,
[], [],
[ [
{ type: 'requestDurationData' }, { type: 'requestDurationData' },
...@@ -77,7 +82,11 @@ describe('DurationChart actions', () => { ...@@ -77,7 +82,11 @@ describe('DurationChart actions', () => {
return actions return actions
.fetchDurationData({ .fetchDurationData({
dispatch, dispatch,
...rootState, rootState,
rootGetters: {
...rootGetters,
activeStages,
},
}) })
.then(() => { .then(() => {
const requestedUrls = mock.history.get.map(({ url }) => url); const requestedUrls = mock.history.get.map(({ url }) => url);
...@@ -98,7 +107,11 @@ describe('DurationChart actions', () => { ...@@ -98,7 +107,11 @@ describe('DurationChart actions', () => {
return actions return actions
.fetchDurationData({ .fetchDurationData({
dispatch, dispatch,
...rootState, rootState,
rootGetters: {
...rootGetters,
activeStages,
},
}) })
.then(() => { .then(() => {
expect(dispatch).toHaveBeenCalledWith('receiveDurationDataError'); expect(dispatch).toHaveBeenCalledWith('receiveDurationDataError');
...@@ -297,7 +310,7 @@ describe('DurationChart actions', () => { ...@@ -297,7 +310,7 @@ describe('DurationChart actions', () => {
return testAction( return testAction(
actions.fetchDurationMedianData, actions.fetchDurationMedianData,
null, null,
{ ...rootState, activeStages }, state,
[], [],
[ [
{ {
...@@ -319,7 +332,7 @@ describe('DurationChart actions', () => { ...@@ -319,7 +332,7 @@ describe('DurationChart actions', () => {
.fetchDurationMedianData({ .fetchDurationMedianData({
dispatch, dispatch,
rootState, rootState,
rootGetters: { activeStages }, rootGetters: { ...rootGetters, activeStages },
}) })
.then(() => { .then(() => {
const requestedUrls = mock.history.get.map(({ url }) => url); const requestedUrls = mock.history.get.map(({ url }) => url);
......
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Api from 'ee/api'; import Api from 'ee/api';
import * as cycleAnalyticsConstants from 'ee/analytics/cycle_analytics/constants'; import * as valueStreamAnalyticsConstants from 'ee/analytics/cycle_analytics/constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import * as analyticsMockData from 'ee_jest/analytics/cycle_analytics/mock_data'; import * as analyticsMockData from 'ee_jest/analytics/cycle_analytics/mock_data';
...@@ -201,15 +201,20 @@ describe('Api', () => { ...@@ -201,15 +201,20 @@ describe('Api', () => {
}); });
describe('Cycle analytics', () => { describe('Cycle analytics', () => {
const groupId = 'counting-54321';
const createdBefore = '2019-11-18'; const createdBefore = '2019-11-18';
const createdAfter = '2019-08-18'; const createdAfter = '2019-08-18';
const groupId = 'counting-54321';
const stageId = 'thursday'; const stageId = 'thursday';
const dummyCycleAnalyticsUrlRoot = `${dummyUrlRoot}/groups/${groupId}`; const valueStreamId = 'a-city-by-the-light-divided';
const dummyValueStreamAnalyticsUrlRoot = `${dummyUrlRoot}/groups/${groupId}/-/analytics/value_stream_analytics`;
const defaultParams = { const defaultParams = {
created_after: createdAfter, created_after: createdAfter,
created_before: createdBefore, created_before: createdBefore,
}; };
const valueStreamBaseUrl = ({ resource = '', id = null }) =>
[dummyValueStreamAnalyticsUrlRoot, id ? `value_streams/${id}/${resource}` : resource].join(
'/',
);
const expectRequestWithCorrectParameters = (responseObj, { params, expectedUrl, response }) => { const expectRequestWithCorrectParameters = (responseObj, { params, expectedUrl, response }) => {
const { const {
...@@ -244,7 +249,7 @@ describe('Api', () => { ...@@ -244,7 +249,7 @@ describe('Api', () => {
const params = { const params = {
...defaultParams, ...defaultParams,
project_ids: null, project_ids: null,
subject: cycleAnalyticsConstants.TASKS_BY_TYPE_SUBJECT_ISSUE, subject: valueStreamAnalyticsConstants.TASKS_BY_TYPE_SUBJECT_ISSUE,
label_ids: labelIds, label_ids: labelIds,
}; };
const expectedUrl = analyticsMockData.endpoints.tasksByTypeData; const expectedUrl = analyticsMockData.endpoints.tasksByTypeData;
...@@ -267,7 +272,7 @@ describe('Api', () => { ...@@ -267,7 +272,7 @@ describe('Api', () => {
const params = { const params = {
...defaultParams, ...defaultParams,
project_ids: null, project_ids: null,
subject: cycleAnalyticsConstants.TASKS_BY_TYPE_SUBJECT_ISSUE, subject: valueStreamAnalyticsConstants.TASKS_BY_TYPE_SUBJECT_ISSUE,
label_ids: labelIds, label_ids: labelIds,
}; };
...@@ -288,11 +293,8 @@ describe('Api', () => { ...@@ -288,11 +293,8 @@ describe('Api', () => {
describe('cycleAnalyticsSummaryData', () => { describe('cycleAnalyticsSummaryData', () => {
it('fetches value stream analytics summary data', done => { it('fetches value stream analytics summary data', done => {
const response = [{ value: 0, title: 'New Issues' }, { value: 0, title: 'Deploys' }]; const response = [{ value: 0, title: 'New Issues' }, { value: 0, title: 'Deploys' }];
const params = { const params = { ...defaultParams };
...defaultParams, const expectedUrl = `${dummyValueStreamAnalyticsUrlRoot}/summary`;
};
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/summary`;
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsSummaryData(groupId, params) Api.cycleAnalyticsSummaryData(groupId, params)
...@@ -314,11 +316,9 @@ describe('Api', () => { ...@@ -314,11 +316,9 @@ describe('Api', () => {
{ value: '10.0', title: 'Lead time', unit: 'per day' }, { value: '10.0', title: 'Lead time', unit: 'per day' },
{ value: '2.0', title: 'Cycle Time', unit: 'per day' }, { value: '2.0', title: 'Cycle Time', unit: 'per day' },
]; ];
const params = { const params = { ...defaultParams };
...defaultParams,
};
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/time_summary`; const expectedUrl = `${dummyValueStreamAnalyticsUrlRoot}/time_summary`;
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsTimeSummaryData(groupId, params) Api.cycleAnalyticsTimeSummaryData(groupId, params)
...@@ -337,8 +337,7 @@ describe('Api', () => { ...@@ -337,8 +337,7 @@ describe('Api', () => {
describe('cycleAnalyticsValueStreams', () => { describe('cycleAnalyticsValueStreams', () => {
it('fetches custom value streams', done => { it('fetches custom value streams', done => {
const response = [{ name: 'value stream 1', id: 1 }]; const response = [{ name: 'value stream 1', id: 1 }];
const expectedUrl = valueStreamBaseUrl({ resource: 'value_streams' });
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`;
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsValueStreams(groupId) Api.cycleAnalyticsValueStreams(groupId)
...@@ -356,10 +355,8 @@ describe('Api', () => { ...@@ -356,10 +355,8 @@ describe('Api', () => {
describe('cycleAnalyticsCreateValueStream', () => { describe('cycleAnalyticsCreateValueStream', () => {
it('submit the custom value stream data', done => { it('submit the custom value stream data', done => {
const response = {}; const response = {};
const customValueStream = { const customValueStream = { name: 'cool-value-stream-stage' };
name: 'cool-value-stream-stage', const expectedUrl = valueStreamBaseUrl({ resource: 'value_streams' });
};
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`;
mock.onPost(expectedUrl).reply(httpStatus.OK, response); mock.onPost(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsCreateValueStream(groupId, customValueStream) Api.cycleAnalyticsCreateValueStream(groupId, customValueStream)
...@@ -381,10 +378,10 @@ describe('Api', () => { ...@@ -381,10 +378,10 @@ describe('Api', () => {
'cycle_analytics[created_after]': createdAfter, 'cycle_analytics[created_after]': createdAfter,
'cycle_analytics[created_before]': createdBefore, 'cycle_analytics[created_before]': createdBefore,
}; };
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/stages`; const expectedUrl = valueStreamBaseUrl({ id: valueStreamId, resource: 'stages' });
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsGroupStagesAndEvents(groupId, params) Api.cycleAnalyticsGroupStagesAndEvents(groupId, valueStreamId, params)
.then(responseObj => .then(responseObj =>
expectRequestWithCorrectParameters(responseObj, { expectRequestWithCorrectParameters(responseObj, {
response, response,
...@@ -400,13 +397,14 @@ describe('Api', () => { ...@@ -400,13 +397,14 @@ describe('Api', () => {
describe('cycleAnalyticsStageEvents', () => { describe('cycleAnalyticsStageEvents', () => {
it('fetches stage events', done => { it('fetches stage events', done => {
const response = { events: [] }; const response = { events: [] };
const params = { const params = { ...defaultParams };
...defaultParams, const expectedUrl = valueStreamBaseUrl({
}; id: valueStreamId,
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/stages/${stageId}/records`; resource: `stages/${stageId}/records`,
});
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsStageEvents(groupId, stageId, params) Api.cycleAnalyticsStageEvents(groupId, valueStreamId, stageId, params)
.then(responseObj => .then(responseObj =>
expectRequestWithCorrectParameters(responseObj, { expectRequestWithCorrectParameters(responseObj, {
response, response,
...@@ -422,13 +420,14 @@ describe('Api', () => { ...@@ -422,13 +420,14 @@ describe('Api', () => {
describe('cycleAnalyticsStageMedian', () => { describe('cycleAnalyticsStageMedian', () => {
it('fetches stage events', done => { it('fetches stage events', done => {
const response = { value: '5 days ago' }; const response = { value: '5 days ago' };
const params = { const params = { ...defaultParams };
...defaultParams, const expectedUrl = valueStreamBaseUrl({
}; id: valueStreamId,
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/stages/${stageId}/median`; resource: `stages/${stageId}/median`,
});
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsStageMedian(groupId, stageId, params) Api.cycleAnalyticsStageMedian(groupId, valueStreamId, stageId, params)
.then(responseObj => .then(responseObj =>
expectRequestWithCorrectParameters(responseObj, { expectRequestWithCorrectParameters(responseObj, {
response, response,
...@@ -442,8 +441,6 @@ describe('Api', () => { ...@@ -442,8 +441,6 @@ describe('Api', () => {
}); });
describe('cycleAnalyticsCreateStage', () => { describe('cycleAnalyticsCreateStage', () => {
const valueStreamId = 'fake-value-stream';
it('submit the custom stage data', done => { it('submit the custom stage data', done => {
const response = {}; const response = {};
const customStage = { const customStage = {
...@@ -453,7 +450,10 @@ describe('Api', () => { ...@@ -453,7 +450,10 @@ describe('Api', () => {
end_event_identifier: 'issue_closed', end_event_identifier: 'issue_closed',
end_event_label_id: null, end_event_label_id: null,
}; };
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams/${valueStreamId}/stages`; const expectedUrl = valueStreamBaseUrl({
id: valueStreamId,
resource: 'stages',
});
mock.onPost(expectedUrl).reply(httpStatus.OK, response); mock.onPost(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsCreateStage(groupId, valueStreamId, customStage) Api.cycleAnalyticsCreateStage(groupId, valueStreamId, customStage)
...@@ -462,6 +462,7 @@ describe('Api', () => { ...@@ -462,6 +462,7 @@ describe('Api', () => {
expect(JSON.parse(reqData)).toMatchObject(customStage); expect(JSON.parse(reqData)).toMatchObject(customStage);
expect(url).toEqual(expectedUrl); expect(url).toEqual(expectedUrl);
}) })
.then(done) .then(done)
.catch(done.fail); .catch(done.fail);
}); });
...@@ -470,14 +471,14 @@ describe('Api', () => { ...@@ -470,14 +471,14 @@ describe('Api', () => {
describe('cycleAnalyticsUpdateStage', () => { describe('cycleAnalyticsUpdateStage', () => {
it('updates the stage data', done => { it('updates the stage data', done => {
const response = { id: stageId, custom: false, hidden: true, name: 'nice-stage' }; const response = { id: stageId, custom: false, hidden: true, name: 'nice-stage' };
const stageData = { const stageData = { name: 'nice-stage', hidden: true };
name: 'nice-stage', const expectedUrl = valueStreamBaseUrl({
hidden: true, id: valueStreamId,
}; resource: `stages/${stageId}`,
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/stages/${stageId}`; });
mock.onPut(expectedUrl).reply(httpStatus.OK, response); mock.onPut(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsUpdateStage(stageId, groupId, stageData) Api.cycleAnalyticsUpdateStage(groupId, valueStreamId, stageId, stageData)
.then(({ data, config: { data: reqData, url } }) => { .then(({ data, config: { data: reqData, url } }) => {
expect(data).toEqual(response); expect(data).toEqual(response);
expect(JSON.parse(reqData)).toMatchObject(stageData); expect(JSON.parse(reqData)).toMatchObject(stageData);
...@@ -491,10 +492,13 @@ describe('Api', () => { ...@@ -491,10 +492,13 @@ describe('Api', () => {
describe('cycleAnalyticsRemoveStage', () => { describe('cycleAnalyticsRemoveStage', () => {
it('deletes the specified data', done => { it('deletes the specified data', done => {
const response = { id: stageId, hidden: true, custom: true }; const response = { id: stageId, hidden: true, custom: true };
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/stages/${stageId}`; const expectedUrl = valueStreamBaseUrl({
id: valueStreamId,
resource: `stages/${stageId}`,
});
mock.onDelete(expectedUrl).reply(httpStatus.OK, response); mock.onDelete(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsRemoveStage(stageId, groupId) Api.cycleAnalyticsRemoveStage(groupId, valueStreamId, stageId)
.then(({ data, config: { url } }) => { .then(({ data, config: { url } }) => {
expect(data).toEqual(response); expect(data).toEqual(response);
...@@ -508,13 +512,14 @@ describe('Api', () => { ...@@ -508,13 +512,14 @@ describe('Api', () => {
describe('cycleAnalyticsDurationChart', () => { describe('cycleAnalyticsDurationChart', () => {
it('fetches stage duration data', done => { it('fetches stage duration data', done => {
const response = []; const response = [];
const params = { const params = { ...defaultParams };
...defaultParams, const expectedUrl = valueStreamBaseUrl({
}; id: valueStreamId,
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/stages/thursday/duration_chart`; resource: `stages/${stageId}/duration_chart`,
});
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsDurationChart(groupId, stageId, params) Api.cycleAnalyticsDurationChart(groupId, valueStreamId, stageId, params)
.then(responseObj => .then(responseObj =>
expectRequestWithCorrectParameters(responseObj, { expectRequestWithCorrectParameters(responseObj, {
response, response,
......
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