Commit 4e6f6aac authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'fguibert/refactor-shared-board-actions' into 'master'

Refactor shared board action [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!60571
parents cc59a8c9 7a075fa2
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
import { __ } from '~/locale';
import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscription.mutation.graphql';
import updateEpicTitleMutation from '~/sidebar/queries/update_epic_title.mutation.graphql';
import boardBlockingIssuesQuery from './graphql/board_blocking_issues.query.graphql';
import destroyBoardListMutation from './graphql/board_list_destroy.mutation.graphql';
import updateBoardListMutation from './graphql/board_list_update.mutation.graphql';
import issueSetSubscriptionMutation from './graphql/issue_set_subscription.mutation.graphql';
import issueSetTitleMutation from './graphql/issue_set_title.mutation.graphql';
......@@ -62,6 +64,12 @@ export const NOT_FILTER = 'not[';
export const flashAnimationDuration = 2000;
export const listsQuery = {
[issuableTypes.issue]: {
query: boardListsQuery,
},
};
export const blockingIssuablesQueries = {
[issuableTypes.issue]: {
query: boardBlockingIssuesQuery,
......
......@@ -37,6 +37,7 @@ import {
import { __ } from '~/locale';
import sidebarEventHub from '~/sidebar/event_hub';
import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
import { fullBoardId } from './boards_util';
import boardConfigToggle from './config_toggle';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
......@@ -135,6 +136,7 @@ export default () => {
created() {
this.setInitialBoardData({
boardId: $boardApp.dataset.boardId,
fullBoardId: fullBoardId($boardApp.dataset.boardId),
fullPath: $boardApp.dataset.fullPath,
boardType: this.parent,
disabled: this.disabled,
......
......@@ -9,10 +9,11 @@ import {
subscriptionQueries,
SupportedFilters,
deleteListQueries,
listsQuery,
updateListQueries,
issuableTypes,
} from 'ee_else_ce/boards/constants';
import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql';
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
......@@ -21,7 +22,6 @@ import { s__ } from '~/locale';
import {
formatBoardLists,
formatListIssues,
fullBoardId,
formatListsPageInfo,
formatIssue,
formatIssueInput,
......@@ -86,24 +86,22 @@ export default {
}
},
fetchLists: ({ dispatch }) => {
dispatch('fetchIssueLists');
},
fetchIssueLists: ({ commit, state, dispatch }) => {
const { boardType, filterParams, fullPath, boardId } = state;
fetchLists: ({ commit, state, dispatch }) => {
const { boardType, filterParams, fullPath, fullBoardId, issuableType } = state;
const variables = {
fullPath,
boardId: fullBoardId(boardId),
boardId: fullBoardId,
filters: filterParams,
...(issuableType === issuableTypes.issue && {
isGroup: boardType === BoardType.group,
isProject: boardType === BoardType.project,
}),
};
return gqlClient
.query({
query: boardListsQuery,
query: listsQuery[issuableType].query,
variables,
})
.then(({ data }) => {
......@@ -137,7 +135,7 @@ export default {
{ state, commit, dispatch, getters },
{ backlog, labelId, milestoneId, assigneeId, iterationId },
) => {
const { boardId } = state;
const { fullBoardId } = state;
const existingList = getters.getListByLabelId(labelId);
......@@ -150,7 +148,7 @@ export default {
.mutate({
mutation: createBoardListMutation,
variables: {
boardId: fullBoardId(boardId),
boardId: fullBoardId,
backlog,
labelId,
milestoneId,
......@@ -296,11 +294,11 @@ export default {
fetchItemsForList: ({ state, commit }, { listId, fetchNext = false }) => {
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { fullPath, boardId, boardType, filterParams } = state;
const { fullPath, fullBoardId, boardType, filterParams } = state;
const variables = {
fullPath,
boardId: fullBoardId(boardId),
boardId: fullBoardId,
id: listId,
filters: filterParams,
isGroup: boardType === BoardType.group,
......@@ -429,7 +427,7 @@ export default {
try {
const { itemId, fromListId, toListId, moveBeforeId, moveAfterId } = moveData;
const {
boardId,
fullBoardId,
boardItems: {
[itemId]: { iid, referencePath },
},
......@@ -440,7 +438,7 @@ export default {
variables: {
iid,
projectPath: referencePath.split(/[#]/)[0],
boardId: fullBoardId(boardId),
boardId: fullBoardId,
fromListId: getIdFromGraphQLId(fromListId),
toListId: getIdFromGraphQLId(toListId),
moveBeforeId,
......
......@@ -40,8 +40,9 @@ export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId
export default {
[mutationTypes.SET_INITIAL_BOARD_DATA](state, data) {
const { boardType, disabled, boardId, fullPath, boardConfig, issuableType } = data;
const { boardType, disabled, boardId, fullBoardId, fullPath, boardConfig, issuableType } = data;
state.boardId = boardId;
state.fullBoardId = fullBoardId;
state.fullPath = fullPath;
state.boardType = boardType;
state.disabled = disabled;
......
/* eslint-disable import/export */
import { issuableTypes } from '~/boards/constants';
import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import updateBoardListMutation from '~/boards/graphql/board_list_update.mutation.graphql';
import { s__ } from '~/locale';
import boardListsQuery from './graphql/board_lists.query.graphql';
import destroyEpicBoardListMutation from './graphql/epic_board_list_destroy.mutation.graphql';
import updateEpicBoardListMutation from './graphql/epic_board_list_update.mutation.graphql';
import epicBoardListsQuery from './graphql/epic_board_lists.query.graphql';
export * from '~/boards/constants';
export const DRAGGABLE_TAG = 'div';
......@@ -61,6 +66,15 @@ export const ErrorMessages = {
),
};
export const listsQuery = {
[issuableTypes.issue]: {
query: boardListsQuery,
},
[issuableTypes.epic]: {
query: epicBoardListsQuery,
},
};
export const updateListQueries = {
[issuableTypes.issue]: {
mutation: updateBoardListMutation,
......@@ -79,22 +93,7 @@ export const deleteListQueries = {
},
};
// re-export some FOSS constants so that lint does not yell
// https://gitlab.com/gitlab-org/gitlab/-/issues/329164
export {
BoardType,
ListType,
inactiveId,
flashAnimationDuration,
ISSUABLE,
titleQueries,
subscriptionQueries,
SupportedFilters,
} from '~/boards/constants';
export default {
deleteListQueries,
updateListQueries,
DRAGGABLE_TAG,
EpicFilterType,
};
......@@ -2,7 +2,7 @@
query ListEpics($fullPath: ID!, $boardId: BoardsEpicBoardID!) {
group(fullPath: $fullPath) {
epicBoard(id: $boardId) {
board: epicBoard(id: $boardId) {
lists {
nodes {
...EpicBoardListFragment
......
import {
formatBoardLists,
formatListIssues,
formatListsPageInfo,
fullBoardId,
......@@ -10,10 +9,9 @@ import {
import { BoardType, SupportedFilters } from '~/boards/constants';
import eventHub from '~/boards/eventhub';
import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
import actionsCE from '~/boards/stores/actions';
import actionsCE, { gqlClient } from '~/boards/stores/actions';
import boardsStore from '~/boards/stores/boards_store';
import * as typesCE from '~/boards/stores/mutation_types';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
import {
historyPushState,
......@@ -36,7 +34,6 @@ import {
} from '../constants';
import epicQuery from '../graphql/epic.query.graphql';
import createEpicBoardListMutation from '../graphql/epic_board_list_create.mutation.graphql';
import epicBoardListsQuery from '../graphql/epic_board_lists.query.graphql';
import epicMoveListMutation from '../graphql/epic_move_list.mutation.graphql';
import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql';
import groupBoardAssigneesQuery from '../graphql/group_board_assignees.query.graphql';
......@@ -60,13 +57,6 @@ const notImplemented = () => {
throw new Error('Not implemented!');
};
export const gqlClient = createGqClient(
{},
{
fetchPolicy: fetchPolicies.NO_CACHE,
},
);
const fetchAndFormatListIssues = (state, extraVariables) => {
const { fullPath, boardId, boardType, filterParams } = state;
......@@ -117,6 +107,8 @@ const fetchAndFormatListEpics = (state, extraVariables) => {
});
};
export { gqlClient };
export default {
...actionsCE,
......@@ -161,7 +153,7 @@ export default {
dispatch('resetEpics');
dispatch('resetIssues');
dispatch('fetchEpicsSwimlanes');
dispatch('fetchIssueLists');
dispatch('fetchLists');
} else if (gon.features.graphqlBoardLists || getters.isEpicBoard) {
dispatch('fetchLists');
dispatch('resetIssues');
......@@ -351,7 +343,7 @@ export default {
}),
);
dispatch('fetchEpicsSwimlanes');
dispatch('fetchIssueLists');
dispatch('fetchLists');
} else if (!gon.features.graphqlBoardLists) {
historyPushState(removeParams(['group_by']), window.location.href, true);
boardsStore.create();
......@@ -538,35 +530,6 @@ export default {
);
},
fetchLists: ({ getters, dispatch }) => {
if (!getters.isEpicBoard) {
dispatch('fetchIssueLists');
} else {
dispatch('fetchEpicLists');
}
},
fetchEpicLists: ({ commit, state }) => {
const { filterParams, fullPath, boardId } = state;
const variables = {
fullPath,
boardId: fullEpicBoardId(boardId),
filters: filterParams,
};
return gqlClient
.query({
query: epicBoardListsQuery,
variables,
})
.then(({ data }) => {
const { lists } = data.group?.epicBoard;
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, formatBoardLists(lists));
})
.catch(() => commit(types.RECEIVE_BOARD_LISTS_FAILURE));
},
fetchMilestones({ state, commit }, searchTerm) {
commit(types.RECEIVE_MILESTONES_REQUEST);
......
......@@ -6,7 +6,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { mapActions, mapState } from 'vuex';
import { transformBoardConfig } from 'ee_component/boards/boards_util';
import { fullEpicBoardId, transformBoardConfig } from 'ee_component/boards/boards_util';
import BoardSidebar from 'ee_component/boards/components/board_sidebar';
import toggleLabels from 'ee_component/boards/toggle_labels';
......@@ -99,6 +99,7 @@ export default () => {
created() {
this.setInitialBoardData({
boardId: $boardApp.dataset.boardId,
fullBoardId: fullEpicBoardId($boardApp.dataset.boardId),
fullPath: $boardApp.dataset.fullPath,
boardType: this.parent,
disabled: this.disabled,
......
......@@ -2,7 +2,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Vue from 'vue';
import Vuex from 'vuex';
import { GroupByParamType } from 'ee/boards/constants';
import { BoardType, GroupByParamType, listsQuery, issuableTypes } from 'ee/boards/constants';
import actions, { gqlClient } from 'ee/boards/stores/actions';
import boardsStoreEE from 'ee/boards/stores/boards_store_ee';
import * as types from 'ee/boards/stores/mutation_types';
......@@ -10,8 +10,7 @@ import mutations from 'ee/boards/stores/mutations';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import { mockMoveIssueParams, mockMoveData, mockMoveState } from 'jest/boards/mock_data';
import { formatBoardLists, formatListIssues } from '~/boards/boards_util';
import { issuableTypes } from '~/boards/constants';
import { formatListIssues } from '~/boards/boards_util';
import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
import * as typesCE from '~/boards/stores/mutation_types';
import * as commonUtils from '~/lib/utils/common_utils';
......@@ -137,7 +136,7 @@ describe('performSearch', () => {
});
});
it('should dispatch setFilters, resetEpics, fetchEpicsSwimlanes, fetchIssueLists and resetIssues action when isSwimlanesOn', async () => {
it('should dispatch setFilters, resetEpics, fetchEpicsSwimlanes, fetchLists and resetIssues action when isSwimlanesOn', async () => {
const getters = { isSwimlanesOn: true };
await testAction({
action: actions.performSearch,
......@@ -147,81 +146,61 @@ describe('performSearch', () => {
{ type: 'resetEpics' },
{ type: 'resetIssues' },
{ type: 'fetchEpicsSwimlanes' },
{ type: 'fetchIssueLists' },
{ type: 'fetchLists' },
],
});
});
});
describe('fetchLists', () => {
it('should dispatch fetchIssueLists action when isEpicBoard is false', async () => {
const getters = { isEpicBoard: false };
await testAction({
action: actions.fetchLists,
state: { issuableType: issuableTypes.issue, ...getters },
expectedActions: [{ type: 'fetchIssueLists' }],
});
});
it('should dispatch fetchEpicLists action when isEpicBoard is true', async () => {
const getters = { isEpicBoard: true };
await testAction({
action: actions.fetchLists,
state: { issuableType: issuableTypes.epic, ...getters },
expectedActions: [{ type: 'fetchEpicLists' }],
});
});
});
describe('fetchEpicLists', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
filterParams: {},
};
const queryResponse = {
data: {
group: {
epicBoard: {
board: {
hideBacklogList: true,
lists: {
nodes: mockLists,
nodes: [mockLists[1]],
},
},
},
},
};
const formattedLists = formatBoardLists(queryResponse.data.group.epicBoard.lists);
it.each`
issuableType | boardType | fullBoardId | isGroup | isProject
${issuableTypes.epic} | ${BoardType.group} | ${'gid://gitlab/Boards::EpicBoard/1'} | ${undefined} | ${undefined}
`(
'calls $issuableType query with correct variables',
async ({ issuableType, boardType, fullBoardId, isGroup, isProject }) => {
const commit = jest.fn();
const dispatch = jest.fn();
it('should commit mutations RECEIVE_BOARD_LISTS_SUCCESS on success', async () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
const state = {
fullPath: 'gitlab-org',
fullBoardId,
filterParams: {},
boardType,
issuableType,
};
await testAction({
action: actions.fetchEpicLists,
state,
expectedMutations: [
{
type: types.RECEIVE_BOARD_LISTS_SUCCESS,
payload: formattedLists,
const variables = {
query: listsQuery[issuableType].query,
variables: {
fullPath: 'gitlab-org',
boardId: fullBoardId,
filters: {},
isGroup,
isProject,
},
],
});
});
};
it('should commit mutations RECEIVE_BOARD_LISTS_FAILURE on failure', async () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
await testAction({
action: actions.fetchEpicLists,
state,
expectedMutations: [
{
type: types.RECEIVE_BOARD_LISTS_FAILURE,
await actions.fetchLists({ commit, state, dispatch });
expect(gqlClient.query).toHaveBeenCalledWith(variables);
},
],
});
});
);
});
describe('fetchEpicsSwimlanes', () => {
......@@ -595,7 +574,7 @@ describe('toggleEpicSwimlanes', () => {
);
});
it('should dispatch fetchEpicsSwimlanes and fetchIssueLists actions when isShowingEpicsSwimlanes is true', () => {
it('should dispatch fetchEpicsSwimlanes and fetchLists actions when isShowingEpicsSwimlanes is true', () => {
global.jsdom.reconfigure({
url: `${TEST_HOST}/groups/gitlab-org/-/boards/1`,
});
......@@ -613,7 +592,7 @@ describe('toggleEpicSwimlanes', () => {
null,
state,
[{ type: types.TOGGLE_EPICS_SWIMLANES }],
[{ type: 'fetchEpicsSwimlanes' }, { type: 'fetchIssueLists' }],
[{ type: 'fetchEpicsSwimlanes' }, { type: 'fetchLists' }],
() => {
expect(commonUtils.historyPushState).toHaveBeenCalledWith(
mergeUrlParams({ group_by: GroupByParamType.epic }, window.location.href),
......
import * as Sentry from '@sentry/browser';
import {
inactiveId,
ISSUABLE,
ListType,
issuableTypes,
BoardType,
listsQuery,
} from 'ee_else_ce/boards/constants';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import testAction from 'helpers/vuex_action_helper';
import {
fullBoardId,
formatListIssues,
formatBoardLists,
formatIssueInput,
formatIssue,
getMoveData,
} from '~/boards/boards_util';
import { inactiveId, ISSUABLE, ListType, issuableTypes } from '~/boards/constants';
import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import issueCreateMutation from '~/boards/graphql/issue_create.mutation.graphql';
import actions, { gqlClient } from '~/boards/stores/actions';
......@@ -132,20 +138,12 @@ describe('setActiveId', () => {
});
describe('fetchLists', () => {
it('should dispatch fetchIssueLists action', () => {
testAction({
action: actions.fetchLists,
expectedActions: [{ type: 'fetchIssueLists' }],
});
});
});
describe('fetchIssueLists', () => {
const state = {
let state = {
fullPath: 'gitlab-org',
boardId: '1',
fullBoardId: 'gid://gitlab/Board/1',
filterParams: {},
boardType: 'group',
issuableType: 'issue',
};
let queryResponse = {
......@@ -167,7 +165,7 @@ describe('fetchIssueLists', () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchIssueLists,
actions.fetchLists,
{},
state,
[
......@@ -185,7 +183,7 @@ describe('fetchIssueLists', () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
testAction(
actions.fetchIssueLists,
actions.fetchLists,
{},
state,
[
......@@ -214,7 +212,7 @@ describe('fetchIssueLists', () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchIssueLists,
actions.fetchLists,
{},
state,
[
......@@ -227,6 +225,43 @@ describe('fetchIssueLists', () => {
done,
);
});
it.each`
issuableType | boardType | fullBoardId | isGroup | isProject
${issuableTypes.issue} | ${BoardType.group} | ${'gid://gitlab/Board/1'} | ${true} | ${false}
${issuableTypes.issue} | ${BoardType.project} | ${'gid://gitlab/Board/1'} | ${false} | ${true}
`(
'calls $issuableType query with correct variables',
async ({ issuableType, boardType, fullBoardId, isGroup, isProject }) => {
const commit = jest.fn();
const dispatch = jest.fn();
state = {
fullPath: 'gitlab-org',
fullBoardId,
filterParams: {},
boardType,
issuableType,
};
const variables = {
query: listsQuery[issuableType].query,
variables: {
fullPath: 'gitlab-org',
boardId: fullBoardId,
filters: {},
isGroup,
isProject,
},
};
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
await actions.fetchLists({ commit, state, dispatch });
expect(gqlClient.query).toHaveBeenCalledWith(variables);
},
);
});
describe('createList', () => {
......@@ -248,7 +283,7 @@ describe('createIssueList', () => {
beforeEach(() => {
state = {
fullPath: 'gitlab-org',
boardId: '1',
fullBoardId: 'gid://gitlab/Board/1',
boardType: 'group',
disabled: false,
boardLists: [{ type: 'closed' }],
......@@ -378,7 +413,7 @@ describe('moveList', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
fullBoardId: 'gid://gitlab/Board/1',
boardType: 'group',
disabled: false,
boardLists: initialBoardListsState,
......@@ -421,7 +456,7 @@ describe('moveList', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
fullBoardId: 'gid://gitlab/Board/1',
boardType: 'group',
disabled: false,
boardLists: initialBoardListsState,
......@@ -455,7 +490,7 @@ describe('updateList', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
fullBoardId: 'gid://gitlab/Board/1',
boardType: 'group',
disabled: false,
boardLists: [{ type: 'closed' }],
......@@ -573,7 +608,7 @@ describe('fetchItemsForList', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
fullBoardId: 'gid://gitlab/Board/1',
filterParams: {},
boardType: 'group',
};
......@@ -960,7 +995,7 @@ describe('updateIssueOrder', () => {
const state = {
boardItems: issues,
boardId: 'gid://gitlab/Board/1',
fullBoardId: 'gid://gitlab/Board/1',
};
const moveData = {
......@@ -974,7 +1009,7 @@ describe('updateIssueOrder', () => {
mutation: issueMoveListMutation,
variables: {
projectPath: getProjectPath(mockIssue.referencePath),
boardId: fullBoardId(state.boardId),
boardId: state.fullBoardId,
iid: mockIssue.iid,
fromListId: 1,
toListId: 2,
......
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