refactor: validate user state with zod
This commit is contained in:
@@ -1,38 +1,22 @@
|
|||||||
import { plainToClass } from "class-transformer";
|
import { z } from "zod";
|
||||||
import { IsBoolean, IsString, validate } from "class-validator";
|
|
||||||
|
|
||||||
export class UserStateUpdate {
|
const userStateUpdateSchema = z.object({
|
||||||
@IsString()
|
userId: z.string(),
|
||||||
userId!: string;
|
username: z.string(),
|
||||||
|
avatar: z.string(),
|
||||||
|
speaking: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
@IsString()
|
export type UserStateUpdate = z.infer<typeof userStateUpdateSchema>;
|
||||||
username!: string;
|
|
||||||
|
|
||||||
@IsString()
|
export interface AudioMessage {
|
||||||
avatar!: string;
|
data: Buffer;
|
||||||
|
userId: string;
|
||||||
@IsBoolean()
|
|
||||||
speaking!: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AudioMessage {
|
|
||||||
data!: Buffer;
|
|
||||||
userId!: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function validateUserStateUpdate(
|
export async function validateUserStateUpdate(
|
||||||
data: unknown,
|
data: unknown,
|
||||||
): Promise<UserStateUpdate | null> {
|
): Promise<UserStateUpdate | null> {
|
||||||
if (typeof data !== "object" || data === null) {
|
const result = userStateUpdateSchema.safeParse(data);
|
||||||
return null;
|
return result.success ? result.data : null;
|
||||||
}
|
|
||||||
|
|
||||||
const obj = plainToClass(UserStateUpdate, data);
|
|
||||||
const errors = await validate(obj);
|
|
||||||
|
|
||||||
if (errors.length > 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|||||||
35
tests/validation.test.ts
Normal file
35
tests/validation.test.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { validateUserStateUpdate } from "../src/validation";
|
||||||
|
|
||||||
|
describe("validateUserStateUpdate", () => {
|
||||||
|
it("returns typed data for a valid user state update", async () => {
|
||||||
|
const result = await validateUserStateUpdate({
|
||||||
|
userId: "123",
|
||||||
|
username: "aseph",
|
||||||
|
avatar: "https://example.invalid/avatar.png",
|
||||||
|
speaking: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
userId: "123",
|
||||||
|
username: "aseph",
|
||||||
|
avatar: "https://example.invalid/avatar.png",
|
||||||
|
speaking: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null for non-object input", async () => {
|
||||||
|
await expect(validateUserStateUpdate("bad")).resolves.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null for invalid field types", async () => {
|
||||||
|
const result = await validateUserStateUpdate({
|
||||||
|
userId: "123",
|
||||||
|
username: "aseph",
|
||||||
|
avatar: "https://example.invalid/avatar.png",
|
||||||
|
speaking: "true",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user