Commit 32a27fdf authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'lm-update-status-details-page' into 'master'

Update status on alert management detail view

See merge request gitlab-org/gitlab!31680
parents 210527c6 f0e998ae
<script> <script>
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { GlAlert, GlIcon, GlLoadingIcon, GlSprintf, GlTabs, GlTab, GlButton } from '@gitlab/ui'; import {
GlAlert,
GlIcon,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
GlSprintf,
GlTabs,
GlTab,
GlButton,
} from '@gitlab/ui';
import createFlash from '~/flash';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import query from '../graphql/queries/details.query.graphql'; import query from '../graphql/queries/details.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 glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ALERTS_SEVERITY_LABELS } from '../constants'; import { ALERTS_SEVERITY_LABELS } from '../constants';
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
export default { export default {
statuses: { statuses: {
...@@ -29,6 +42,8 @@ export default { ...@@ -29,6 +42,8 @@ export default {
GlIcon, GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlDropdown,
GlDropdownItem,
GlTab, GlTab,
GlTabs, GlTabs,
GlButton, GlButton,
...@@ -85,9 +100,28 @@ export default { ...@@ -85,9 +100,28 @@ export default {
}, },
}, },
methods: { methods: {
capitalizeFirstCharacter,
dismissError() { dismissError() {
this.isErrorDismissed = true; this.isErrorDismissed = true;
}, },
updateAlertStatus(status) {
this.$apollo
.mutate({
mutation: updateAlertStatus,
variables: {
iid: this.alertId,
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>
...@@ -97,7 +131,7 @@ export default { ...@@ -97,7 +131,7 @@ export default {
{{ $options.i18n.errorMsg }} {{ $options.i18n.errorMsg }}
</gl-alert> </gl-alert>
<div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div> <div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div>
<div v-if="alert" class="alert-management-details"> <div v-if="alert" class="alert-management-details gl-relative">
<div <div
class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-px-1 gl-py-6 gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid" class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-px-1 gl-py-6 gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid"
> >
...@@ -137,6 +171,28 @@ export default { ...@@ -137,6 +171,28 @@ export default {
> >
<h2 data-testid="title">{{ alert.title }}</h2> <h2 data-testid="title">{{ alert.title }}</h2>
</div> </div>
<gl-dropdown
:text="capitalizeFirstCharacter(alert.status.toLowerCase())"
class="gl-absolute gl-right-0"
right
>
<gl-dropdown-item
v-for="(label, field) in $options.statuses"
:key="field"
data-testid="statusDropdownItem"
class="gl-vertical-align-middle"
@click="updateAlertStatus(label)"
>
<span class="d-flex">
<gl-icon
class="flex-shrink-0 append-right-4"
:class="{ invisible: label.toUpperCase() !== alert.status }"
name="mobile-issue-close"
/>
{{ label }}
</span>
</gl-dropdown-item>
</gl-dropdown>
<gl-tabs v-if="alert" data-testid="alertDetailsTabs"> <gl-tabs v-if="alert" data-testid="alertDetailsTabs">
<gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle"> <gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle">
<ul class="pl-4 mb-n1"> <ul class="pl-4 mb-n1">
......
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 AlertDetails from './components/alert_details.vue'; import AlertDetails from './components/alert_details.vue';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -10,7 +11,20 @@ export default selector => { ...@@ -10,7 +11,20 @@ export default selector => {
const { alertId, projectPath, newIssuePath } = domEl.dataset; const { alertId, projectPath, newIssuePath } = domEl.dataset;
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);
},
},
},
),
}); });
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
......
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon, GlDropdownItem } from '@gitlab/ui';
import AlertDetails from '~/alert_management/components/alert_details.vue'; import AlertDetails from '~/alert_management/components/alert_details.vue';
import updateAlertStatus from '~/alert_management/graphql/mutations/update_alert_status.graphql';
import createFlash from '~/flash';
import mockAlerts from '../mocks/alerts.json'; import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0]; const mockAlert = mockAlerts[0];
jest.mock('~/flash');
describe('AlertDetails', () => { describe('AlertDetails', () => {
let wrapper; let wrapper;
const newIssuePath = 'root/alerts/-/issues/new'; const newIssuePath = 'root/alerts/-/issues/new';
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
function mountComponent({ function mountComponent({
data, data,
...@@ -31,6 +35,7 @@ describe('AlertDetails', () => { ...@@ -31,6 +35,7 @@ describe('AlertDetails', () => {
}, },
mocks: { mocks: {
$apollo: { $apollo: {
mutate: jest.fn(),
queries: { queries: {
alert: { alert: {
loading, loading,
...@@ -184,4 +189,50 @@ describe('AlertDetails', () => { ...@@ -184,4 +189,50 @@ describe('AlertDetails', () => {
}); });
}); });
}); });
describe('updating the alert status', () => {
const mockUpdatedMutationResult = {
data: {
updateAlertStatus: {
errors: [],
alert: {
status: 'acknowledged',
},
},
},
};
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alert: mockAlert },
loading: false,
});
});
it('calls `$apollo.mutate` with `updateAlertStatus` mutation and variables containing `iid`, `status`, & `projectPath`', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findStatusDropdownItem().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateAlertStatus,
variables: {
iid: 'alertId',
status: 'TRIGGERED',
projectPath: 'projectPath',
},
});
});
it('calls `createFlash` when request fails', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
findStatusDropdownItem().vm.$emit('click');
setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error while updating the status of the alert. Please try again.',
);
});
});
});
}); });
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