Commit 230a348c authored by Coung Ngo's avatar Coung Ngo

Use GraphQL to query participants in epics

Epic participants were being loaded from a HAML template which caused
slowness in some cases. This commit changes the logic so participants
are loaded asynchronously using GraphQL.
parent 64b93ca8
...@@ -61,9 +61,11 @@ export default { ...@@ -61,9 +61,11 @@ export default {
}, },
mounted() { mounted() {
this.toggleSidebarFlag(epicUtils.getCollapsedGutter()); this.toggleSidebarFlag(epicUtils.getCollapsedGutter());
this.fetchEpicDetails();
}, },
methods: { methods: {
...mapActions([ ...mapActions([
'fetchEpicDetails',
'toggleSidebar', 'toggleSidebar',
'toggleSidebarFlag', 'toggleSidebarFlag',
'toggleStartDateType', 'toggleStartDateType',
......
query epicDetails($fullPath: ID!, $iid: ID!) {
group(fullPath: $fullPath) {
epic(iid: $iid) {
participants {
edges {
node {
name,
avatarUrl,
webUrl
}
}
}
}
}
}
...@@ -7,6 +7,7 @@ import { visitUrl } from '~/lib/utils/url_utility'; ...@@ -7,6 +7,7 @@ import { visitUrl } from '~/lib/utils/url_utility';
import epicUtils from '../utils/epic_utils'; import epicUtils from '../utils/epic_utils';
import { statusType, statusEvent, dateTypes } from '../constants'; import { statusType, statusEvent, dateTypes } from '../constants';
import epicDetailsQuery from '../queries/epicDetails.query.graphql';
import updateEpic from '../queries/updateEpic.mutation.graphql'; import updateEpic from '../queries/updateEpic.mutation.graphql';
import epicSetSubscription from '../queries/epicSetSubscription.mutation.graphql'; import epicSetSubscription from '../queries/epicSetSubscription.mutation.graphql';
...@@ -16,6 +17,33 @@ export const setEpicMeta = ({ commit }, meta) => commit(types.SET_EPIC_META, met ...@@ -16,6 +17,33 @@ export const setEpicMeta = ({ commit }, meta) => commit(types.SET_EPIC_META, met
export const setEpicData = ({ commit }, data) => commit(types.SET_EPIC_DATA, data); export const setEpicData = ({ commit }, data) => commit(types.SET_EPIC_DATA, data);
export const fetchEpicDetails = ({ state, dispatch }) => {
const variables = {
fullPath: state.fullPath,
iid: state.epicIid,
};
epicUtils.gqClient
.query({
query: epicDetailsQuery,
variables,
})
.then(({ data }) => {
const participants = data.group.epic.participants.edges.map(participant => ({
name: participant.node.name,
avatar_url: participant.node.avatarUrl,
web_url: participant.node.webUrl,
}));
dispatch('setEpicData', { participants });
})
.catch(() => dispatch('requestEpicParticipantsFailure'));
};
export const requestEpicParticipantsFailure = () => {
flash(__('There was an error getting the epic participants.'));
};
export const requestEpicStatusChange = ({ commit }) => commit(types.REQUEST_EPIC_STATUS_CHANGE); export const requestEpicStatusChange = ({ commit }) => commit(types.REQUEST_EPIC_STATUS_CHANGE);
export const requestEpicStatusChangeSuccess = ({ commit }, data) => export const requestEpicStatusChangeSuccess = ({ commit }, data) =>
......
...@@ -44,7 +44,6 @@ class EpicPresenter < Gitlab::View::Presenter::Delegated ...@@ -44,7 +44,6 @@ class EpicPresenter < Gitlab::View::Presenter::Delegated
def initial_data def initial_data
{ {
labels: epic.labels, labels: epic.labels,
participants: participants,
subscribed: subscribed? subscribed: subscribed?
} }
end end
...@@ -122,10 +121,6 @@ class EpicPresenter < Gitlab::View::Presenter::Delegated ...@@ -122,10 +121,6 @@ class EpicPresenter < Gitlab::View::Presenter::Delegated
} }
end end
def participants
UserEntity.represent(epic.participants)
end
def epic_pending_todo def epic_pending_todo
current_user.pending_todo_for(epic) if current_user current_user.pending_todo_for(epic) if current_user
end end
......
import Vue from 'vue'; import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import EpicSidebar from 'ee/epic/components/epic_sidebar.vue'; import EpicSidebar from 'ee/epic/components/epic_sidebar.vue';
import createStore from 'ee/epic/store'; import createStore from 'ee/epic/store';
...@@ -237,4 +238,19 @@ describe('EpicSidebarComponent', () => { ...@@ -237,4 +238,19 @@ describe('EpicSidebarComponent', () => {
expect(vm.$el.querySelector('.block.subscription')).not.toBeNull(); expect(vm.$el.querySelector('.block.subscription')).not.toBeNull();
}); });
}); });
describe('mounted', () => {
it('makes request to get epic details', () => {
const methodSpies = {
fetchEpicDetails: jest.fn(),
};
shallowMount(EpicSidebar, {
store,
methods: methodSpies,
});
expect(methodSpies.fetchEpicDetails).toHaveBeenCalled();
});
});
}); });
...@@ -43,6 +43,120 @@ describe('Epic Store Actions', () => { ...@@ -43,6 +43,120 @@ describe('Epic Store Actions', () => {
}); });
}); });
describe('fetchEpicDetails', () => {
let mock;
const payload = {
fullPath: 'gitlab-org',
iid: 8,
};
const gqlQueryResponse = {
group: {
epic: {
participants: {
edges: [
{
node: {
name: 'Jane Doe',
avatarUrl: 'https://example.com/avatar/jane-doe.jpg',
webUrl: 'https://example.com/user/jane-doe.jpg',
},
},
{
node: {
name: 'John Doe',
avatarUrl: 'https://example.com/avatar/john-doe.jpg',
webUrl: 'https://example.com/user/john-doe.jpg',
},
},
],
},
},
},
};
const formattedParticipants = [
{
name: 'Jane Doe',
avatar_url: 'https://example.com/avatar/jane-doe.jpg',
web_url: 'https://example.com/user/jane-doe.jpg',
},
{
name: 'John Doe',
avatar_url: 'https://example.com/avatar/john-doe.jpg',
web_url: 'https://example.com/user/john-doe.jpg',
},
];
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
it('dispatches setEpicData when request is successful', done => {
mock.onPut(/(.*)/).replyOnce(200, {});
jest.spyOn(epicUtils.gqClient, 'query').mockReturnValue(
Promise.resolve({
data: gqlQueryResponse,
}),
);
testAction(
actions.fetchEpicDetails,
payload,
state,
[],
[
{
type: 'setEpicData',
payload: { participants: formattedParticipants },
},
],
done,
);
});
it('dispatches requestEpicParticipantsFailure when request fails', done => {
mock.onPut(/(.*)/).replyOnce(500, {});
jest.spyOn(epicUtils.gqClient, 'query').mockReturnValue(Promise.resolve({}));
testAction(
actions.fetchEpicDetails,
payload,
state,
[],
[
{
type: 'requestEpicParticipantsFailure',
},
],
done,
);
});
});
describe('requestEpicParticipantsFailure', () => {
beforeEach(() => {
setFixtures('<div class="flash-container"></div>');
});
it('does not invoke any mutations or actions', done => {
testAction(actions.requestEpicParticipantsFailure, {}, state, [], [], done);
});
it('shows flash error', () => {
actions.requestEpicParticipantsFailure({ commit: () => {} });
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
'There was an error getting the epic participants.',
);
});
});
describe('requestEpicStatusChange', () => { describe('requestEpicStatusChange', () => {
it('should set status change flag', done => { it('should set status change flag', done => {
testAction( testAction(
......
...@@ -19176,6 +19176,9 @@ msgstr "" ...@@ -19176,6 +19176,9 @@ msgstr ""
msgid "There was an error gathering the chart data" msgid "There was an error gathering the chart data"
msgstr "" msgstr ""
msgid "There was an error getting the epic participants."
msgstr ""
msgid "There was an error loading users activity calendar." msgid "There was an error loading users activity calendar."
msgstr "" msgstr ""
......
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