refactor(data): merge DTO models into schemas
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { Character } from "@/data/schemas/character";
|
||||
|
||||
describe("Character", () => {
|
||||
it("keeps only the four public character fields", () => {
|
||||
const character = Character.fromJson({
|
||||
id: "character_elio",
|
||||
slug: "elio",
|
||||
displayName: " Elio Silvestri ",
|
||||
avatarUrl: "https://cdn.example.com/elio.jpg",
|
||||
enabled: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(character.toJson()).toEqual({
|
||||
id: "character_elio",
|
||||
slug: "elio",
|
||||
displayName: "Elio Silvestri",
|
||||
avatarUrl: "https://cdn.example.com/elio.jpg",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects unsafe slugs and non-HTTPS avatars", () => {
|
||||
expect(() =>
|
||||
Character.from({
|
||||
id: "character_aria",
|
||||
slug: "Aria Profile",
|
||||
displayName: "Aria",
|
||||
avatarUrl: "https://cdn.example.com/aria.jpg",
|
||||
}),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
Character.from({
|
||||
id: "character_aria",
|
||||
slug: "aria",
|
||||
displayName: "Aria",
|
||||
avatarUrl: "http://cdn.example.com/aria.jpg",
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -11,3 +11,27 @@ export const CharacterSchema = z.object({
|
||||
|
||||
export type CharacterInput = z.input<typeof CharacterSchema>;
|
||||
export type CharacterData = z.output<typeof CharacterSchema>;
|
||||
|
||||
export class Character {
|
||||
declare readonly id: string;
|
||||
declare readonly slug: string;
|
||||
declare readonly displayName: string;
|
||||
declare readonly avatarUrl: string;
|
||||
|
||||
private constructor(input: CharacterInput) {
|
||||
Object.assign(this, CharacterSchema.parse(input));
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
static from(input: CharacterInput): Character {
|
||||
return new Character(input);
|
||||
}
|
||||
|
||||
static fromJson(json: unknown): Character {
|
||||
return Character.from(json as CharacterInput);
|
||||
}
|
||||
|
||||
toJson(): CharacterData {
|
||||
return CharacterSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { CharacterSchema } from "./character";
|
||||
import { Character, CharacterSchema } from "./character";
|
||||
|
||||
export const CharactersResponseSchema = z.object({
|
||||
items: z.array(CharacterSchema).default([]),
|
||||
@@ -8,3 +8,29 @@ export const CharactersResponseSchema = z.object({
|
||||
|
||||
export type CharactersResponseInput = z.input<typeof CharactersResponseSchema>;
|
||||
export type CharactersResponseData = z.output<typeof CharactersResponseSchema>;
|
||||
|
||||
export class CharactersResponse {
|
||||
declare readonly items: Character[];
|
||||
|
||||
private constructor(input: CharactersResponseInput) {
|
||||
const data = CharactersResponseSchema.parse(input);
|
||||
Object.assign(this, {
|
||||
items: data.items.map((item) => Character.from(item)),
|
||||
});
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
static from(input: CharactersResponseInput): CharactersResponse {
|
||||
return new CharactersResponse(input);
|
||||
}
|
||||
|
||||
static fromJson(json: unknown): CharactersResponse {
|
||||
return CharactersResponse.from(json as CharactersResponseInput);
|
||||
}
|
||||
|
||||
toJson(): CharactersResponseData {
|
||||
return CharactersResponseSchema.parse({
|
||||
items: this.items.map((item) => item.toJson()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
/**
|
||||
* @file Automatically generated by barrelsby.
|
||||
*/
|
||||
|
||||
export * from "./character";
|
||||
export * from "./characters_response";
|
||||
|
||||
Reference in New Issue
Block a user