Commit 7c29ff14 authored by Alexander Turinske's avatar Alexander Turinske

Use assignee sidebar widget in the alert drawer

- update assignee sidebar widget to fit the alert case
  not having a participant query
- update assignee to fit alert mutation not being
  standard
- update alerts to work with widget
- create alert assignees query for use with widget
- alias for response shape to match other
  queries
- alias mutation properties
- update other sidebar component to continue working
- make iid the alert unique identifier
- add tests
- Refactor alert details graphql queries
- removed assignees from the alert.fragment
- created a new alert details query without assignees
  and use it for the threat monitoring alert drawer
- renamed the old alert details query and added
  assignees with the user fragment to it for use just
  in the alert details page, using the user
- added assignees to get alert fragment and use the
  user fragment

Changelog: added
EE: true
parent eefe06ee
......@@ -11,12 +11,4 @@ fragment AlertListItem on AlertManagementAlert {
title
webUrl
}
assignees {
nodes {
name
username
avatarUrl
webUrl
}
}
}
#import "~/graphql_shared/fragments/alert.fragment.graphql"
#import "~/graphql_shared/fragments/user.fragment.graphql"
query getAlerts(
$projectPath: ID!
......@@ -26,6 +27,11 @@ query getAlerts(
) {
nodes {
...AlertListItem
assignees {
nodes {
...User
}
}
}
pageInfo {
hasNextPage
......
......@@ -16,6 +16,7 @@ export const IssuableType = {
Issue: 'issue',
Epic: 'epic',
MergeRequest: 'merge_request',
Alert: 'alert',
};
export const IssueStateEvent = {
......
......@@ -61,7 +61,7 @@ export default {
required: false,
default: IssuableType.Issue,
validator(value) {
return [IssuableType.Issue, IssuableType.MergeRequest].includes(value);
return [IssuableType.Issue, IssuableType.MergeRequest, IssuableType.Alert].includes(value);
},
},
issuableId: {
......
......@@ -19,6 +19,8 @@ import updateIssueConfidentialMutation from '~/sidebar/queries/update_issue_conf
import updateIssueDueDateMutation from '~/sidebar/queries/update_issue_due_date.mutation.graphql';
import updateIssueSubscriptionMutation from '~/sidebar/queries/update_issue_subscription.mutation.graphql';
import updateMergeRequestSubscriptionMutation from '~/sidebar/queries/update_merge_request_subscription.mutation.graphql';
import updateAlertAssigneesMutation from '~/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql';
import getAlertAssignees from '~/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql';
import getIssueAssignees from '~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql';
import issueParticipantsQuery from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql';
import getIssueTimelogsQuery from '~/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql';
......@@ -40,6 +42,10 @@ export const assigneesQueries = {
query: getMergeRequestAssignees,
mutation: updateMergeRequestAssigneesMutation,
},
[IssuableType.Alert]: {
query: getAlertAssignees,
mutation: updateAlertAssigneesMutation,
},
};
export const participantsQueries = {
......@@ -52,6 +58,10 @@ export const participantsQueries = {
[IssuableType.Epic]: {
query: epicParticipantsQuery,
},
[IssuableType.Alert]: {
query: '',
skipQuery: true,
},
};
export const confidentialityQueries = {
......
......@@ -24,7 +24,7 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { PAGE_CONFIG, SEVERITY_LEVELS } from '../constants';
import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql';
import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql';
import alertQuery from '../graphql/queries/alert_details.query.graphql';
import alertQuery from '../graphql/queries/alert_sidebar_details.query.graphql';
import sidebarStatusQuery from '../graphql/queries/alert_sidebar_status.query.graphql';
import AlertMetrics from './alert_metrics.vue';
import AlertSidebar from './alert_sidebar.vue';
......
......@@ -167,10 +167,10 @@ export default {
variables: {
iid: this.alert.iid,
assigneeUsernames: [this.isActive(assignees) ? '' : assignees],
projectPath: this.projectPath,
fullPath: this.projectPath,
},
})
.then(({ data: { alertSetAssignees: { errors } = [] } = {} } = {}) => {
.then(({ data: { issuableSetAssignees: { errors } = [] } = {} } = {}) => {
this.hideDropdown();
if (errors[0]) {
......
......@@ -4,7 +4,7 @@ import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.muta
import { s__ } from '~/locale';
import Todo from '~/sidebar/components/todo_toggle/todo.vue';
import createAlertTodoMutation from '../../graphql/mutations/alert_todo_create.mutation.graphql';
import alertQuery from '../../graphql/queries/alert_details.query.graphql';
import alertQuery from '../../graphql/queries/alert_sidebar_details.query.graphql';
export default {
i18n: {
......
#import "~/graphql_shared/fragments/alert_note.fragment.graphql"
#import "~/graphql_shared/fragments/user.fragment.graphql"
#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
mutation alertSetAssignees($projectPath: ID!, $assigneeUsernames: [String!]!, $iid: String!) {
alertSetAssignees(
input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $projectPath }
mutation alertSetAssignees($fullPath: ID!, $assigneeUsernames: [String!]!, $iid: String!) {
issuableSetAssignees: alertSetAssignees(
input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $fullPath }
) {
errors
alert {
issuable: alert {
iid
assignees {
nodes {
username
name
avatarUrl
webUrl
...User
...UserAvailability
}
}
notes {
......
#import "../fragments/alert_detail_item.fragment.graphql"
#import "~/graphql_shared/fragments/alert_detail_item.fragment.graphql"
#import "~/graphql_shared/fragments/user.fragment.graphql"
mutation alertTodoCreate($projectPath: ID!, $iid: String!) {
alertTodoCreate(input: { iid: $iid, projectPath: $projectPath }) {
errors
alert {
...AlertDetailItem
assignees {
nodes {
...User
}
}
}
}
}
#import "~/graphql_shared/fragments/alert_detail_item.fragment.graphql"
#import "~/graphql_shared/fragments/user.fragment.graphql"
query alertDetails($fullPath: ID!, $alertId: String) {
project(fullPath: $fullPath) {
alertManagementAlerts(iid: $alertId) {
nodes {
...AlertDetailItem
assignees {
nodes {
...User
}
}
}
}
}
}
#import "~/graphql_shared/fragments/user.fragment.graphql"
#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
query alertAssignees(
$domain: AlertManagementDomainFilter = threat_monitoring
$fullPath: ID!
$iid: String!
) {
workspace: project(fullPath: $fullPath) {
issuable: alertManagementAlert(domain: $domain, iid: $iid) {
iid
assignees {
nodes {
...User
...UserAvailability
}
}
}
}
}
......@@ -74,6 +74,9 @@ export default {
query() {
return participantsQueries[this.issuableType].query;
},
skip() {
return Boolean(participantsQueries[this.issuableType].skipQuery);
},
variables() {
return {
iid: this.iid,
......
<script>
import { GlAlert, GlButton, GlDrawer, GlLink, GlSkeletonLoader } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import getAlertDetailsQuery from '~/graphql_shared/queries/alert_details.query.graphql';
import { capitalizeFirstCharacter, splitCamelCase } from '~/lib/utils/text_utility';
import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarStatus from '~/vue_shared/alert_details/components/sidebar/sidebar_status.vue';
import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql';
import getAlertDetailsQuery from '~/vue_shared/alert_details/graphql/queries/alert_details.query.graphql';
import { getContentWrapperHeight } from '../../utils';
import { ALERT_DETAILS_LOADING_ROWS, DRAWER_ERRORS, HIDDEN_VALUES } from './constants';
......@@ -23,7 +24,9 @@ export default {
GlDrawer,
GlLink,
GlSkeletonLoader,
SidebarAssigneesWidget,
},
provide: { canUpdate: true },
inject: ['projectPath'],
apollo: {
alertDetails: {
......@@ -159,6 +162,13 @@ export default {
<gl-alert v-if="errored" variant="danger" :dismissible="false" contained>
{{ errorMessage }}
</gl-alert>
<sidebar-assignees-widget
issuable-type="alert"
:iid="selectedAlert.iid"
:full-path="projectPath"
:allow-multiple-assignees="false"
@assignees-updated="handleAlertUpdate"
/>
<sidebar-status
:alert="selectedAlert"
:project-path="projectPath"
......
......@@ -160,7 +160,7 @@ export default {
handleFilterChange(newFilters) {
this.filters = newFilters;
},
handleStatusUpdate() {
handleAlertUpdate() {
this.$apollo.queries.alerts.refetch();
},
hasAssignees(assignees) {
......@@ -287,7 +287,7 @@ export default {
:alert="item"
:project-path="projectPath"
@alert-error="handleAlertError"
@alert-update="handleStatusUpdate"
@alert-update="handleAlertUpdate"
/>
</template>
......@@ -320,8 +320,8 @@ export default {
v-if="selectedAlert"
:is-alert-drawer-open="isAlertDrawerOpen"
:selected-alert="selectedAlert"
@alert-update="handleStatusUpdate"
@deselect-alert="handleAlertDeselect"
@alert-update="handleAlertUpdate"
/>
</div>
</template>
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
......@@ -7,7 +8,20 @@ import createStore from './store';
Vue.use(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);
},
},
},
),
});
export default () => {
......
......@@ -7,9 +7,10 @@ import { DRAWER_ERRORS } from 'ee/threat_monitoring/components/alerts/constants'
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getAlertDetailsQuery from '~/graphql_shared/queries/alert_details.query.graphql';
import { visitUrl } from '~/lib/utils/url_utility';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarStatus from '~/vue_shared/alert_details/components/sidebar/sidebar_status.vue';
import getAlertDetailsQuery from '~/vue_shared/alert_details/graphql/queries/alert_details.query.graphql';
import {
erroredGetAlertDetailsQuerySpy,
getAlertDetailsQueryErrorMessage,
......@@ -44,6 +45,7 @@ describe('Alert Drawer', () => {
});
const findAlert = () => wrapper.findComponent(GlAlert);
const findAssignee = () => wrapper.findComponent(SidebarAssigneesWidget);
const findCreateIssueButton = () => wrapper.findByTestId('create-issue-button');
const findDrawer = () => wrapper.findComponent(GlDrawer);
const findIssueLink = () => wrapper.findByTestId('issue-link');
......@@ -84,7 +86,7 @@ describe('Alert Drawer', () => {
provide: {
projectPath: DEFAULT_PROJECT_PATH,
},
stubs: { GlDrawer },
stubs: { GlDrawer, SidebarAssigneesWidget: true, SidebarStatus: true },
...apolloOptions,
});
};
......@@ -94,6 +96,8 @@ describe('Alert Drawer', () => {
component | status | findComponent | state | mount
${'alert'} | ${'does not display'} | ${findAlert} | ${false} | ${undefined}
${'"Create Issue" button'} | ${'does not display'} | ${findCreateIssueButton} | ${false} | ${undefined}
${'assignee widget'} | ${'does display'} | ${findAssignee} | ${true} | ${undefined}
${'status widget'} | ${'does display'} | ${findStatus} | ${true} | ${undefined}
${'details list'} | ${'does display'} | ${findDetails} | ${true} | ${undefined}
${'drawer'} | ${'does display'} | ${findDrawer} | ${true} | ${undefined}
${'issue link'} | ${'does display'} | ${findIssueLink} | ${true} | ${undefined}
......@@ -163,6 +167,13 @@ describe('Alert Drawer', () => {
});
});
it('handles an alert assignee update', () => {
createWrapper({ props: { selectedAlert: mockAlerts[0] } });
expect(wrapper.emitted('alert-update')).toBeUndefined();
findAssignee().vm.$emit('assignees-updated');
expect(wrapper.emitted('alert-update')).toEqual([[]]);
});
it('handles an alert status update', () => {
createWrapper({ props: { selectedAlert: mockAlerts[0] } });
expect(wrapper.emitted('alert-update')).toBeUndefined();
......
......@@ -97,6 +97,7 @@ export const mockAlerts = [
assignees: {
nodes: [
{
id: 'Alert:1',
name: 'Administrator',
username: 'root',
avatarUrl: '/test-avatar-url',
......
......@@ -21,6 +21,7 @@ describe('Alert Details Sidebar Assignees', () => {
id: 1,
name: 'User 1',
username: 'root',
webUrl: 'https://gitlab:3443/root',
},
{
avatar_url:
......@@ -28,6 +29,7 @@ describe('Alert Details Sidebar Assignees', () => {
id: 2,
name: 'User 2',
username: 'not-root',
webUrl: 'https://gitlab:3443/non-root',
},
];
......@@ -128,7 +130,7 @@ describe('Alert Details Sidebar Assignees', () => {
variables: {
iid: '1527542',
assigneeUsernames: ['root'],
projectPath: 'projectPath',
fullPath: 'projectPath',
},
});
});
......@@ -137,7 +139,7 @@ describe('Alert Details Sidebar Assignees', () => {
wrapper.setData({ isDropdownSearching: false });
const errorMutationResult = {
data: {
alertSetAssignees: {
issuableSetAssignees: {
errors: ['There was a problem for sure.'],
alert: {},
},
......
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