Fix error handling to display them correctly without crashing
This commit is contained in:
@@ -4,6 +4,6 @@ import { AppModule } from './app.module';
|
|||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
app.enableCors();
|
app.enableCors();
|
||||||
await app.listen(3000);
|
await app.listen(3500);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
12
backend/src/models/RequestError.ts
Normal file
12
backend/src/models/RequestError.ts
Normal file
@@ -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;
|
||||||
@@ -31,7 +31,7 @@ export class SinusBotService {
|
|||||||
.then(token => this.bearer = token)
|
.then(token => this.bearer = token)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error(`Oh oh! Something went wrong while fetching Bearer token.`, 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(
|
logger.debug(
|
||||||
`Seems like I have my Bearer token!
|
`Seems like I have my Bearer token!
|
||||||
@@ -46,7 +46,7 @@ export class SinusBotService {
|
|||||||
.then(data => this.consumeTunakillResponse(data))
|
.then(data => this.consumeTunakillResponse(data))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error(`I couldn't fetch user data.`, 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[] {
|
private consumeTunakillResponse(data: any): TableEntry[] {
|
||||||
if (!(data !== null || data[0] !== null || data[0].data !== null)) {
|
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;
|
const response = data[0].data;
|
||||||
if (!(response.length > 0)) {
|
if (!(response.length > 0)) {
|
||||||
throw Error('User list is empty.')
|
throw Error('Response from SinusBot does not have any data to parse.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
@@ -134,7 +134,9 @@ export class SinusBotService {
|
|||||||
|
|
||||||
private checkStatus = response => {
|
private checkStatus = response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw Error(response.statusText);
|
let err = new RequestError(response.errorText);
|
||||||
|
err.response = response;
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,50 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
import UserStatsMockService from "./mock/UserStatsMockService";
|
import UserStatsMockService from "./mock/UserStatsMockService";
|
||||||
import UserStats from "./models/TableEntry";
|
|
||||||
import UserStatsService from "./services/UserStatsService";
|
import UserStatsService from "./services/UserStatsService";
|
||||||
import {findDOMNode} from "react-dom";
|
import {findDOMNode} from "react-dom";
|
||||||
import TableEntry from "./models/TableEntry";
|
import TableEntry from "./models/TableEntry";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
error?: any,
|
error?: string,
|
||||||
isLoaded: boolean,
|
isLoaded: boolean,
|
||||||
users?: UserStats[],
|
users?: TableEntry[],
|
||||||
mock?: UserStats[]
|
mock?: TableEntry[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
|
|
||||||
private apiService: UserStatsService = new UserStatsService();
|
private apiService: UserStatsService = new UserStatsService();
|
||||||
private mockService: UserStatsService = new UserStatsMockService();
|
private mockService = new UserStatsMockService();
|
||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
error: null,
|
error: undefined,
|
||||||
isLoaded: false,
|
isLoaded: false,
|
||||||
users: undefined,
|
users: undefined,
|
||||||
mock: undefined
|
mock: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.setState({isLoaded: false})
|
this.setState({isLoaded: false, mock: this.mockService.getStatsWithoutPromise()});
|
||||||
this.mockService.getStats()
|
// this.mockService.getStats()
|
||||||
.then(data => this.setState({
|
// .then(data => this.setState({
|
||||||
data: data,
|
// data: data,
|
||||||
mock: data
|
// mock: data
|
||||||
}));
|
// }))
|
||||||
|
// .finally(() => {
|
||||||
|
// });
|
||||||
this.apiService.getStats()
|
this.apiService.getStats()
|
||||||
.then(data => this.setState({
|
.then(data => this.setState({
|
||||||
isLoaded: true,
|
isLoaded: true,
|
||||||
users: data
|
users: data
|
||||||
}))
|
}))
|
||||||
.catch(error => this.setState({
|
.catch(error => {
|
||||||
isLoaded: true,
|
error.response.json()
|
||||||
error: error
|
.then((err: any) => this.setState({
|
||||||
}));
|
isLoaded: true,
|
||||||
|
error: err.error
|
||||||
|
}))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
@@ -73,6 +77,8 @@ export default class App extends React.Component {
|
|||||||
return this.createTableEntries(users);
|
return this.createTableEntries(users);
|
||||||
} else if (isLoaded && error != null && mock != null) {
|
} else if (isLoaded && error != null && mock != null) {
|
||||||
return this.createTableEntries(mock);
|
return this.createTableEntries(mock);
|
||||||
|
} else if (mock != null) {
|
||||||
|
return this.createTableEntries(mock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +86,7 @@ export default class App extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<p className="title">Humenius' TeamSpeak 3-Ranking</p>
|
<p className="title">Humenius' TeamSpeak 3-Ranking</p>
|
||||||
{ this.state.error != null ? <p className="error-message">Data could not be loaded. Please try again later!</p> : null}
|
{ this.state.error != null ? <p className="error-message"> { this.state.error } Please try again later!</p> : null}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -89,7 +95,7 @@ export default class App extends React.Component {
|
|||||||
<th>Online time</th>
|
<th>Online time</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className={this.state.error != null ? "blurred" : undefined}>
|
<tbody className={this.state.error != null || !this.state.isLoaded ? "blurred" : undefined}>
|
||||||
{this.renderTableData()}
|
{this.renderTableData()}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -19,4 +19,8 @@ export default class UserStatsMockService extends UserStatsService {
|
|||||||
async getStats(): Promise<TableEntry[]> {
|
async getStats(): Promise<TableEntry[]> {
|
||||||
return Promise.resolve(this.entries);
|
return Promise.resolve(this.entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStatsWithoutPromise(): TableEntry[] {
|
||||||
|
return this.entries;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
frontend/src/models/RequestError.ts
Normal file
3
frontend/src/models/RequestError.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default class RequestError extends Error {
|
||||||
|
response?: any;
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import TableEntry from "../models/TableEntry";
|
import TableEntry from "../models/TableEntry";
|
||||||
|
import RequestError from "../models/RequestError";
|
||||||
|
|
||||||
|
|
||||||
export default class UserStatsService {
|
export default class UserStatsService {
|
||||||
|
|
||||||
@@ -13,13 +15,16 @@ export default class UserStatsService {
|
|||||||
|
|
||||||
async getStats(): Promise<TableEntry[]> {
|
async getStats(): Promise<TableEntry[]> {
|
||||||
return fetch(this.apiURL, this.requestInit)
|
return fetch(this.apiURL, this.requestInit)
|
||||||
.then(res => this.checkResponse(res))
|
.then(res => UserStatsService.checkResponse(res))
|
||||||
.then(data => data.json())
|
.then(data => data.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkResponse(response: any): any {
|
private static checkResponse(response: any): any {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw Error(response.statusText);
|
console.log(response);
|
||||||
|
let error = new RequestError(response.statusText);
|
||||||
|
error.response = response;
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user