Commit e69773c8 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'lm-update-status-list' into 'master'

Update status for alert in alert management list

See merge request gitlab-org/gitlab!31276
parents 9ee7ad43 2a043397
...@@ -5,17 +5,21 @@ import { ...@@ -5,17 +5,21 @@ import {
GlLoadingIcon, GlLoadingIcon,
GlTable, GlTable,
GlAlert, GlAlert,
GlNewDropdown, GlIcon,
GlNewDropdownItem, GlDropdown,
GlDropdownItem,
GlTabs, GlTabs,
GlTab, GlTab,
GlBadge, GlBadge,
} from '@gitlab/ui'; } from '@gitlab/ui';
import createFlash from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import getAlerts from '../graphql/queries/getAlerts.query.graphql'; import getAlerts from '../graphql/queries/getAlerts.query.graphql';
import { ALERTS_STATUS, ALERTS_STATUS_TABS } from '../constants'; import { ALERTS_STATUS, ALERTS_STATUS_TABS } from '../constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
const tdClass = 'table-col d-flex d-md-table-cell align-items-center'; const tdClass = 'table-col d-flex d-md-table-cell align-items-center';
...@@ -53,6 +57,8 @@ export default { ...@@ -53,6 +57,8 @@ export default {
}, },
{ {
key: 'status', key: 'status',
thClass: 'w-15p',
trClass: 'w-15p',
label: s__('AlertManagement|Status'), label: s__('AlertManagement|Status'),
tdClass: `${tdClass} rounded-bottom text-capitalize`, tdClass: `${tdClass} rounded-bottom text-capitalize`,
}, },
...@@ -70,8 +76,9 @@ export default { ...@@ -70,8 +76,9 @@ export default {
GlAlert, GlAlert,
GlDeprecatedButton, GlDeprecatedButton,
TimeAgo, TimeAgo,
GlNewDropdown, GlDropdown,
GlNewDropdownItem, GlDropdownItem,
GlIcon,
GlTabs, GlTabs,
GlTab, GlTab,
GlBadge, GlBadge,
...@@ -140,6 +147,25 @@ export default { ...@@ -140,6 +147,25 @@ export default {
filterALertsByStatus(tabIndex) { filterALertsByStatus(tabIndex) {
this.statusFilter = this.$options.statusTabs[tabIndex].status; this.statusFilter = this.$options.statusTabs[tabIndex].status;
}, },
capitalizeFirstCharacter,
updateAlertStatus(status, iid) {
this.$apollo
.mutate({
mutation: updateAlertStatus,
variables: {
iid,
status: status.toUpperCase(),
projectPath: this.projectPath,
},
})
.catch(() => {
createFlash(
s__(
'AlertManagement|There was an error while updating the status of the alert. Please try again.',
),
);
});
},
}, },
}; };
</script> </script>
...@@ -190,11 +216,26 @@ export default { ...@@ -190,11 +216,26 @@ export default {
</template> </template>
<template #cell(status)="{ item }"> <template #cell(status)="{ item }">
<gl-new-dropdown :text="item.status"> <gl-dropdown
<gl-new-dropdown-item v-for="(label, field) in $options.statuses" :key="field"> :text="capitalizeFirstCharacter(item.status.toLowerCase())"
{{ label }} class="w-100"
</gl-new-dropdown-item> right
</gl-new-dropdown> >
<gl-dropdown-item
v-for="(label, field) in $options.statuses"
:key="field"
@click="updateAlertStatus(label, item.iid)"
>
<span class="d-flex">
<gl-icon
class="flex-shrink-0 append-right-4"
:class="{ invisible: label.toUpperCase() !== item.status }"
name="mobile-issue-close"
/>
{{ label }}
</span>
</gl-dropdown-item>
</gl-dropdown>
</template> </template>
<template #empty> <template #empty>
......
mutation ($projectPath: ID!, $status: AlertManagementStatus!, $iid: String!) {
updateAlertStatus(input: { iid: $iid, status: $status, projectPath: $projectPath }) {
errors
alert {
iid,
status,
}
}
}
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import AlertManagementList from './components/alert_management_list.vue'; import AlertManagementList from './components/alert_management_list.vue';
...@@ -17,7 +18,20 @@ export default () => { ...@@ -17,7 +18,20 @@ export default () => {
userCanEnableAlertManagement = parseBoolean(userCanEnableAlertManagement); userCanEnableAlertManagement = parseBoolean(userCanEnableAlertManagement);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(
{},
{
cacheConfig: {
dataIdFromObject: object => {
// eslint-disable-next-line no-underscore-dangle
if (object.__typename === 'AlertManagementAlert') {
return object.iid;
}
return defaultDataIdFromObject(object);
},
},
},
),
}); });
return new Vue({ return new Vue({
......
...@@ -25,7 +25,6 @@ describe('Selection Summary component', () => { ...@@ -25,7 +25,6 @@ describe('Selection Summary component', () => {
const dismissButton = () => wrapper.find(GlButton); const dismissButton = () => wrapper.find(GlButton);
const dismissMessage = () => wrapper.find({ ref: 'dismiss-message' }); const dismissMessage = () => wrapper.find({ ref: 'dismiss-message' });
const formSelect = () => wrapper.find(GlFormSelect); const formSelect = () => wrapper.find(GlFormSelect);
const createComponent = ({ props = {}, data = defaultData, mocks = defaultMocks }) => { const createComponent = ({ props = {}, data = defaultData, mocks = defaultMocks }) => {
if (wrapper) { if (wrapper) {
throw new Error('Please avoid recreating components in the same spec'); throw new Error('Please avoid recreating components in the same spec');
......
...@@ -1796,6 +1796,9 @@ msgstr "" ...@@ -1796,6 +1796,9 @@ msgstr ""
msgid "AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear." msgid "AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear."
msgstr "" msgstr ""
msgid "AlertManagement|There was an error while updating the status of the alert. Please try again."
msgstr ""
msgid "AlertManagement|Tool" msgid "AlertManagement|Tool"
msgstr "" msgstr ""
......
...@@ -4,16 +4,20 @@ import { ...@@ -4,16 +4,20 @@ import {
GlTable, GlTable,
GlAlert, GlAlert,
GlLoadingIcon, GlLoadingIcon,
GlNewDropdown, GlDropdown,
GlBadge, GlBadge,
GlTab, GlTab,
GlDropdownItem,
} from '@gitlab/ui'; } from '@gitlab/ui';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import createFlash from '~/flash';
import AlertManagementList from '~/alert_management/components/alert_management_list.vue'; import AlertManagementList from '~/alert_management/components/alert_management_list.vue';
import { ALERTS_STATUS_TABS } from '../../../../app/assets/javascripts/alert_management/constants'; import { ALERTS_STATUS_TABS } from '../../../../app/assets/javascripts/alert_management/constants';
import updateAlertStatus from '~/alert_management/graphql/mutations/update_alert_status.graphql';
import mockAlerts from '../mocks/alerts.json'; import mockAlerts from '../mocks/alerts.json';
jest.mock('~/flash');
describe('AlertManagementList', () => { describe('AlertManagementList', () => {
let wrapper; let wrapper;
...@@ -21,10 +25,11 @@ describe('AlertManagementList', () => { ...@@ -21,10 +25,11 @@ describe('AlertManagementList', () => {
const findAlerts = () => wrapper.findAll('table tbody tr'); const findAlerts = () => wrapper.findAll('table tbody tr');
const findAlert = () => wrapper.find(GlAlert); const findAlert = () => wrapper.find(GlAlert);
const findLoader = () => wrapper.find(GlLoadingIcon); const findLoader = () => wrapper.find(GlLoadingIcon);
const findStatusDropdown = () => wrapper.find(GlNewDropdown); const findStatusDropdown = () => wrapper.find(GlDropdown);
const findStatusFilterTabs = () => wrapper.findAll(GlTab); const findStatusFilterTabs = () => wrapper.findAll(GlTab);
const findNumberOfAlertsBadge = () => wrapper.findAll(GlBadge); const findNumberOfAlertsBadge = () => wrapper.findAll(GlBadge);
const findDateFields = () => wrapper.findAll(TimeAgo); const findDateFields = () => wrapper.findAll(TimeAgo);
const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
function mountComponent({ function mountComponent({
props = { props = {
...@@ -51,6 +56,7 @@ describe('AlertManagementList', () => { ...@@ -51,6 +56,7 @@ describe('AlertManagementList', () => {
}, },
mocks: { mocks: {
$apollo: { $apollo: {
mutate: jest.fn(),
queries: { queries: {
alerts: { alerts: {
loading, loading,
...@@ -191,6 +197,7 @@ describe('AlertManagementList', () => { ...@@ -191,6 +197,7 @@ describe('AlertManagementList', () => {
alerts: [ alerts: [
{ {
iid: 1, iid: 1,
status: 'acknowledged',
startedAt: '2020-03-17T23:18:14.996Z', startedAt: '2020-03-17T23:18:14.996Z',
endedAt: '2020-04-17T23:18:14.996Z', endedAt: '2020-04-17T23:18:14.996Z',
}, },
...@@ -209,6 +216,7 @@ describe('AlertManagementList', () => { ...@@ -209,6 +216,7 @@ describe('AlertManagementList', () => {
alerts: [ alerts: [
{ {
iid: 1, iid: 1,
status: 'acknowledged',
startedAt: null, startedAt: null,
endedAt: null, endedAt: null,
}, },
...@@ -221,4 +229,52 @@ describe('AlertManagementList', () => { ...@@ -221,4 +229,52 @@ describe('AlertManagementList', () => {
}); });
}); });
}); });
describe('updating the alert status', () => {
const iid = '1527542';
const mockUpdatedMutationResult = {
data: {
updateAlertStatus: {
errors: [],
alert: {
iid,
status: 'acknowledged',
},
},
},
};
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
loading: false,
});
});
it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findFirstStatusOption().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateAlertStatus,
variables: {
iid,
status: 'TRIGGERED',
projectPath: 'gitlab-org/gitlab',
},
});
});
it('calls `createFlash` when request fails', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
findFirstStatusOption().vm.$emit('click');
setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error while updating the status of the alert. Please try again.',
);
});
});
});
}); });
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
"status": "triggered" "status": "triggered"
}, },
{ {
"iid": "1527542", "iid": "1527543",
"title": "Some otherr alert Some otherr alert Some otherr alert Some otherr alert Some otherr alert Some otherr alert", "title": "Some other alert Some other alert Some other alert Some other alert Some other alert Some other alert",
"severity": "MEDIUM", "severity": "MEDIUM",
"eventCount": 1, "eventCount": 1,
"startedAt": "2020-04-17T23:18:14.996Z", "startedAt": "2020-04-17T23:18:14.996Z",
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"status": "acknowledged" "status": "acknowledged"
}, },
{ {
"iid": "1527542", "iid": "1527544",
"title": "SyntaxError: Invalid or unexpected token", "title": "SyntaxError: Invalid or unexpected token",
"severity": "LOW", "severity": "LOW",
"eventCount": 4, "eventCount": 4,
......
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