File size: 3,687 Bytes
d76f93d
 
 
 
 
 
 
 
 
 
2200342
 
d76f93d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2200342
 
 
 
 
 
 
 
d76f93d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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);
   }
}