Refactor catching of error from API : check the type of errors

This commit is contained in:
CEC 2022-04-19 16:50:21 +02:00
parent 891ad2c34a
commit e45fcb371c
5 changed files with 69 additions and 35 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,21 @@
import { z } from "zod";
/*
* WARNING! The original file is in /messages/JsonMessages.
* All other files are automatically copied from this file on container startup / build
*/
export const isErrorApiData = z.object({
type: z.string(),
code: z.string(),
title: z.string(),
subtitle: z.string(),
details: z.string(),
image: z.string(),
urlToRedirect: z.optional(z.nullable(z.string())),
buttonTitle: z.optional(z.nullable(z.string())),
timeToRetry: z.optional(z.nullable(z.bigint())),
canRetryManual: z.optional(z.nullable(z.boolean()))
});
export type ErrorApiData = z.infer<typeof isErrorApiData>;

View File

@ -1,6 +1,7 @@
import { Server } from "hyper-express"; import { Server } from "hyper-express";
import Response from "hyper-express/types/components/http/Response"; import Response from "hyper-express/types/components/http/Response";
import axios from "axios"; import axios from "axios";
import {isErrorApiData} from "../Messages/JsonMessages/ErrorApiData";
export class BaseHttpController { export class BaseHttpController {
constructor(protected app: Server) { constructor(protected app: Server) {
@ -31,14 +32,15 @@ export class BaseHttpController {
if (axios.isAxiosError(e) && e.response) { if (axios.isAxiosError(e) && e.response) {
res.status(e.response.status); res.status(e.response.status);
if (!e.response.data?.code) { const errorType = isErrorApiData.safeParse(e?.response?.data);
if (!errorType.success) {
res.send( res.send(
"An error occurred: " + "An error occurred: " +
e.response.status + e.response.status +
" " + " " +
(e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText) (e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText)
); );
} else res.json(e.response.data); } else res.json(errorType.data);
return; return;
} else { } else {
res.status(500); res.status(500);

View File

@ -41,6 +41,7 @@ import { localWokaService } from "../Services/LocalWokaService";
import { WebSocket } from "uWebSockets.js"; import { WebSocket } from "uWebSockets.js";
import { WokaDetail } from "../Messages/JsonMessages/PlayerTextures"; import { WokaDetail } from "../Messages/JsonMessages/PlayerTextures";
import { z } from "zod"; import { z } from "zod";
import {ErrorApiData, isErrorApiData} from "../Messages/JsonMessages/ErrorApiData";
/** /**
* The object passed between the "open" and the "upgrade" methods when opening a websocket * The object passed between the "open" and the "upgrade" methods when opening a websocket
@ -68,13 +69,21 @@ interface UpgradeData {
}; };
} }
interface UpgradeFailedData { interface UpgradeFailedInvalidData {
rejected: true; rejected: true;
reason: "tokenInvalid" | "textureInvalid" | "error" | null; reason: "tokenInvalid" | "textureInvalid" | null;
message: string; message: string;
roomId: string; roomId: string;
} }
interface UpgradeFailedErrorData {
rejected: true;
reason: "error";
error: ErrorApiData;
}
type UpgradeFailedData = UpgradeFailedErrorData | UpgradeFailedInvalidData;
export class IoSocketController { export class IoSocketController {
private nextUserId: number = 1; private nextUserId: number = 1;
@ -314,35 +323,23 @@ export class IoSocketController {
); );
} catch (err) { } catch (err) {
if (Axios.isAxiosError(err)) { if (Axios.isAxiosError(err)) {
if (err?.response?.status == 404 || !err?.response?.data.code) { const errorType = isErrorApiData.safeParse(err?.response?.data);
// If we get an HTTP 404, the token is invalid. Let's perform an anonymous login! if(errorType.success) {
return res.upgrade(
console.warn( {
'Cannot find user with email "' + rejected: true,
(userIdentifier || "anonymous") + reason: "error",
'". Performing an anonymous login instead.' status: err?.response?.status,
); error: errorType.data,
} else if (err?.response?.data.code) { } as UpgradeFailedData,
//OLD // If we get an HTTP 403, the world is full. We need to broadcast a special error to the client. websocketKey,
//OLD // we finish immediately the upgrade then we will close the socket as soon as it starts opening. websocketProtocol,
return res.upgrade( websocketExtensions,
{ context
rejected: true, );
reason: "error", }
message: err?.response?.data.code,
status: err?.response?.status,
error: err?.response?.data,
roomId,
} as UpgradeFailedData,
websocketKey,
websocketProtocol,
websocketExtensions,
context
);
}
} else {
throw err;
} }
throw err;
} }
memberMessages = userData.messages; memberMessages = userData.messages;
memberTags = userData.tags; memberTags = userData.tags;
@ -487,7 +484,7 @@ export class IoSocketController {
} else if (ws.reason === "textureInvalid") { } else if (ws.reason === "textureInvalid") {
socketManager.emitInvalidTextureMessage(ws); socketManager.emitInvalidTextureMessage(ws);
} else if (ws.reason === "error") { } else if (ws.reason === "error") {
socketManager.emitErrorScreenMessage(ws, ws.error as ErrorScreenMessage); socketManager.emitErrorScreenMessage(ws, ws.error);
} else { } else {
socketManager.emitConnexionErrorMessage(ws, ws.message); socketManager.emitConnexionErrorMessage(ws, ws.message);
} }

View File

@ -55,6 +55,7 @@ import Debug from "debug";
import { ExAdminSocketInterface } from "../Model/Websocket/ExAdminSocketInterface"; import { ExAdminSocketInterface } from "../Model/Websocket/ExAdminSocketInterface";
import { compressors } from "hyper-express"; import { compressors } from "hyper-express";
import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData";
import {ErrorApiData} from "../Messages/JsonMessages/ErrorApiData";
const debug = Debug("socket"); const debug = Debug("socket");
@ -644,9 +645,22 @@ export class SocketManager implements ZoneEventListener {
client.send(serverToClientMessage.serializeBinary().buffer, true); client.send(serverToClientMessage.serializeBinary().buffer, true);
} }
public emitErrorScreenMessage(client: compressors.WebSocket, error: ErrorScreenMessage) { public emitErrorScreenMessage(client: compressors.WebSocket, error: ErrorApiData) {
const errorMessage = new ErrorScreenMessage();
errorMessage.setType(error.type);
errorMessage.setCode(error.code);
errorMessage.setTitle(error.title);
errorMessage.setSubtitle(error.subtitle);
errorMessage.setDetails(error.details);
errorMessage.setImage(error.image);
if(error.urlToRedirect) errorMessage.setUrltoredirect(error.urlToRedirect);
if(error.buttonTitle) errorMessage.setButtontitle(error.buttonTitle);
if(!!error.canRetryManual) errorMessage.setCanretrymanual(error.canRetryManual);
if(error.timeToRetry) errorMessage.setTimetoretry(error.timeToRetry);
const serverToClientMessage = new ServerToClientMessage(); const serverToClientMessage = new ServerToClientMessage();
serverToClientMessage.setErrorscreenmessage(error); serverToClientMessage.setErrorscreenmessage(errorMessage);
//if (!client.disconnecting) { //if (!client.disconnecting) {
client.send(serverToClientMessage.serializeBinary().buffer, true); client.send(serverToClientMessage.serializeBinary().buffer, true);