Commit 9eb0b043 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'alert-management-apollo-cache' into 'master'

Alert Management Sidebar Apollo Cache

See merge request gitlab-org/gitlab!35522
parents d3e1a56c 0a782fcf
...@@ -12,13 +12,15 @@ import { ...@@ -12,13 +12,15 @@ import {
GlTable, GlTable,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import query from '../graphql/queries/details.query.graphql'; import alertQuery from '../graphql/queries/details.query.graphql';
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user'; import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import initUserPopovers from '~/user_popovers'; import initUserPopovers from '~/user_popovers';
import { ALERTS_SEVERITY_LABELS, trackAlertsDetailsViewsOptions } from '../constants'; import { ALERTS_SEVERITY_LABELS, trackAlertsDetailsViewsOptions } from '../constants';
import createIssueQuery from '../graphql/mutations/create_issue_from_alert.graphql'; import createIssueMutation from '../graphql/mutations/create_issue_from_alert.graphql';
import toggleSidebarStatusMutation from '../graphql/mutations/toggle_sidebar_status.mutation.graphql';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility'; import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { toggleContainerClasses } from '~/lib/utils/dom_utils'; import { toggleContainerClasses } from '~/lib/utils/dom_utils';
...@@ -52,28 +54,27 @@ export default { ...@@ -52,28 +54,27 @@ export default {
AlertSidebar, AlertSidebar,
SystemNote, SystemNote,
}, },
props: { inject: {
projectPath: {
default: '',
},
alertId: { alertId: {
type: String, type: String,
required: true, default: '',
}, },
projectId: { projectId: {
type: String, type: String,
required: true, default: '',
},
projectPath: {
type: String,
required: true,
}, },
projectIssuesPath: { projectIssuesPath: {
type: String, type: String,
required: true, default: '',
}, },
}, },
apollo: { apollo: {
alert: { alert: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
query, query: alertQuery,
variables() { variables() {
return { return {
fullPath: this.projectPath, fullPath: this.projectPath,
...@@ -88,15 +89,18 @@ export default { ...@@ -88,15 +89,18 @@ export default {
Sentry.captureException(error); Sentry.captureException(error);
}, },
}, },
sidebarStatus: {
query: sidebarStatusQuery,
},
}, },
data() { data() {
return { return {
alert: null, alert: null,
errored: false, errored: false,
sidebarStatus: false,
isErrorDismissed: false, isErrorDismissed: false,
createIssueError: '', createIssueError: '',
issueCreationInProgress: false, issueCreationInProgress: false,
sidebarCollapsed: false,
sidebarErrorMessage: '', sidebarErrorMessage: '',
}; };
}, },
...@@ -132,10 +136,10 @@ export default { ...@@ -132,10 +136,10 @@ export default {
this.sidebarErrorMessage = ''; this.sidebarErrorMessage = '';
}, },
toggleSidebar() { toggleSidebar() {
this.sidebarCollapsed = !this.sidebarCollapsed; this.$apollo.mutate({ mutation: toggleSidebarStatusMutation });
toggleContainerClasses(containerEl, { toggleContainerClasses(containerEl, {
'right-sidebar-collapsed': this.sidebarCollapsed, 'right-sidebar-collapsed': !this.sidebarStatus,
'right-sidebar-expanded': !this.sidebarCollapsed, 'right-sidebar-expanded': this.sidebarStatus,
}); });
}, },
handleAlertSidebarError(errorMessage) { handleAlertSidebarError(errorMessage) {
...@@ -147,7 +151,7 @@ export default { ...@@ -147,7 +151,7 @@ export default {
this.$apollo this.$apollo
.mutate({ .mutate({
mutation: createIssueQuery, mutation: createIssueMutation,
variables: { variables: {
iid: this.alert.iid, iid: this.alert.iid,
projectPath: this.projectPath, projectPath: this.projectPath,
...@@ -197,7 +201,7 @@ export default { ...@@ -197,7 +201,7 @@ export default {
<div <div
v-if="alert" v-if="alert"
class="alert-management-details gl-relative" class="alert-management-details gl-relative"
:class="{ 'pr-sm-8': sidebarCollapsed }" :class="{ 'pr-sm-8': sidebarStatus }"
> >
<div <div
class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline gl-px-1 py-3 py-md-4 gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid flex-column flex-sm-row" class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline gl-px-1 py-3 py-md-4 gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid flex-column flex-sm-row"
...@@ -330,10 +334,7 @@ export default { ...@@ -330,10 +334,7 @@ export default {
</gl-tab> </gl-tab>
</gl-tabs> </gl-tabs>
<alert-sidebar <alert-sidebar
:project-path="projectPath"
:project-id="projectId"
:alert="alert" :alert="alert"
:sidebar-collapsed="sidebarCollapsed"
@alert-refresh="alertRefresh" @alert-refresh="alertRefresh"
@toggle-sidebar="toggleSidebar" @toggle-sidebar="toggleSidebar"
@alert-error="handleAlertSidebarError" @alert-error="handleAlertSidebarError"
......
...@@ -4,6 +4,8 @@ import SidebarTodo from './sidebar/sidebar_todo.vue'; ...@@ -4,6 +4,8 @@ import SidebarTodo from './sidebar/sidebar_todo.vue';
import SidebarStatus from './sidebar/sidebar_status.vue'; import SidebarStatus from './sidebar/sidebar_status.vue';
import SidebarAssignees from './sidebar/sidebar_assignees.vue'; import SidebarAssignees from './sidebar/sidebar_assignees.vue';
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
export default { export default {
components: { components: {
SidebarAssignees, SidebarAssignees,
...@@ -11,27 +13,34 @@ export default { ...@@ -11,27 +13,34 @@ export default {
SidebarTodo, SidebarTodo,
SidebarStatus, SidebarStatus,
}, },
props: { inject: {
sidebarCollapsed: { projectPath: {
type: Boolean, default: '',
required: true,
}, },
projectId: { projectId: {
type: String, type: String,
required: true, default: '',
},
projectPath: {
type: String,
required: true,
}, },
},
props: {
alert: { alert: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
apollo: {
sidebarStatus: {
query: sidebarStatusQuery,
},
},
data() {
return {
sidebarStatus: false,
};
},
computed: { computed: {
sidebarCollapsedClass() { sidebarCollapsedClass() {
return this.sidebarCollapsed ? 'right-sidebar-collapsed' : 'right-sidebar-expanded'; return this.sidebarStatus ? 'right-sidebar-collapsed' : 'right-sidebar-expanded';
}, },
}, },
}; };
...@@ -41,10 +50,10 @@ export default { ...@@ -41,10 +50,10 @@ export default {
<aside :class="sidebarCollapsedClass" class="right-sidebar alert-sidebar"> <aside :class="sidebarCollapsedClass" class="right-sidebar alert-sidebar">
<div class="issuable-sidebar js-issuable-update"> <div class="issuable-sidebar js-issuable-update">
<sidebar-header <sidebar-header
:sidebar-collapsed="sidebarCollapsed" :sidebar-collapsed="sidebarStatus"
@toggle-sidebar="$emit('toggle-sidebar')" @toggle-sidebar="$emit('toggle-sidebar')"
/> />
<sidebar-todo v-if="sidebarCollapsed" :sidebar-collapsed="sidebarCollapsed" /> <sidebar-todo v-if="sidebarStatus" :sidebar-collapsed="sidebarStatus" />
<sidebar-status <sidebar-status
:project-path="projectPath" :project-path="projectPath"
:alert="alert" :alert="alert"
...@@ -55,7 +64,7 @@ export default { ...@@ -55,7 +64,7 @@ export default {
:project-path="projectPath" :project-path="projectPath"
:project-id="projectId" :project-id="projectId"
:alert="alert" :alert="alert"
:sidebar-collapsed="sidebarCollapsed" :sidebar-collapsed="sidebarStatus"
@alert-refresh="$emit('alert-refresh')" @alert-refresh="$emit('alert-refresh')"
@toggle-sidebar="$emit('toggle-sidebar')" @toggle-sidebar="$emit('toggle-sidebar')"
@alert-error="$emit('alert-error', $event)" @alert-error="$emit('alert-error', $event)"
......
...@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo'; ...@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import AlertDetails from './components/alert_details.vue'; import AlertDetails from './components/alert_details.vue';
import sidebarStatusQuery from './graphql/queries/sidebar_status.query.graphql';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -10,39 +11,51 @@ export default selector => { ...@@ -10,39 +11,51 @@ export default selector => {
const domEl = document.querySelector(selector); const domEl = document.querySelector(selector);
const { alertId, projectPath, projectIssuesPath, projectId } = domEl.dataset; const { alertId, projectPath, projectIssuesPath, projectId } = domEl.dataset;
const resolvers = {
Mutation: {
toggleSidebarStatus: (_, __, { cache }) => {
const data = cache.readQuery({ query: sidebarStatusQuery });
data.sidebarStatus = !data.sidebarStatus;
cache.writeQuery({ query: sidebarStatusQuery, data });
},
},
};
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(resolvers, {
{}, cacheConfig: {
{ dataIdFromObject: object => {
cacheConfig: { // eslint-disable-next-line no-underscore-dangle
dataIdFromObject: object => { if (object.__typename === 'AlertManagementAlert') {
// eslint-disable-next-line no-underscore-dangle return object.iid;
if (object.__typename === 'AlertManagementAlert') { }
return object.iid; return defaultDataIdFromObject(object);
}
return defaultDataIdFromObject(object);
},
}, },
}, },
), }),
});
apolloProvider.clients.defaultClient.cache.writeData({
data: {
sidebarStatus: false,
},
}); });
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
el: selector, el: selector,
provide: {
projectPath,
alertId,
projectIssuesPath,
projectId,
},
apolloProvider, apolloProvider,
components: { components: {
AlertDetails, AlertDetails,
}, },
render(createElement) { render(createElement) {
return createElement('alert-details', { return createElement('alert-details', {});
props: {
alertId,
projectPath,
projectId,
projectIssuesPath,
},
});
}, },
}); });
}; };
mutation($projectPath: ID!, $assigneeUsernames: [String!]!, $iid: String!) { mutation alertSetAssignees($projectPath: ID!, $assigneeUsernames: [String!]!, $iid: String!) {
alertSetAssignees( alertSetAssignees(
input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $projectPath } input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $projectPath }
) { ) {
......
mutation ($projectPath: ID!, $iid: String!) { mutation createAlertIssue($projectPath: ID!, $iid: String!) {
createAlertIssue(input: { iid: $iid, projectPath: $projectPath }) { createAlertIssue(input: { iid: $iid, projectPath: $projectPath }) {
errors errors
issue { issue {
......
mutation ($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) { mutation updateAlertStatus($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) {
updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) { updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {
errors errors
alert { alert {
......
...@@ -3,7 +3,7 @@ import { GlAlert, GlLoadingIcon, GlTable } from '@gitlab/ui'; ...@@ -3,7 +3,7 @@ import { GlAlert, GlLoadingIcon, GlTable } from '@gitlab/ui';
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import AlertDetails from '~/alert_management/components/alert_details.vue'; import AlertDetails from '~/alert_management/components/alert_details.vue';
import createIssueQuery from '~/alert_management/graphql/mutations/create_issue_from_alert.graphql'; import createIssueMutation from '~/alert_management/graphql/mutations/create_issue_from_alert.graphql';
import { joinPaths } from '~/lib/utils/url_utility'; import { joinPaths } from '~/lib/utils/url_utility';
import { import {
trackAlertsDetailsViewsOptions, trackAlertsDetailsViewsOptions,
...@@ -25,14 +25,14 @@ describe('AlertDetails', () => { ...@@ -25,14 +25,14 @@ describe('AlertDetails', () => {
function mountComponent({ data, loading = false, mountMethod = shallowMount, stubs = {} } = {}) { function mountComponent({ data, loading = false, mountMethod = shallowMount, stubs = {} } = {}) {
wrapper = mountMethod(AlertDetails, { wrapper = mountMethod(AlertDetails, {
propsData: { provide: {
alertId: 'alertId', alertId: 'alertId',
projectPath, projectPath,
projectIssuesPath, projectIssuesPath,
projectId, projectId,
}, },
data() { data() {
return { alert: { ...mockAlert }, ...data }; return { alert: { ...mockAlert }, sidebarStatus: false, ...data };
}, },
mocks: { mocks: {
$apollo: { $apollo: {
...@@ -41,6 +41,7 @@ describe('AlertDetails', () => { ...@@ -41,6 +41,7 @@ describe('AlertDetails', () => {
alert: { alert: {
loading, loading,
}, },
sidebarStatus: {},
}, },
}, },
}, },
...@@ -135,7 +136,7 @@ describe('AlertDetails', () => { ...@@ -135,7 +136,7 @@ describe('AlertDetails', () => {
it('should display "View issue" button that links the issue page when issue exists', () => { it('should display "View issue" button that links the issue page when issue exists', () => {
const issueIid = '3'; const issueIid = '3';
mountComponent({ mountComponent({
data: { alert: { ...mockAlert, issueIid } }, data: { alert: { ...mockAlert, issueIid }, sidebarStatus: false },
}); });
expect(findViewIssueBtn().exists()).toBe(true); expect(findViewIssueBtn().exists()).toBe(true);
expect(findViewIssueBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, issueIid)); expect(findViewIssueBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, issueIid));
...@@ -148,8 +149,11 @@ describe('AlertDetails', () => { ...@@ -148,8 +149,11 @@ describe('AlertDetails', () => {
mountMethod: mount, mountMethod: mount,
data: { alert: { ...mockAlert, issueIid } }, data: { alert: { ...mockAlert, issueIid } },
}); });
expect(findViewIssueBtn().exists()).toBe(false);
expect(findCreateIssueBtn().exists()).toBe(true); return wrapper.vm.$nextTick().then(() => {
expect(findViewIssueBtn().exists()).toBe(false);
expect(findCreateIssueBtn().exists()).toBe(true);
});
}); });
it('calls `$apollo.mutate` with `createIssueQuery`', () => { it('calls `$apollo.mutate` with `createIssueQuery`', () => {
...@@ -160,7 +164,7 @@ describe('AlertDetails', () => { ...@@ -160,7 +164,7 @@ describe('AlertDetails', () => {
findCreateIssueBtn().trigger('click'); findCreateIssueBtn().trigger('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: createIssueQuery, mutation: createIssueMutation,
variables: { variables: {
iid: mockAlert.iid, iid: mockAlert.iid,
projectPath, projectPath,
......
...@@ -11,20 +11,28 @@ describe('Alert Details Sidebar', () => { ...@@ -11,20 +11,28 @@ describe('Alert Details Sidebar', () => {
let wrapper; let wrapper;
let mock; let mock;
function mountComponent({ function mountComponent({ mountMethod = shallowMount, stubs = {}, alert = {} } = {}) {
sidebarCollapsed = true,
mountMethod = shallowMount,
stubs = {},
alert = {},
} = {}) {
wrapper = mountMethod(AlertSidebar, { wrapper = mountMethod(AlertSidebar, {
data() {
return {
sidebarStatus: false,
};
},
propsData: { propsData: {
alert, alert,
sidebarCollapsed, },
provide: {
projectPath: 'projectPath', projectPath: 'projectPath',
projectId: '1', projectId: '1',
}, },
stubs, stubs,
mocks: {
$apollo: {
queries: {
sidebarStatus: {},
},
},
},
}); });
} }
...@@ -42,7 +50,7 @@ describe('Alert Details Sidebar', () => { ...@@ -42,7 +50,7 @@ describe('Alert Details Sidebar', () => {
}); });
it('open as default', () => { it('open as default', () => {
expect(wrapper.props('sidebarCollapsed')).toBe(true); expect(wrapper.classes('right-sidebar-expanded')).toBe(true);
}); });
it('should render side bar assignee dropdown', () => { it('should render side bar assignee dropdown', () => {
......
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