Skip to content

Commit e89a9b9

Browse files
feat(api): add logs to exam environment routes (freeCodeCamp#59139)
1 parent f206ba2 commit e89a9b9

File tree

1 file changed

+86
-5
lines changed

1 file changed

+86
-5
lines changed

Diff for: api/src/exam-environment/routes/exam-environment.ts

+86-5
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,33 @@ async function tokenMetaHandler(
107107
req: UpdateReqType<typeof schemas.examEnvironmentTokenMeta>,
108108
reply: FastifyReply
109109
) {
110+
const logger = this.log.child({ req });
110111
const { 'exam-environment-authorization-token': encodedToken } = req.headers;
112+
logger.info({ encodedToken });
111113

112114
let payload: JwtPayload;
113115
try {
114116
payload = jwt.verify(encodedToken, JWT_SECRET) as JwtPayload;
115117
} catch (e) {
116118
// Server refuses to brew (verify) coffee (jwts) with a teapot (random strings)
119+
logger.warn(
120+
{ examEnvironmentAuthorizationTokenError: e },
121+
'Invalid token provided.'
122+
);
117123
void reply.code(418);
118124
return reply.send(
119125
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_AUTHORIZATION_TOKEN(JSON.stringify(e))
120126
);
121127
}
122128

123129
if (!isObjectID(payload.examEnvironmentAuthorizationToken)) {
130+
logger.warn(
131+
{
132+
examEnvironmentAuthorizationToken:
133+
payload.examEnvironmentAuthorizationToken
134+
},
135+
'Token is not an object id.'
136+
);
124137
void reply.code(418);
125138
return reply.send(
126139
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_AUTHORIZATION_TOKEN(
@@ -137,6 +150,7 @@ async function tokenMetaHandler(
137150

138151
if (!token) {
139152
// Endpoint is valid, but resource does not exists
153+
logger.warn('Token does not appear to exist.');
140154
void reply.code(404);
141155
return reply.send(
142156
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_AUTHORIZATION_TOKEN(
@@ -161,6 +175,8 @@ async function postExamGeneratedExamHandler(
161175
req: UpdateReqType<typeof schemas.examEnvironmentPostExamGeneratedExam>,
162176
reply: FastifyReply
163177
) {
178+
const logger = this.log.child({ req });
179+
logger.info({ userId: req.user?.id });
164180
// Get exam from DB
165181
const examId = req.body.examId;
166182
const maybeExam = await mapErr(
@@ -172,10 +188,12 @@ async function postExamGeneratedExamHandler(
172188
);
173189
if (maybeExam.hasError) {
174190
if (maybeExam.error instanceof PrismaClientValidationError) {
191+
logger.warn({ examError: maybeExam.error }, 'Invalid exam id given.');
175192
void reply.code(400);
176193
return reply.send(ERRORS.FCC_EINVAL_EXAM_ID(maybeExam.error.message));
177194
}
178195

196+
logger.error({ examError: maybeExam.error });
179197
void reply.code(500);
180198
return reply.send(
181199
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeExam.error))
@@ -185,6 +203,7 @@ async function postExamGeneratedExamHandler(
185203
const exam = maybeExam.data;
186204

187205
if (!exam) {
206+
logger.warn({ examId }, 'No exam with given id.');
188207
void reply.code(404);
189208
return reply.send(
190209
ERRORS.FCC_ENOENT_EXAM_ENVIRONMENT_MISSING_EXAM('Invalid exam id given.')
@@ -196,6 +215,10 @@ async function postExamGeneratedExamHandler(
196215
const isExamPrerequisitesMet = checkPrerequisites(user, exam.prerequisites);
197216

198217
if (!isExamPrerequisitesMet) {
218+
logger.warn(
219+
{ examId: exam.id },
220+
'User has not completed prerequisites to take exam.'
221+
);
199222
void reply.code(403);
200223
// TODO: Consider sending unmet prerequisites
201224
return reply.send(
@@ -216,6 +239,10 @@ async function postExamGeneratedExamHandler(
216239
);
217240

218241
if (maybeExamAttempts.hasError) {
242+
logger.error(
243+
{ examAttemptsError: maybeExamAttempts.error },
244+
'Unable to query exam attempts.'
245+
);
219246
void reply.code(500);
220247
return reply.send(
221248
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeExamAttempts.error))
@@ -238,6 +265,10 @@ async function postExamGeneratedExamHandler(
238265
examExpirationTime + exam.config.retakeTimeInMS < Date.now();
239266

240267
if (!retakeAllowed) {
268+
logger.warn(
269+
{ examExpirationTime },
270+
'User has completed exam too recently to retake.'
271+
);
241272
void reply.code(429);
242273
// TODO: Consider sending last completed time
243274
return reply.send(
@@ -259,13 +290,21 @@ async function postExamGeneratedExamHandler(
259290
);
260291

261292
if (generated.hasError) {
293+
logger.error(
294+
{ generatedError: generated.error },
295+
'Unable to query generated exam.'
296+
);
262297
void reply.code(500);
263298
return reply.send(
264299
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(generated.error))
265300
);
266301
}
267302

268303
if (generated.data === null) {
304+
logger.error(
305+
{ generatedExamId: lastAttempt.generatedExamId },
306+
'Generated exam not found.'
307+
);
269308
void reply.code(500);
270309
return reply.send(
271310
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(
@@ -301,6 +340,7 @@ async function postExamGeneratedExamHandler(
301340
);
302341

303342
if (maybeGeneratedExams.hasError) {
343+
logger.error({ generatedExamsError: maybeGeneratedExams.error });
304344
void reply.code(500);
305345
return reply.send(
306346
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(maybeGeneratedExams.error)
@@ -310,12 +350,10 @@ async function postExamGeneratedExamHandler(
310350
const generatedExams = maybeGeneratedExams.data;
311351

312352
if (generatedExams.length === 0) {
353+
const errMessage = `Unable to provide a generated exam. Either all generated exams have been exhausted, or all generated exams are deprecated.`;
354+
logger.error({ examId: exam.id }, errMessage);
313355
void reply.code(500);
314-
return reply.send(
315-
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(
316-
`Unable to provide a generated exam. Either all generated exams have been exhausted, or all generated exams are deprecated.`
317-
)
318-
);
356+
return reply.send(ERRORS.FCC_ERR_EXAM_ENVIRONMENT(errMessage));
319357
}
320358

321359
const randomGeneratedExam =
@@ -330,6 +368,7 @@ async function postExamGeneratedExamHandler(
330368
);
331369

332370
if (maybeGeneratedExam.hasError) {
371+
logger.error({ generatedExamError: maybeGeneratedExam.error });
333372
void reply.code(500);
334373
return reply.send(
335374
// TODO: Consider more specific code
@@ -343,6 +382,10 @@ async function postExamGeneratedExamHandler(
343382
const generatedExam = maybeGeneratedExam.data;
344383

345384
if (generatedExam === null) {
385+
logger.error(
386+
{ generatedExamId: randomGeneratedExam.id },
387+
'Generated exam not found.'
388+
);
346389
void reply.code(500);
347390
return reply.send(
348391
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(`Unable to locate generated exam.`)
@@ -364,6 +407,7 @@ async function postExamGeneratedExamHandler(
364407
);
365408

366409
if (attempt.hasError) {
410+
logger.error({ attemptError: attempt.error });
367411
void reply.code(500);
368412
return reply.send(
369413
ERRORS.FCC_ERR_EXAM_ENVIRONMENT_CREATE_EXAM_ATTEMPT(
@@ -378,6 +422,7 @@ async function postExamGeneratedExamHandler(
378422
);
379423

380424
if (maybeUserExam.hasError) {
425+
logger.error({ userExamError: maybeUserExam.error });
381426
await this.prisma.envExamAttempt.delete({
382427
where: {
383428
id: attempt.data.id
@@ -413,6 +458,8 @@ async function postExamAttemptHandler(
413458
req: UpdateReqType<typeof schemas.examEnvironmentPostExamAttempt>,
414459
reply: FastifyReply
415460
) {
461+
const logger = this.log.child({ req });
462+
logger.info({ userId: req.user?.id });
416463
const { attempt } = req.body;
417464

418465
const user = req.user!;
@@ -427,6 +474,10 @@ async function postExamAttemptHandler(
427474
);
428475

429476
if (maybeAttempts.hasError) {
477+
logger.error(
478+
{ error: maybeAttempts.error },
479+
'User attempt cannot be linked to an exam attempt.'
480+
);
430481
void reply.code(500);
431482
return reply.send(
432483
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeAttempts.error))
@@ -436,6 +487,7 @@ async function postExamAttemptHandler(
436487
const attempts = maybeAttempts.data;
437488

438489
if (attempts.length === 0) {
490+
logger.warn({ examId: attempt.examId }, 'No attempts found for user.');
439491
void reply.code(404);
440492
return reply.send(
441493
ERRORS.FCC_ERR_EXAM_ENVIRONMENT_EXAM_ATTEMPT(
@@ -457,6 +509,7 @@ async function postExamAttemptHandler(
457509
);
458510

459511
if (maybeExam.hasError) {
512+
logger.error({ examError: maybeExam.error });
460513
void reply.code(500);
461514
return reply.send(
462515
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeExam.error))
@@ -466,6 +519,7 @@ async function postExamAttemptHandler(
466519
const exam = maybeExam.data;
467520

468521
if (exam === null) {
522+
logger.warn({ examId: attempt.examId }, 'Invalid exam id given.');
469523
void reply.code(404);
470524
return reply.send(
471525
ERRORS.FCC_ENOENT_EXAM_ENVIRONMENT_MISSING_EXAM('Invalid exam id given.')
@@ -476,6 +530,10 @@ async function postExamAttemptHandler(
476530
latestAttempt.startTimeInMS + exam.config.totalTimeInMS < Date.now();
477531

478532
if (isAttemptExpired) {
533+
logger.warn(
534+
{ examAttemptId: latestAttempt.id },
535+
'Attempt has exceeded submission time.'
536+
);
479537
void reply.code(403);
480538
return reply.send(
481539
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_EXAM_ATTEMPT(
@@ -494,6 +552,7 @@ async function postExamAttemptHandler(
494552
);
495553

496554
if (maybeGeneratedExam.hasError) {
555+
logger.error({ generatedExamError: maybeGeneratedExam.error });
497556
void reply.code(500);
498557
return reply.send(
499558
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeGeneratedExam.error))
@@ -503,6 +562,10 @@ async function postExamAttemptHandler(
503562
const generatedExam = maybeGeneratedExam.data;
504563

505564
if (generatedExam === null) {
565+
logger.warn(
566+
{ generatedExamId: latestAttempt.generatedExamId },
567+
'Generated exam not found.'
568+
);
506569
void reply.code(404);
507570
return reply.send(
508571
ERRORS.FCC_ENOENT_EXAM_ENVIRONMENT_GENERATED_EXAM(
@@ -536,6 +599,10 @@ async function postExamAttemptHandler(
536599
);
537600

538601
if (maybeValidExamAttempt.hasError) {
602+
logger.warn(
603+
{ validExamAttemptError: maybeValidExamAttempt.error },
604+
'Invalid exam attempt.'
605+
);
539606
void reply.code(400);
540607
const message =
541608
maybeValidExamAttempt.error instanceof Error
@@ -545,6 +612,7 @@ async function postExamAttemptHandler(
545612
}
546613

547614
if (maybeUpdatedAttempt.hasError) {
615+
logger.error({ updatedAttemptError: maybeUpdatedAttempt.error });
548616
void reply.code(500);
549617
return reply.send(
550618
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeUpdatedAttempt.error))
@@ -564,9 +632,12 @@ async function postScreenshotHandler(
564632
req: UpdateReqType<typeof schemas.examEnvironmentPostScreenshot>,
565633
reply: FastifyReply
566634
) {
635+
const logger = this.log.child({ req });
636+
logger.info({ userId: req.user?.id });
567637
const isMultipart = req.isMultipart();
568638

569639
if (!isMultipart) {
640+
logger.warn('Request is not multipart form data.');
570641
void reply.code(400);
571642
return reply.send(
572643
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_SCREENSHOT(
@@ -579,6 +650,7 @@ async function postScreenshotHandler(
579650
const imgData = await req.file();
580651

581652
if (!imgData) {
653+
logger.warn('No image provided.');
582654
void reply.code(400);
583655
return reply.send(
584656
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_SCREENSHOT('No image provided.')
@@ -594,6 +666,10 @@ async function postScreenshotHandler(
594666
);
595667

596668
if (maybeAttempt.hasError) {
669+
logger.error(
670+
{ error: maybeAttempt.error },
671+
'User screenshot cannot be linked to an exam attempt.'
672+
);
597673
void reply.code(500);
598674
return reply.send(
599675
ERRORS.FCC_ERR_EXAM_ENVIRONMENT(JSON.stringify(maybeAttempt.error))
@@ -603,6 +679,7 @@ async function postScreenshotHandler(
603679
const attempt = maybeAttempt.data;
604680

605681
if (attempt.length === 0) {
682+
logger.warn('No exam attempts found for user.');
606683
void reply.code(404);
607684
return reply.send(
608685
ERRORS.FCC_ERR_EXAM_ENVIRONMENT_EXAM_ATTEMPT(
@@ -615,6 +692,7 @@ async function postScreenshotHandler(
615692

616693
// Verify image is JPG using magic number
617694
if (imgBinary[0] !== 0xff || imgBinary[1] !== 0xd8 || imgBinary[2] !== 0xff) {
695+
logger.warn('Invalid image format');
618696
void reply.code(400);
619697
return reply.send(
620698
ERRORS.FCC_EINVAL_EXAM_ENVIRONMENT_SCREENSHOT('Invalid image format.')
@@ -642,6 +720,9 @@ async function getExams(
642720
req: UpdateReqType<typeof schemas.examEnvironmentExams>,
643721
reply: FastifyReply
644722
) {
723+
const logger = this.log.child({ req });
724+
logger.info({ user: req.user });
725+
645726
const user = req.user!;
646727
const exams = await this.prisma.envExam.findMany({
647728
where: {

0 commit comments

Comments
 (0)