-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathTraineeController.ts
More file actions
143 lines (127 loc) · 4.67 KB
/
TraineeController.ts
File metadata and controls
143 lines (127 loc) · 4.67 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { Request, Response, NextFunction } from 'express';
import { TraineesRepository } from '../../repositories';
import { AuthenticatedUser, ResponseError } from '../../models';
import { NotificationService, UpdateChange } from '../../services';
import { validateTrainee } from '../../models/Trainee';
export interface TraineeControllerType {
getTrainee(req: Request, res: Response, next: NextFunction): Promise<void>;
createTrainee(req: Request, res: Response, next: NextFunction): Promise<void>;
updateTrainee(req: Request, res: Response, next: NextFunction): Promise<void>;
deleteTrainee(req: Request, res: Response, next: NextFunction): Promise<void>;
}
export class TraineeController implements TraineeControllerType {
constructor(
private readonly traineesRepository: TraineesRepository,
private readonly notificationService: NotificationService
) {}
async getTrainee(req: Request, res: Response, next: NextFunction) {
const traineeId = String(req.params.id);
try {
const trainee = await this.traineesRepository.getTrainee(traineeId);
if (!trainee) {
res.status(404).json({ error: 'Trainee was not found' });
return;
}
res.status(200).json(trainee);
} catch (error: any) {
next(error);
}
}
async createTrainee(req: Request, res: Response, next: NextFunction): Promise<void> {
const body = req.body;
body.educationInfo = body.educationInfo ?? {};
body.employmentInfo = body.employmentInfo ?? {};
// Check if the request is valid
try {
validateTrainee(req.body);
} catch (error: any) {
const message: string = `Invalid trainee information. ` + error.message;
res.status(400).json(new ResponseError(message));
return;
}
// Check if there is another trainee with the same email
const email = req.body.contactInfo.email;
let emailExists: boolean = false;
try {
emailExists = await this.traineesRepository.isEmailExists(email);
} catch (error: any) {
next(error);
return;
}
if (emailExists) {
const message: string = `There is already another trainee in the system with the email ${email}`;
res.status(400).json(new ResponseError(message));
return;
}
// Create new trainee and return it
try {
const newTrainee = await this.traineesRepository.createTrainee(req.body);
res.status(201).json(newTrainee);
} catch (error: any) {
res.status(500).send(new ResponseError(error.message));
}
}
async updateTrainee(req: Request, res: Response, next: NextFunction) {
const trainee = await this.traineesRepository.getTrainee(String(req.params.id));
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
return;
}
// Apply all changes from the request body to the trainee object
const changes = this.applyObjectUpdate(req.body, trainee);
// Validate new trainee model after applying the changes
try {
validateTrainee(trainee);
} catch (error: any) {
res.status(400).send(new ResponseError(error.message));
return;
}
// Save the updated trainee
try {
await this.traineesRepository.updateTrainee(trainee);
res.status(200).json(trainee);
this.notificationService.traineeUpdated(trainee, changes, res.locals.user as AuthenticatedUser);
} catch (error: any) {
res.status(500).send(new ResponseError(error.message));
}
}
async deleteTrainee(req: Request, res: Response, next: NextFunction) {
res.status(500).send('Not implemented');
}
// This function updates the destination object with the source object.
// It is similar to Object.assign but works for nested objects and skips arrays.
private applyObjectUpdate(
source: any,
destination: any,
nestLevel: number = 0,
changes: UpdateChange[] = []
): UpdateChange[] {
// safeguard against infinite recursion
if (nestLevel > 5) {
return changes;
}
for (let key of Object.keys(source)) {
if (Array.isArray(source[key]) || !(key in destination)) {
continue;
}
if (typeof source[key] === 'object' && source[key] !== null) {
this.applyObjectUpdate(source[key], destination[key], nestLevel + 1, changes);
continue;
}
if (destination[key] === source[key]) {
continue;
}
// If the value has changed, record the change
if (destination[key] !== source[key]) {
changes.push({
fieldName: key,
previousValue: destination[key],
newValue: source[key],
});
}
// Update the destination object with the new value
destination[key] = source[key];
}
return changes;
}
}