From eb2be6b94e8ea0e7c3a8d38d12b09824d864ad04 Mon Sep 17 00:00:00 2001 From: Humenius Date: Wed, 17 Jun 2020 18:48:33 +0200 Subject: [PATCH] Fix error handling to display them correctly without crashing --- backend/src/main.ts | 2 +- backend/src/models/RequestError.ts | 12 +++++++ backend/src/services/sinusbot.service.ts | 12 ++++--- frontend/src/App.tsx | 42 +++++++++++++---------- frontend/src/mock/UserStatsMockService.ts | 4 +++ frontend/src/models/RequestError.ts | 3 ++ frontend/src/services/UserStatsService.ts | 13 ++++--- 7 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 backend/src/models/RequestError.ts create mode 100644 frontend/src/models/RequestError.ts diff --git a/backend/src/main.ts b/backend/src/main.ts index da5451c..d5a2baa 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -4,6 +4,6 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors(); - await app.listen(3000); + await app.listen(3500); } bootstrap(); diff --git a/backend/src/models/RequestError.ts b/backend/src/models/RequestError.ts new file mode 100644 index 0000000..19dac85 --- /dev/null +++ b/backend/src/models/RequestError.ts @@ -0,0 +1,12 @@ +// @ts-ignore +interface RequestError extends Error { + response?: any; +} + +interface RequestErrorConstructor extends ErrorConstructor { + new(message?: string): RequestError; + (message?: string): RequestError; + readonly prototype: RequestError; +} + +declare var RequestError: RequestErrorConstructor; diff --git a/backend/src/services/sinusbot.service.ts b/backend/src/services/sinusbot.service.ts index 79d4269..f2b9620 100644 --- a/backend/src/services/sinusbot.service.ts +++ b/backend/src/services/sinusbot.service.ts @@ -31,7 +31,7 @@ export class SinusBotService { .then(token => this.bearer = token) .catch(error => { logger.error(`Oh oh! Something went wrong while fetching Bearer token.`, error); - throw this.createHttpException(HttpStatus.UNAUTHORIZED, error.message); + throw this.createHttpException(HttpStatus.UNAUTHORIZED, `Fetching Bearer token for Sinusbot failed.`); }); logger.debug( `Seems like I have my Bearer token! @@ -46,7 +46,7 @@ export class SinusBotService { .then(data => this.consumeTunakillResponse(data)) .catch(error => { logger.error(`I couldn't fetch user data.`, error); - throw this.createHttpException(error.statusCode, error.message); + throw this.createHttpException(HttpStatus.INTERNAL_SERVER_ERROR, error.message); }); } @@ -90,12 +90,12 @@ export class SinusBotService { private consumeTunakillResponse(data: any): TableEntry[] { if (!(data !== null || data[0] !== null || data[0].data !== null)) { - throw Error('Response from SinusBot does not have any data to parse.') + throw Error('Response from SinusBot does not have any data to parse.'); } const response = data[0].data; if (!(response.length > 0)) { - throw Error('User list is empty.') + throw Error('Response from SinusBot does not have any data to parse.'); } return response @@ -134,7 +134,9 @@ export class SinusBotService { private checkStatus = response => { if (!response.ok) { - throw Error(response.statusText); + let err = new RequestError(response.errorText); + err.response = response; + throw err; } return response; } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e7c791c..4223f90 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,46 +1,50 @@ import React from 'react'; import './App.scss'; import UserStatsMockService from "./mock/UserStatsMockService"; -import UserStats from "./models/TableEntry"; import UserStatsService from "./services/UserStatsService"; import {findDOMNode} from "react-dom"; import TableEntry from "./models/TableEntry"; interface State { - error?: any, + error?: string, isLoaded: boolean, - users?: UserStats[], - mock?: UserStats[] + users?: TableEntry[], + mock?: TableEntry[] } export default class App extends React.Component { private apiService: UserStatsService = new UserStatsService(); - private mockService: UserStatsService = new UserStatsMockService(); + private mockService = new UserStatsMockService(); state: State = { - error: null, + error: undefined, isLoaded: false, users: undefined, mock: undefined } componentDidMount() { - this.setState({isLoaded: false}) - this.mockService.getStats() - .then(data => this.setState({ - data: data, - mock: data - })); + this.setState({isLoaded: false, mock: this.mockService.getStatsWithoutPromise()}); + // this.mockService.getStats() + // .then(data => this.setState({ + // data: data, + // mock: data + // })) + // .finally(() => { + // }); this.apiService.getStats() .then(data => this.setState({ isLoaded: true, users: data })) - .catch(error => this.setState({ - isLoaded: true, - error: error - })); + .catch(error => { + error.response.json() + .then((err: any) => this.setState({ + isLoaded: true, + error: err.error + })) + }); } componentDidUpdate() { @@ -73,6 +77,8 @@ export default class App extends React.Component { return this.createTableEntries(users); } else if (isLoaded && error != null && mock != null) { return this.createTableEntries(mock); + } else if (mock != null) { + return this.createTableEntries(mock); } } @@ -80,7 +86,7 @@ export default class App extends React.Component { return (

Humenius' TeamSpeak 3-Ranking

- { this.state.error != null ?

Data could not be loaded. Please try again later!

: null} + { this.state.error != null ?

{ this.state.error } Please try again later!

: null} @@ -89,7 +95,7 @@ export default class App extends React.Component { - + {this.renderTableData()}
Online time
diff --git a/frontend/src/mock/UserStatsMockService.ts b/frontend/src/mock/UserStatsMockService.ts index 0f558e2..41de68a 100644 --- a/frontend/src/mock/UserStatsMockService.ts +++ b/frontend/src/mock/UserStatsMockService.ts @@ -19,4 +19,8 @@ export default class UserStatsMockService extends UserStatsService { async getStats(): Promise { return Promise.resolve(this.entries); } + + getStatsWithoutPromise(): TableEntry[] { + return this.entries; + } } diff --git a/frontend/src/models/RequestError.ts b/frontend/src/models/RequestError.ts new file mode 100644 index 0000000..376f0fc --- /dev/null +++ b/frontend/src/models/RequestError.ts @@ -0,0 +1,3 @@ +export default class RequestError extends Error { + response?: any; +} diff --git a/frontend/src/services/UserStatsService.ts b/frontend/src/services/UserStatsService.ts index f256c9f..e41b41d 100644 --- a/frontend/src/services/UserStatsService.ts +++ b/frontend/src/services/UserStatsService.ts @@ -1,4 +1,6 @@ import TableEntry from "../models/TableEntry"; +import RequestError from "../models/RequestError"; + export default class UserStatsService { @@ -13,13 +15,16 @@ export default class UserStatsService { async getStats(): Promise { return fetch(this.apiURL, this.requestInit) - .then(res => this.checkResponse(res)) - .then(data => data.json()) + .then(res => UserStatsService.checkResponse(res)) + .then(data => data.json()); } - private checkResponse(response: any): any { + private static checkResponse(response: any): any { if (!response.ok) { - throw Error(response.statusText); + console.log(response); + let error = new RequestError(response.statusText); + error.response = response; + throw error; } return response; }