katya / src /controllers /visitors.ts
esubtelnik's picture
Add download endpoints for visitors, employees, and books; update Swagger documentation
2200342
import {
Controller,
Get,
Route,
Tags,
Post,
Body,
Path,
Delete,
Patch,
Response,
Produces,
} from "tsoa";
import { Visitor, CreateVisitorRequest, VisitorWithBooks } from "../models/Visitor";
import { readMyData, writeMyData } from "../utils/fileStorage";
import { wrapResponse, ApiResponse } from "../utils/responseWrapper";
import { ValidationError, NotFoundError } from "../utils/apiErrors";
import { v4 as uuidv4 } from "uuid";
import { VisitorValidator } from "../validators/visitorValidator";
import { visitorLinkManager } from "../models/Visitor";
@Route("visitors")
@Tags("Visitors")
export class VisitorController extends Controller {
private readonly file = "visitors.sea";
@Get("/")
public async getVisitors(): Promise<ApiResponse<VisitorWithBooks[]>> {
const visitors = await readMyData<Visitor>(this.file);
const enriched = await Promise.all(
visitors.map((v) => visitorLinkManager.enrichWithBooks(v))
);
return wrapResponse(enriched);
}
@Get("/{id}")
public async getVisitor(@Path() id: string): Promise<ApiResponse<VisitorWithBooks>> {
const visitors = await readMyData<Visitor>(this.file);
const visitor = visitors.find((v) => v.id === id);
if (!visitor) {
throw new NotFoundError(`Visitor with id ${id} not found`);
}
const enriched = await visitorLinkManager.enrichWithBooks(visitor);
return wrapResponse(enriched);
}
@Post("/")
public async addVisitor(
@Body() body: CreateVisitorRequest
): Promise<ApiResponse<Visitor>> {
VisitorValidator.validate(body);
const visitors = await readMyData<Visitor>(this.file);
const newVisitor: Visitor = {
id: uuidv4(),
name: body.name.trim(),
surname: body.surname.trim(),
registrationDate: body.registrationDate,
currentBooks: [],
history: [],
};
visitors.push(newVisitor);
await writeMyData(this.file, visitors);
return wrapResponse(newVisitor);
}
@Patch("/{id}")
public async updateVisitor(
@Path() id: string,
@Body() body: Partial<CreateVisitorRequest>
): Promise<ApiResponse<VisitorWithBooks>> {
VisitorValidator.validateForUpdate(body);
const visitors = await readMyData<Visitor>(this.file);
const index = visitors.findIndex((v) => v.id === id);
if (index === -1) {
throw new NotFoundError(`Visitor with id ${id} not found`);
}
visitors[index] = { ...visitors[index], ...body };
await writeMyData(this.file, visitors);
const enriched = await visitorLinkManager.enrichWithBooks(visitors[index]);
return wrapResponse(enriched);
}
@Delete("/{id}")
public async deleteVisitor(@Path() id: string): Promise<ApiResponse<void>> {
const visitors = await readMyData<Visitor>(this.file);
const index = visitors.findIndex((v) => v.id === id);
if (index === -1) {
throw new NotFoundError(`Visitor with id ${id} not found`);
}
if (
visitors[index].currentBooks &&
visitors[index].currentBooks.length > 0
) {
throw new ValidationError(
"Cannot delete visitor with unreturned books"
);
}
visitors.splice(index, 1);
await writeMyData(this.file, visitors);
return wrapResponse();
}
@Get("/{id}/download")
@Produces("application/json")
@Response(200, "File download")
public async downloadVisitor(@Path() id: string): Promise<string> {
const visitor = await this.getVisitor(id);
return JSON.stringify(visitor.data, null, 2);
}
}