diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 767736b5a4d53a44da323642d602f7634fb672b0..ff27c51addf78f4f50d1c550ff46d5cc19101b84 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,4 +1,5 @@ import axios, { AxiosInstance, AxiosResponse } from 'axios' +import { errorInterceptor } from '@/util/interceptor' import { Credentials } from '@/store/modules/authentication' import { Assignment, @@ -233,4 +234,6 @@ export async function fetchInstanceExportData (): Promise<InstanceExportData> { return (await ax.get('/api/instance/export')).data } +ax.interceptors.response.use(undefined, errorInterceptor); + export default ax diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index 4433b6be0f7129ee6d22ebd14774ba098cbdd981..4dc2172cf715c061ce4295173c75489b0b9f5669 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -135,6 +135,10 @@ export default { SubmissionNotes.SET_ORIG_FEEDBACK(feedback) this.$emit('feedbackCreated') }).catch(err => { + // ignore trivial errors as those are handled + // by an interceptor + if (err.message.includes("Request failed")) return + this.$notify({ title: 'Feedback creation Error!', text: err.message, diff --git a/frontend/src/util/helpers.ts b/frontend/src/util/helpers.ts index 8b55485f18d516ccee2c995635e7da2242fc26ce..6aff2308308c296c0cee8e7d902b8ba6f7226b14 100644 --- a/frontend/src/util/helpers.ts +++ b/frontend/src/util/helpers.ts @@ -1,4 +1,4 @@ -import { AxiosError } from 'axios' +import { AxiosError, AxiosResponse } from 'axios' import { Dispatch } from 'vuex' import { MutationHandler } from 'vuex-typex' @@ -139,3 +139,35 @@ export function syntaxPostProcess (code: string): string { }) return code } + +export function parseBlacklist (blacklist: Array<string>): string { + return blacklist.reduce((acc, curr) => { + return acc + "|" + curr + }) +} + +export function parseErrorNotification (response: AxiosResponse): string { + if (!response.data || Object.keys(response.data).length === 0) { + return 'There is no useful error data. Please ask the staff for help.' + } else { + let msg = "<ul>"; + function pickRecursive(obj: any) { + if (obj instanceof Object) { + for (let k of Object.keys(obj)) { + pickRecursive(obj[k]) + } + } else { + msg += "<li>" + obj + "</li>" + } + } + pickRecursive(response.data) + msg += "</ul>" + + if (response.status === 404) { + msg += "<br/>If you experience unusual behaviour, finish all unfinished work and relog." + + " If not, this is probably not a critical error." + } + + return msg + } +} diff --git a/frontend/src/util/interceptor.ts b/frontend/src/util/interceptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ceeedafcb5eab909b86e8bb31651ed6d9cfd22b --- /dev/null +++ b/frontend/src/util/interceptor.ts @@ -0,0 +1,24 @@ +import instance from '@/main' +import { parseErrorNotification, parseBlacklist } from '@/util/helpers' + +const errorUrlBlacklist = [ + "/api/get-token/", +] +const blackListRegExp = new RegExp(parseBlacklist(errorUrlBlacklist), "g") + +export function errorInterceptor (error: any): any { + // TODO: log errors and store them somewhere + + if (error.response.request.responseURL.match(blackListRegExp)) { + return + } + + instance.$notify({ + title: 'Request failed.', + text: parseErrorNotification(error.response), + type: 'error', + duration: -1 + }) + + return Promise.reject(error) +} \ No newline at end of file diff --git a/frontend/tests/unit/helpers/helpers.spec.ts b/frontend/tests/unit/helpers/helpers.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..18c7aa96b4cd485a17f188236aacd33f8e0c8d49 --- /dev/null +++ b/frontend/tests/unit/helpers/helpers.spec.ts @@ -0,0 +1,51 @@ +import * as helpers from "@/util/helpers" +import * as chai from "chai" +import { AxiosResponse } from 'axios'; + +chai.should(); + +describe("# Helpers Unit Tests", () => { + describe("# praseBlacklist", () => { + it("should correclty parse the blacklist object into a string that can be used for regex", () => { + const blacklist = ["test", "another", "final"] + const parsed = helpers.parseBlacklist(blacklist) + const testUrl = "https:somehost:someport/api/feedback/final" + const isMatched = testUrl.match(new RegExp(parsed, "g")) + // @ts-ignore + isMatched.should.not.be.null + parsed.should.equal("test|another|final") + }) + }) + describe("# parseErrorNotification", () => { + it("should return html parsed information about the error if existing", () => { + const response = { data: { someKey: "this is an error message" } } + const parsed = helpers.parseErrorNotification(response as AxiosResponse) + parsed.should.equal("<ul><li>this is an error message</li></ul>") + }) + it("should return html parsed list of all values of data object if existing", () => { + const response = { + data: { + someKey: "some debug information", + someOtherKey: "some error information", + someFinalKey: "some response stuff" + } + } + const parsed = helpers.parseErrorNotification(response as AxiosResponse) + const expected = "<ul><li>some debug information</li><li>some error information</li>" + + "<li>some response stuff</li></ul>" + parsed.should.equal(expected) + }) + it("should return default message when no error information exists", () => { + const response = { data: {}, someKey: "test" } + const parsed = helpers.parseErrorNotification(response as unknown as AxiosResponse) + parsed.should.equal("There is no useful error data. Please ask the staff for help.") + }) + it("should give additional information for 404 requests", () => { + const response = { status: 404, data: { someKey: "Not found." } } + const parsed = helpers.parseErrorNotification(response as AxiosResponse) + parsed.should.include("Not found.") + parsed.should.include("If you experience unusual behaviour, finish all unfinished work and relog." + + " If not, this is probably not a critical error.") + }) + }) +}) \ No newline at end of file