Admin /api/map endpoint return type is now generated with generic type guards

This commit is contained in:
David Négrier 2021-07-16 09:52:51 +02:00
parent d0d191fc28
commit a4a123c331
9 changed files with 60 additions and 22 deletions

View File

@ -22,13 +22,14 @@ import { UserMovesMessage } from "../Messages/generated/messages_pb";
import { TemplatedApp } from "uWebSockets.js"; import { TemplatedApp } from "uWebSockets.js";
import { parse } from "query-string"; import { parse } from "query-string";
import { jwtTokenManager } from "../Services/JWTTokenManager"; import { jwtTokenManager } from "../Services/JWTTokenManager";
import { adminApi, CharacterTexture, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
import { SocketManager, socketManager } from "../Services/SocketManager"; import { SocketManager, socketManager } from "../Services/SocketManager";
import { emitInBatch } from "../Services/IoSocketHelpers"; import { emitInBatch } from "../Services/IoSocketHelpers";
import { ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; import { ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable";
import { Zone } from "_Model/Zone"; import { Zone } from "_Model/Zone";
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
import { v4 } from "uuid"; import { v4 } from "uuid";
import { CharacterTexture } from "../Services/AdminApi/CharacterTexture";
export class IoSocketController { export class IoSocketController {
private nextUserId: number = 1; private nextUserId: number = 1;

View File

@ -1,9 +1,10 @@
import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js";
import { BaseController } from "./BaseController"; import { BaseController } from "./BaseController";
import { parse } from "query-string"; import { parse } from "query-string";
import { adminApi, MapDetailsData } from "../Services/AdminApi"; import { adminApi } from "../Services/AdminApi";
import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import { ADMIN_API_URL } from "../Enum/EnvironmentVariable";
import { GameRoomPolicyTypes } from "../Model/PusherRoom"; import { GameRoomPolicyTypes } from "../Model/PusherRoom";
import { MapDetailsData } from "../Services/AdminApi/MapDetailsData";
export class MapController extends BaseController { export class MapController extends BaseController {
constructor(private App: TemplatedApp) { constructor(private App: TemplatedApp) {
@ -57,6 +58,7 @@ export class MapController extends BaseController {
policy_type: GameRoomPolicyTypes.ANONYMOUS_POLICY, policy_type: GameRoomPolicyTypes.ANONYMOUS_POLICY,
roomSlug: "", // Deprecated roomSlug: "", // Deprecated
tags: [], tags: [],
textures: [],
} as MapDetailsData) } as MapDetailsData)
); );

View File

@ -10,7 +10,6 @@ import {
SubMessage, SubMessage,
} from "../../Messages/generated/messages_pb"; } from "../../Messages/generated/messages_pb";
import { WebSocket } from "uWebSockets.js"; import { WebSocket } from "uWebSockets.js";
import { CharacterTexture } from "../../Services/AdminApi";
import { ClientDuplexStream } from "grpc"; import { ClientDuplexStream } from "grpc";
import { Zone } from "_Model/Zone"; import { Zone } from "_Model/Zone";

View File

@ -9,9 +9,9 @@ import {
SubMessage, SubMessage,
} from "../../Messages/generated/messages_pb"; } from "../../Messages/generated/messages_pb";
import { WebSocket } from "uWebSockets.js"; import { WebSocket } from "uWebSockets.js";
import { CharacterTexture } from "../../Services/AdminApi";
import { ClientDuplexStream } from "grpc"; import { ClientDuplexStream } from "grpc";
import { Zone } from "_Model/Zone"; import { Zone } from "_Model/Zone";
import { CharacterTexture } from "../../Services/AdminApi/CharacterTexture";
export type BackConnection = ClientDuplexStream<PusherToBackMessage, ServerToClientMessage>; export type BackConnection = ClientDuplexStream<PusherToBackMessage, ServerToClientMessage>;

View File

@ -1,6 +1,9 @@
import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable";
import Axios from "axios"; import Axios from "axios";
import { GameRoomPolicyTypes } from "_Model/PusherRoom"; import { GameRoomPolicyTypes } from "_Model/PusherRoom";
import { CharacterTexture } from "./AdminApi/CharacterTexture";
import { MapDetailsData } from "./AdminApi/MapDetailsData";
import { RoomRedirect } from "./AdminApi/RoomRedirect";
export interface AdminApiData { export interface AdminApiData {
roomUrl: string; roomUrl: string;
@ -12,25 +15,11 @@ export interface AdminApiData {
textures: CharacterTexture[]; textures: CharacterTexture[];
} }
export interface MapDetailsData {
roomSlug: string;
mapUrl: string;
policy_type: GameRoomPolicyTypes;
tags: string[];
}
export interface AdminBannedData { export interface AdminBannedData {
is_banned: boolean; is_banned: boolean;
message: string; message: string;
} }
export interface CharacterTexture {
id: number;
level: number;
url: string;
rights: string;
}
export interface FetchMemberDataByUuidResponse { export interface FetchMemberDataByUuidResponse {
uuid: string; uuid: string;
tags: string[]; tags: string[];
@ -41,7 +30,7 @@ export interface FetchMemberDataByUuidResponse {
} }
class AdminApi { class AdminApi {
async fetchMapDetails(playUri: string): Promise<MapDetailsData> { async fetchMapDetails(playUri: string): Promise<MapDetailsData | RoomRedirect> {
if (!ADMIN_API_URL) { if (!ADMIN_API_URL) {
return Promise.reject(new Error("No admin backoffice set!")); return Promise.reject(new Error("No admin backoffice set!"));
} }

View File

@ -0,0 +1,11 @@
import * as tg from "generic-type-guard";
export const isCharacterTexture = new tg.IsInterface()
.withProperties({
id: tg.isNumber,
level: tg.isNumber,
url: tg.isString,
rights: tg.isString,
})
.get();
export type CharacterTexture = tg.GuardedType<typeof isCharacterTexture>;

View File

@ -0,0 +1,20 @@
import * as tg from "generic-type-guard";
import { GameRoomPolicyTypes } from "_Model/PusherRoom";
import { isCharacterTexture } from "./CharacterTexture";
import { isAny, isNumber } from "generic-type-guard";
/*const isNumericEnum =
<T extends { [n: number]: string }>(vs: T) =>
(v: any): v is T =>
typeof v === "number" && v in vs;*/
export const isMapDetailsData = new tg.IsInterface()
.withProperties({
roomSlug: tg.isOptional(tg.isString), // deprecated
mapUrl: tg.isString,
policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes),
tags: tg.isArray(tg.isString),
textures: tg.isArray(isCharacterTexture),
})
.get();
export type MapDetailsData = tg.GuardedType<typeof isMapDetailsData>;

View File

@ -0,0 +1,8 @@
import * as tg from "generic-type-guard";
export const isRoomRedirect = new tg.IsInterface()
.withProperties({
redirectUrl: tg.isString,
})
.get();
export type RoomRedirect = tg.GuardedType<typeof isRoomRedirect>;

View File

@ -33,7 +33,7 @@ import {
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
import { ADMIN_API_URL, JITSI_ISS, SECRET_JITSI_KEY } from "../Enum/EnvironmentVariable"; import { ADMIN_API_URL, JITSI_ISS, SECRET_JITSI_KEY } from "../Enum/EnvironmentVariable";
import { adminApi, CharacterTexture } from "./AdminApi"; import { adminApi } from "./AdminApi";
import { emitInBatch } from "./IoSocketHelpers"; import { emitInBatch } from "./IoSocketHelpers";
import Jwt from "jsonwebtoken"; import Jwt from "jsonwebtoken";
import { JITSI_URL } from "../Enum/EnvironmentVariable"; import { JITSI_URL } from "../Enum/EnvironmentVariable";
@ -44,6 +44,8 @@ import { GroupDescriptor, UserDescriptor, ZoneEventListener } from "_Model/Zone"
import Debug from "debug"; import Debug from "debug";
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
import { WebSocket } from "uWebSockets.js"; import { WebSocket } from "uWebSockets.js";
import { isRoomRedirect } from "./AdminApi/RoomRedirect";
import { CharacterTexture } from "./AdminApi/CharacterTexture";
const debug = Debug("socket"); const debug = Debug("socket");
@ -374,8 +376,14 @@ export class SocketManager implements ZoneEventListener {
public async updateRoomWithAdminData(room: PusherRoom): Promise<void> { public async updateRoomWithAdminData(room: PusherRoom): Promise<void> {
const data = await adminApi.fetchMapDetails(room.roomUrl); const data = await adminApi.fetchMapDetails(room.roomUrl);
room.tags = data.tags;
room.policyType = Number(data.policy_type); if (isRoomRedirect(data)) {
// TODO: if the updated room data is actually a redirect, we need to take everybody on the map
// and redirect everybody to the new location (so we need to close the connection for everybody)
} else {
room.tags = data.tags;
room.policyType = Number(data.policy_type);
}
} }
emitPlayGlobalMessage(client: ExSocketInterface, playglobalmessage: PlayGlobalMessage) { emitPlayGlobalMessage(client: ExSocketInterface, playglobalmessage: PlayGlobalMessage) {