Improve error handling

Use existing mechanism to display run scan errors without blocking other
potential erros from being presented to the user (eg profile deletion
errors).
parent f2e9fbf3
<script>
import { GlButton } from '@gitlab/ui';
import * as Sentry from '~/sentry/wrapper';
import { redirectTo } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { ERROR_RUN_SCAN, ERROR_MESSAGES } from 'ee/on_demand_scans/settings';
import dastProfileRunMutation from '../graphql/dast_profile_run.mutation.graphql';
import ProfilesList from './dast_profiles_list.vue';
......@@ -18,13 +18,43 @@ export default {
type: String,
required: true,
},
errorMessage: {
type: String,
required: false,
default: '',
},
errorDetails: {
type: Array,
required: false,
default: () => [],
},
},
data: () => ({
isRunningScan: null,
hasRunScanError: false,
runScanErrors: [],
}),
computed: {
error() {
if (this.hasRunScanError) {
return {
errorMessage: ERROR_MESSAGES[ERROR_RUN_SCAN],
errorDetails: this.runScanErrors,
};
}
const { errorMessage, errorDetails } = this;
return { errorMessage, errorDetails };
},
},
watch: {
errorMessage() {
this.hasRunScanError = false;
},
},
methods: {
async runScan({ id }) {
this.isRunningScan = id;
this.hasRunScanError = false;
try {
const {
data: {
......@@ -41,24 +71,34 @@ export default {
});
if (errors.length) {
this.handleError();
this.handleRunScanError({ errors });
} else {
redirectTo(pipelineUrl);
}
} catch (error) {
this.handleError(error);
this.handleRunScanError(error);
}
},
handleError(error) {
handleRunScanError({ exception = null, errors = [] } = {}) {
this.isRunningScan = null;
createFlash({ message: ERROR_MESSAGES[ERROR_RUN_SCAN], error, captureError: true });
this.hasRunScanError = true;
this.runScanErrors = errors;
if (exception !== null) {
Sentry.captureException(exception);
}
},
},
};
</script>
<template>
<profiles-list :full-path="fullPath" v-bind="$attrs" v-on="$listeners">
<profiles-list
:full-path="fullPath"
:error-message="error.errorMessage"
:error-details="error.errorDetails"
v-bind="$attrs"
v-on="$listeners"
>
<!-- eslint-disable-next-line vue/valid-v-slot -->
<template #cell(dastScannerProfile.scanType)="{ value }">
<scan-type-badge :scan-type="value" />
......
import { mount, shallowMount } from '@vue/test-utils';
import { merge } from 'lodash';
import { ERROR_RUN_SCAN, ERROR_MESSAGES } from 'ee/on_demand_scans/settings';
import Component from 'ee/security_configuration/dast_profiles/components/dast_saved_scans_list.vue';
import ProfilesList from 'ee/security_configuration/dast_profiles/components/dast_profiles_list.vue';
import waitForPromises from 'helpers/wait_for_promises';
......@@ -124,31 +125,48 @@ describe('EE - DastSavedScansList', () => {
expect(createFlash).not.toHaveBeenCalled();
});
it('create a flash error on failure', async () => {
it('passes the error message down to the list on failure but does not block errors passed by the parent', async () => {
const initialErrorMessage = 'Initial error message';
const finalErrorMessage = 'Final error message';
createFullComponent({
propsData: { profiles: savedScans },
propsData: {
profiles: savedScans,
errorMessage: initialErrorMessage,
},
mocks: {
$apollo: {
mutate: jest.fn().mockRejectedValue(),
},
},
});
const profilesList = findProfileList();
expect(profilesList.props('errorMessage')).toBe(initialErrorMessage);
wrapper.findByTestId('dast-scan-run-button').trigger('click');
await waitForPromises();
expect(createFlash).toHaveBeenCalled();
expect(profilesList.props('errorMessage')).toBe(ERROR_MESSAGES[ERROR_RUN_SCAN]);
expect(redirectTo).not.toHaveBeenCalled();
await wrapper.setProps({ errorMessage: finalErrorMessage });
expect(profilesList.props('errorMessage')).toBe(finalErrorMessage);
});
it('create a flash error if the API responds with errors-as-data', async () => {
it('passes the error message and details down to the list if the API responds with errors-as-data', async () => {
const errors = ['error-as-data'];
createFullComponent({
propsData: { profiles: savedScans },
mocks: {
$apollo: {
mutate: jest.fn().mockResolvedValue({
dastProfileRun: {
pipelineUrl: null,
errors: ['error-as-data'],
data: {
dastProfileRun: {
pipelineUrl: null,
errors,
},
},
}),
},
......@@ -157,7 +175,8 @@ describe('EE - DastSavedScansList', () => {
wrapper.findByTestId('dast-scan-run-button').trigger('click');
await waitForPromises();
expect(createFlash).toHaveBeenCalled();
expect(findProfileList().props('errorMessage')).toBe(ERROR_MESSAGES[ERROR_RUN_SCAN]);
expect(findProfileList().props('errorDetails')).toBe(errors);
expect(redirectTo).not.toHaveBeenCalled();
});
});
......
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