Commit 64081ae6 authored by Savas Vedova's avatar Savas Vedova

Migrate vulnerability state management to GraphQL

- As of today, we're using a POST request to update the vulnerability
state. With these changes, the state change will be managed by
GraphQL.
- Add changelog
parent 18937395
import vulnerabilityConfirm from './vulnerability_confirm.mutation.graphql';
import vulnerabilityDismiss from './vulnerability_dismiss.mutation.graphql';
import vulnerabilityResolve from './vulnerability_resolve.mutation.graphql';
import vulnerabilityRevertToDetected from './vulnerability_revert_to_detected.mutation.graphql';
export default {
revert: vulnerabilityRevertToDetected,
dismiss: vulnerabilityDismiss,
confirm: vulnerabilityConfirm,
resolve: vulnerabilityResolve,
};
mutation($id: VulnerabilityID!) {
vulnerabilityConfirm(input: { id: $id }) {
errors
vulnerability {
id
state
confirmedAt
}
}
}
mutation($id: ID!, $comment: String!) { mutation($id: VulnerabilityID!, $comment: String!) {
vulnerabilityDismiss(input: { id: $id, comment: $comment }) { vulnerabilityDismiss(input: { id: $id, comment: $comment }) {
errors errors
vulnerability { vulnerability {
id id
state state
dismissedAt
} }
} }
} }
mutation($id: VulnerabilityID!) {
vulnerabilityResolve(input: { id: $id }) {
errors
vulnerability {
id
state
resolvedAt
}
}
}
mutation($id: VulnerabilityID!) {
vulnerabilityRevertToDetected(input: { id: $id }) {
errors
vulnerability {
id
state
detectedAt
}
}
}
...@@ -3,12 +3,10 @@ import { GlLoadingIcon, GlButton, GlBadge } from '@gitlab/ui'; ...@@ -3,12 +3,10 @@ import { GlLoadingIcon, GlButton, GlBadge } from '@gitlab/ui';
import Api from 'ee/api'; import Api from 'ee/api';
import { CancelToken } from 'axios'; import { CancelToken } from 'axios';
import SplitButton from 'ee/vue_shared/security_reports/components/split_button.vue'; import SplitButton from 'ee/vue_shared/security_reports/components/split_button.vue';
import vulnerabilityStateMutations from 'ee/security_dashboard/graphql/mutate_vulnerability_state';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import download from '~/lib/utils/downloader'; import download from '~/lib/utils/downloader';
import { import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
convertObjectPropsToSnakeCase,
convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
...@@ -18,6 +16,8 @@ import VulnerabilityStateDropdown from './vulnerability_state_dropdown.vue'; ...@@ -18,6 +16,8 @@ import VulnerabilityStateDropdown from './vulnerability_state_dropdown.vue';
import StatusDescription from './status_description.vue'; import StatusDescription from './status_description.vue';
import { VULNERABILITY_STATE_OBJECTS, FEEDBACK_TYPES, HEADER_ACTION_BUTTONS } from '../constants'; import { VULNERABILITY_STATE_OBJECTS, FEEDBACK_TYPES, HEADER_ACTION_BUTTONS } from '../constants';
const gidPrefix = 'gid://gitlab/Vulnerability/';
export default { export default {
name: 'VulnerabilityHeader', name: 'VulnerabilityHeader',
...@@ -109,7 +109,9 @@ export default { ...@@ -109,7 +109,9 @@ export default {
handler(state) { handler(state) {
const id = this.vulnerability[`${state}ById`]; const id = this.vulnerability[`${state}ById`];
if (id === undefined) return; // Don't do anything if there's no ID. if (!id) {
return;
}
this.isLoadingUser = true; this.isLoadingUser = true;
...@@ -132,25 +134,39 @@ export default { ...@@ -132,25 +134,39 @@ export default {
const fn = this[action]; const fn = this[action];
if (typeof fn === 'function') fn(); if (typeof fn === 'function') fn();
}, },
changeVulnerabilityState(newState) {
async changeVulnerabilityState({ action, payload }) {
this.isLoadingVulnerability = true; this.isLoadingVulnerability = true;
Api.changeVulnerabilityState(this.vulnerability.id, newState) try {
.then(({ data }) => { const { data } = await this.$apollo.mutate({
Object.assign(this.vulnerability, convertObjectPropsToCamelCase(data)); mutation: vulnerabilityStateMutations[action],
this.$emit('vulnerability-state-change'); variables: { id: `${gidPrefix}${this.vulnerability.id}`, ...payload },
})
.catch(() => {
createFlash(
s__(
'VulnerabilityManagement|Something went wrong, could not update vulnerability state.',
),
);
})
.finally(() => {
this.isLoadingVulnerability = false;
}); });
const [queryName] = Object.keys(data);
const { vulnerability } = data[queryName];
vulnerability.id = vulnerability.id.replace(gidPrefix, '');
vulnerability.state = vulnerability.state.toLowerCase();
this.vulnerability = {
...this.vulnerability,
...vulnerability,
};
this.$emit('vulnerability-state-change');
} catch (error) {
createFlash({
error,
captureError: true,
message: s__(
'VulnerabilityManagement|Something went wrong, could not update vulnerability state.',
),
});
} finally {
this.isLoadingVulnerability = false;
}
}, },
createMergeRequest() { createMergeRequest() {
this.isProcessingAction = true; this.isProcessingAction = true;
......
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
}, },
saveState(selectedState) { saveState(selectedState) {
this.$emit('change', selectedState.action); this.$emit('change', selectedState);
this.closeDropdown(); this.closeDropdown();
}, },
}, },
......
...@@ -4,6 +4,8 @@ import { ...@@ -4,6 +4,8 @@ import {
FEEDBACK_TYPE_MERGE_REQUEST, FEEDBACK_TYPE_MERGE_REQUEST,
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
const falsePositiveMessage = s__('VulnerabilityManagement|Will not fix or a false-positive');
export const VULNERABILITY_STATE_OBJECTS = { export const VULNERABILITY_STATE_OBJECTS = {
detected: { detected: {
action: 'revert', action: 'revert',
...@@ -16,7 +18,10 @@ export const VULNERABILITY_STATE_OBJECTS = { ...@@ -16,7 +18,10 @@ export const VULNERABILITY_STATE_OBJECTS = {
action: 'dismiss', action: 'dismiss',
state: 'dismissed', state: 'dismissed',
displayName: s__('Dismiss'), displayName: s__('Dismiss'),
description: s__('VulnerabilityManagement|Will not fix or a false-positive'), description: falsePositiveMessage,
payload: {
comment: falsePositiveMessage,
},
}, },
confirmed: { confirmed: {
action: 'confirm', action: 'confirm',
......
import Vue from 'vue'; import Vue from 'vue';
import App from 'ee/vulnerabilities/components/vulnerability.vue'; import App from 'ee/vulnerabilities/components/vulnerability.vue';
import apolloProvider from 'ee/security_dashboard/graphql/provider';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default el => { export default el => {
if (!el) { if (!el) {
return null; return null;
} }
const vulnerability = convertObjectPropsToCamelCase(JSON.parse(el.dataset.vulnerability), { const vulnerability = convertObjectPropsToCamelCase(JSON.parse(el.dataset.vulnerability), {
deep: true, deep: true,
}); });
return new Vue({ return new Vue({
el, el,
apolloProvider,
provide: { provide: {
reportType: vulnerability.reportType, reportType: vulnerability.reportType,
newIssueUrl: vulnerability.newIssueUrl, newIssueUrl: vulnerability.newIssueUrl,
...@@ -21,7 +23,6 @@ export default el => { ...@@ -21,7 +23,6 @@ export default el => {
issueTrackingHelpPath: vulnerability.issueTrackingHelpPath, issueTrackingHelpPath: vulnerability.issueTrackingHelpPath,
permissionsHelpPath: vulnerability.permissionsHelpPath, permissionsHelpPath: vulnerability.permissionsHelpPath,
}, },
render: h => render: h =>
h(App, { h(App, {
props: { vulnerability }, props: { vulnerability },
......
---
title: Migrate vulnerability state management to GraphQL
merge_request: 50034
author:
type: changed
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