forked from Code-4-Community/scaffolding
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathusers.service.ts
More file actions
215 lines (185 loc) · 5.97 KB
/
users.service.ts
File metadata and controls
215 lines (185 loc) · 5.97 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import {
BadRequestException,
Injectable,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Between, In, Repository } from 'typeorm';
import { User } from './users.entity';
import { Role } from './types';
import { validateId } from '../utils/validation.utils';
import { UpdateUserInfoDto } from './dtos/update-user-info.dto';
import { AuthService } from '../auth/auth.service';
import { userSchemaDto } from './dtos/userSchema.dto';
import { emailTemplates } from '../emails/emailTemplates';
import { EmailsService } from '../emails/email.service';
import { FoodRequest } from '../foodRequests/request.entity';
import { Order } from '../orders/order.entity';
import { Donation } from '../donations/donations.entity';
import { UserStatsDto } from './dtos/user-stats.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private repo: Repository<User>,
@InjectRepository(FoodRequest)
private foodRequestRepo: Repository<FoodRequest>,
@InjectRepository(Order)
private orderRepo: Repository<Order>,
@InjectRepository(Donation)
private donationRepo: Repository<Donation>,
private authService: AuthService,
private emailsService: EmailsService,
) {}
async create(createUserDto: userSchemaDto): Promise<User> {
const { email, firstName, lastName, phone, role } = createUserDto;
const emailsEnabled = process.env.SEND_AUTOMATED_EMAILS === 'true';
// Just save to DB if emails are disabled (no Cognito creation)
if (!emailsEnabled) {
const user = this.repo.create({
role,
firstName,
lastName,
email,
phone,
});
return this.repo.save(user);
}
// Pantry and food manufacturer users must already exist in the DB
// (created during application) before a Cognito account is made
if (role === Role.PANTRY || role === Role.FOODMANUFACTURER) {
const existingUser = await this.repo.findOneBy({ email });
if (!existingUser) {
throw new NotFoundException(`User with email ${email} not found`);
}
existingUser.userCognitoSub = await this.authService.adminCreateUser({
firstName,
lastName,
email,
});
return this.repo.save(existingUser);
}
// Create Cognito user and save to DB
const userCognitoSub = await this.authService.adminCreateUser({
firstName,
lastName,
email,
});
const user = this.repo.create({
role,
firstName,
lastName,
email,
phone,
userCognitoSub,
});
await this.repo.save(user);
// Send welcome email to new volunteers (only after successful creation)
if (role === Role.VOLUNTEER) {
try {
const message = emailTemplates.volunteerAccountCreated();
await this.emailsService.sendEmails(
[email],
message.subject,
message.bodyHTML,
);
} catch {
throw new InternalServerErrorException(
'Failed to send account created notification email to volunteer',
);
}
}
return user;
}
async findOne(id: number): Promise<User> {
validateId(id, 'User');
const user = await this.repo.findOneBy({ id });
if (!user) {
throw new NotFoundException(`User ${id} not found`);
}
return user;
}
// given userIds should not have duplicates
async findByIds(userIds: number[]): Promise<User[]> {
userIds.forEach((id) => validateId(id, 'User'));
const users = await this.repo.findBy({ id: In(userIds) });
if (users.length !== userIds.length) {
const foundIds = new Set(users.map((u) => u.id));
const missingIds = userIds.filter((id) => !foundIds.has(id));
throw new NotFoundException(`Users not found: ${missingIds.join(', ')}`);
}
return users;
}
async update(id: number, dto: UpdateUserInfoDto): Promise<User> {
validateId(id, 'User');
if (
dto.firstName === undefined &&
dto.lastName === undefined &&
dto.phone === undefined
) {
throw new BadRequestException(
'At least one field must be provided to update',
);
}
const user = await this.findOne(id);
if (!user) {
throw new NotFoundException(`User ${id} not found`);
}
Object.assign(user, dto);
return this.repo.save(user);
}
async remove(id: number) {
validateId(id, 'User');
const user = await this.findOne(id);
if (!user) {
throw new NotFoundException(`User ${id} not found`);
}
return this.repo.remove(user);
}
async findUsersByRoles(roles: Role[]): Promise<User[]> {
return this.repo.find({
where: { role: In(roles) },
relations: ['pantries'],
});
}
async findUserByCognitoId(cognitoId: string): Promise<User> {
const user = await this.repo.findOneBy({ userCognitoSub: cognitoId });
if (!user) {
throw new NotFoundException(`User with cognitoId ${cognitoId} not found`);
}
return user;
}
async getMonthlyAggregatedStats(): Promise<UserStatsDto> {
const now = new Date();
const startMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const endMonth = new Date(
now.getFullYear(),
now.getMonth() + 1,
0,
23,
59,
59,
999,
);
const [foodRequestsCount, ordersCount, donationsCount, volunteersCount] =
await Promise.all([
this.foodRequestRepo.count({
where: { requestedAt: Between(startMonth, endMonth) },
}),
this.orderRepo.count({
where: { createdAt: Between(startMonth, endMonth) },
}),
this.donationRepo.count({
where: { dateDonated: Between(startMonth, endMonth) },
}),
this.repo.count({ where: { role: Role.VOLUNTEER } }),
]);
return {
'Food Requests': foodRequestsCount.toString(),
Orders: ordersCount.toString(),
Donations: donationsCount.toString(),
Volunteers: volunteersCount.toString(),
};
}
}