Skip to content

Commit 6e6a7aa

Browse files
committed
Merging branches
2 parents 92315f8 + 4791e05 commit 6e6a7aa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+3323
-821
lines changed

backend/src/controllers/image.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Functions that process image route requests.
3+
*/
4+
5+
import { NextFunction, Request, Response } from "express";
6+
import mongoose from "mongoose";
7+
8+
import { InternalError } from "../errors";
9+
import { ServiceError } from "../errors/service";
10+
import { ValidationError } from "../errors/validation";
11+
import { Image } from "../models/image";
12+
import StudentModel from "../models/student";
13+
import UserModel from "../models/user";
14+
import { handleImageParsing } from "../util/image";
15+
16+
import { OwnerRequestBody } from "./types/types";
17+
import { EditPhotoRequestBody } from "./types/userTypes";
18+
19+
export const editPhoto = (req: EditPhotoRequestBody, res: Response, nxt: NextFunction) => {
20+
try {
21+
//Validation logic inside handleImageParsing
22+
handleImageParsing(req, res, nxt);
23+
} catch (e) {
24+
console.log(e);
25+
nxt(e);
26+
}
27+
};
28+
29+
export const getPhoto = async (
30+
req: Request<Record<string, never>, Record<string, never>, OwnerRequestBody>,
31+
res: Response,
32+
nxt: NextFunction,
33+
) => {
34+
try {
35+
const { ownerId, ownerType, imageId } = req.body;
36+
37+
if (!mongoose.Types.ObjectId.isValid(imageId)) {
38+
return res
39+
.status(ValidationError.INVALID_MONGO_ID.status)
40+
.send({ error: ValidationError.INVALID_MONGO_ID.message });
41+
}
42+
43+
let owner = null;
44+
45+
if (ownerType === "user") {
46+
owner = await UserModel.findById(ownerId);
47+
} else if (ownerType === "student") {
48+
owner = await StudentModel.findById(ownerId);
49+
}
50+
51+
if (!owner) {
52+
throw ValidationError.USER_NOT_FOUND;
53+
}
54+
55+
const image = await Image.findById(imageId);
56+
if (!image) {
57+
throw ValidationError.IMAGE_NOT_FOUND;
58+
}
59+
60+
if (image.ownerId !== ownerId) {
61+
throw ValidationError.IMAGE_USER_MISMATCH;
62+
}
63+
64+
return res.status(200).set("Content-type", image.mimetype).send(image.buffer);
65+
} catch (e) {
66+
console.log(e);
67+
if (e instanceof ServiceError) {
68+
nxt(e);
69+
}
70+
return res
71+
.status(InternalError.ERROR_GETTING_IMAGE.status)
72+
.send(InternalError.ERROR_GETTING_IMAGE.displayMessage(true));
73+
}
74+
};

backend/src/controllers/student.ts

Lines changed: 97 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { validationResult } from "express-validator";
88
import mongoose, { HydratedDocument } from "mongoose";
99

1010
import EnrollmentModel from "../models/enrollment";
11+
import { Image } from "../models/image";
12+
import ProgressNoteModel from "../models/progressNote";
1113
import StudentModel from "../models/student";
1214
import { Enrollment } from "../types/enrollment";
1315
import { createEnrollment, editEnrollment } from "../util/enrollment";
@@ -22,16 +24,25 @@ export const createStudent: RequestHandler = async (req, res, next) => {
2224

2325
validationErrorParser(errors);
2426

27+
const newStudentId = new mongoose.Types.ObjectId();
28+
2529
const { enrollments, ...studentData } = req.body as StudentRequest;
26-
const newStudent = await StudentModel.create(studentData);
30+
2731
// create enrollments for the student
28-
await Promise.all(
32+
const createdEnrollments = await Promise.all(
2933
enrollments.map(async (program: Enrollment) => {
30-
await createEnrollment({ ...program, studentId: newStudent._id });
34+
return await EnrollmentModel.create({ ...program, studentId: newStudentId });
3135
}),
3236
);
3337

34-
res.status(201).json(newStudent);
38+
const newStudent = await StudentModel.create({
39+
...studentData,
40+
enrollments: createdEnrollments.map((enrollment) => enrollment._id),
41+
});
42+
43+
const populatedStudent = await StudentModel.findById(newStudent._id).populate("enrollments");
44+
45+
res.status(201).json(populatedStudent);
3546
} catch (error) {
3647
next(error);
3748
}
@@ -49,42 +60,106 @@ export const editStudent: RequestHandler = async (req, res, next) => {
4960
if (studentId !== studentData._id.toString()) {
5061
return res.status(400).json({ message: "Invalid student ID" });
5162
}
52-
const updatedStudent = await StudentModel.findByIdAndUpdate(studentId, studentData, {
53-
new: true,
54-
});
55-
if (!updatedStudent) {
56-
return res.status(404).json({ message: "Student not found" });
63+
64+
if (!enrollments) {
65+
const updatedStudent = await StudentModel.findByIdAndUpdate(
66+
studentId,
67+
{ ...studentData },
68+
{
69+
new: true,
70+
},
71+
);
72+
if (!updatedStudent) {
73+
return res.status(404).json({ message: "Student not found" });
74+
}
75+
76+
return res.status(200).json(updatedStudent);
5777
}
5878

5979
// update enrollments for the student
60-
await Promise.all(
80+
const updatedEnrollments = await Promise.all(
6181
enrollments.map(async (enrollment: Enrollment) => {
6282
const enrollmentExists = await EnrollmentModel.findById(enrollment._id);
6383
const enrollmentBody = { ...enrollment, studentId: new mongoose.Types.ObjectId(studentId) };
64-
if (!enrollmentExists) await createEnrollment(enrollmentBody);
65-
else await editEnrollment(enrollmentBody);
84+
if (!enrollmentExists) {
85+
return await createEnrollment(enrollmentBody);
86+
} else {
87+
return await editEnrollment(enrollmentBody);
88+
}
6689
}),
6790
);
6891

69-
res.status(200).json({ ...updatedStudent, enrollments });
92+
const updatedStudent = await StudentModel.findByIdAndUpdate(
93+
studentId,
94+
{ ...studentData, enrollments: updatedEnrollments.map((enrollment) => enrollment?._id) },
95+
{
96+
new: true,
97+
},
98+
);
99+
if (!updatedStudent) {
100+
return res.status(404).json({ message: "Student not found" });
101+
}
102+
103+
const populatedStudent = await StudentModel.findById(updatedStudent._id).populate(
104+
"enrollments",
105+
);
106+
107+
console.log({ populatedStudent });
108+
109+
res.status(200).json(populatedStudent);
70110
} catch (error) {
71111
next(error);
72112
}
73113
};
74114

75115
export const getAllStudents: RequestHandler = async (_, res, next) => {
76116
try {
77-
const students = await StudentModel.find();
117+
const students = await StudentModel.find().populate("enrollments");
78118

79-
// gather all enrollments for each student and put them in student.programs
80-
const hydratedStudents = await Promise.all(
81-
students.map(async (student) => {
82-
const enrollments = await EnrollmentModel.find({ studentId: student._id });
83-
return { ...student.toObject(), programs: enrollments };
84-
}),
85-
);
119+
res.status(200).json(students);
120+
} catch (error) {
121+
next(error);
122+
}
123+
};
124+
125+
export const getStudent: RequestHandler = async (req, res, next) => {
126+
try {
127+
const errors = validationResult(req);
128+
129+
validationErrorParser(errors);
130+
131+
const studentId = req.params.id;
132+
const studentData = await StudentModel.findById(req.params.id);
133+
134+
if (!studentData) {
135+
return res.status(404).json({ message: "Student not found" });
136+
}
137+
138+
const enrollments = await EnrollmentModel.find({ studentId });
139+
140+
res.status(200).json({ ...studentData.toObject(), enrollments });
141+
} catch (error) {
142+
next(error);
143+
}
144+
};
145+
146+
export const deleteStudent: RequestHandler = async (req, res, next) => {
147+
try {
148+
const errors = validationResult(req);
149+
validationErrorParser(errors);
150+
151+
const studentId = req.params.id;
152+
const deletedStudent = await StudentModel.findById(studentId);
153+
if (!deletedStudent) {
154+
return res.status(404).json({ message: "Student not found" });
155+
}
156+
157+
await EnrollmentModel.deleteMany({ studentId });
158+
await ProgressNoteModel.deleteMany({ studentId });
159+
await Image.deleteMany({ userId: studentId });
160+
await StudentModel.deleteOne({ _id: studentId });
86161

87-
res.status(200).json(hydratedStudents);
162+
res.status(200).json(deletedStudent);
88163
} catch (error) {
89164
next(error);
90165
}

backend/src/controllers/types/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,21 @@ export type UserId = {
55
};
66

77
export type UserIdRequestBody = Request & UserId;
8+
9+
// Add type so that if uploadType is "create", new field imgeId is required
10+
type NewUploadType = {
11+
uploadType: "new";
12+
imageId: string;
13+
};
14+
15+
type EditUploadType = {
16+
uploadType: "edit";
17+
imageId: never;
18+
};
19+
20+
export type OwnerInfo = {
21+
ownerId: string;
22+
ownerType: string;
23+
} & (NewUploadType | EditUploadType);
24+
25+
export type OwnerRequestBody = Request & OwnerInfo;

backend/src/controllers/types/userTypes.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Request } from "express";
22

3-
import { UserId } from "./types";
3+
import { OwnerInfo, UserId } from "./types";
44

55
export type CreateUserRequestBody = {
66
name: string;
@@ -32,7 +32,10 @@ export type UpdateAccountTypeRequestBody = UserId & {
3232
export type SaveImageRequest = {
3333
body: {
3434
previousImageId: string;
35-
userId: string;
35+
ownerId: string;
36+
ownerType: string;
37+
uploadType: string;
38+
imageId: string;
3639
};
3740
file: {
3841
buffer: Buffer;
@@ -42,6 +45,10 @@ export type SaveImageRequest = {
4245
};
4346
};
4447

45-
export type EditPhotoRequestBody = Request<Record<string, never>, Record<string, never>, UserId> & {
48+
export type EditPhotoRequestBody = Request<
49+
Record<string, never>,
50+
Record<string, never>,
51+
OwnerInfo
52+
> & {
4653
rawBody?: Buffer;
4754
};

backend/src/controllers/user.ts

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
import { NextFunction, Request, Response } from "express";
22
import { validationResult } from "express-validator";
33
import admin from "firebase-admin";
4-
import mongoose from "mongoose";
54

6-
import { InternalError } from "../errors";
7-
import { ServiceError } from "../errors/service";
85
import { ValidationError } from "../errors/validation";
9-
import { Image } from "../models/image";
106
import UserModel from "../models/user";
117
import { sendApprovalEmail, sendDenialEmail } from "../util/email";
128
import { firebaseAdminAuth } from "../util/firebase";
13-
import { handleImageParsing } from "../util/image";
149
import { deleteUserFromFirebase, deleteUserFromMongoDB } from "../util/user";
1510
import validationErrorParser from "../util/validationErrorParser";
1611

@@ -20,7 +15,6 @@ import {
2015
EditEmailRequestBody,
2116
EditLastChangedPasswordRequestBody,
2217
EditNameRequestBody,
23-
EditPhotoRequestBody,
2418
LoginUserRequestBody,
2519
UpdateAccountTypeRequestBody,
2620
} from "./types/userTypes";
@@ -174,57 +168,6 @@ export const loginUser = async (
174168
}
175169
};
176170

177-
export const editPhoto = (req: EditPhotoRequestBody, res: Response, nxt: NextFunction) => {
178-
try {
179-
//Validation logic inside handleImageParsing
180-
handleImageParsing(req, res, nxt);
181-
} catch (e) {
182-
console.log(e);
183-
nxt(e);
184-
}
185-
};
186-
187-
export const getPhoto = async (
188-
req: Request<Record<string, never>, Record<string, never>, UserIdRequestBody>,
189-
res: Response,
190-
nxt: NextFunction,
191-
) => {
192-
try {
193-
const { uid } = req.body;
194-
const { id: imageId } = req.params;
195-
196-
if (!mongoose.Types.ObjectId.isValid(imageId)) {
197-
return res
198-
.status(ValidationError.INVALID_MONGO_ID.status)
199-
.send({ error: ValidationError.INVALID_MONGO_ID.message });
200-
}
201-
202-
const user = await UserModel.findById(uid);
203-
if (!user) {
204-
throw ValidationError.USER_NOT_FOUND;
205-
}
206-
207-
const image = await Image.findById(imageId);
208-
if (!image) {
209-
throw ValidationError.IMAGE_NOT_FOUND;
210-
}
211-
212-
if (image.userId !== uid) {
213-
throw ValidationError.IMAGE_USER_MISMATCH;
214-
}
215-
216-
return res.status(200).set("Content-type", image.mimetype).send(image.buffer);
217-
} catch (e) {
218-
console.log(e);
219-
if (e instanceof ServiceError) {
220-
nxt(e);
221-
}
222-
return res
223-
.status(InternalError.ERROR_GETTING_IMAGE.status)
224-
.send(InternalError.ERROR_GETTING_IMAGE.displayMessage(true));
225-
}
226-
};
227-
228171
export const editName = async (
229172
req: Request<Record<string, never>, Record<string, never>, EditNameRequestBody>,
230173
res: Response,

backend/src/errors/validation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const IMAGE_NOT_FOUND = "Image was not found. Please make sure id passed in rout
1111
const INVALID_MONGO_ID = "Mongo ID was invalid. Please ensure that the id is correct";
1212
const IMAGE_USER_MISMATCH = "Image does not belong to the user";
1313
const PROGRESS_NOTE_NOT_FOUND = "Progress note not found in database";
14+
const IMAGE_UPDATED_FAILED = "Image was not updated successfully";
1415

1516
export class ValidationError extends CustomError {
1617
static USER_CREATION_UNSUCCESSFUL = new ValidationError(1, 400, USER_CREATION_UNSUCCESSFUL);
@@ -24,4 +25,5 @@ export class ValidationError extends CustomError {
2425
static INVALID_MONGO_ID = new ValidationError(9, 400, INVALID_MONGO_ID);
2526
static IMAGE_USER_MISMATCH = new ValidationError(10, 401, IMAGE_USER_MISMATCH);
2627
static PROGRESS_NOTE_NOT_FOUND = new ValidationError(11, 404, PROGRESS_NOTE_NOT_FOUND);
28+
static IMAGE_UPDATED_FAILED = new ValidationError(12, 400, IMAGE_UPDATED_FAILED);
2729
}

0 commit comments

Comments
 (0)