Skip to content

Commit 0490f55

Browse files
authored
refactor schemas and logging (#126)
* refactor schemas and logging * fix tests * fix URL * fix function and add unit tests for creating room requests * add status update unit tests
1 parent 155a3d2 commit 0490f55

File tree

5 files changed

+587
-15
lines changed

5 files changed

+587
-15
lines changed

src/api/routes/roomRequests.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
3030
import { withRoles, withTags } from "api/components/index.js";
3131
import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
3232
import { z } from "zod";
33+
import { buildAuditLogTransactPut } from "api/functions/auditLog.js";
34+
import { Modules } from "common/modules.js";
3335

3436
const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => {
3537
await fastify.register(rateLimiter, {
@@ -93,7 +95,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => {
9395
});
9496
}
9597
const createdAt = new Date().toISOString();
96-
const command = new PutItemCommand({
98+
const itemPut = {
9799
TableName: genericConfig.RoomRequestsStatusTableName,
98100
Item: marshall({
99101
requestId,
@@ -102,9 +104,22 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => {
102104
createdBy: request.username,
103105
...request.body,
104106
}),
107+
};
108+
const logPut = buildAuditLogTransactPut({
109+
entry: {
110+
module: Modules.ROOM_RESERVATIONS,
111+
actor: request.username!,
112+
target: `${semesterId}/${requestId}`,
113+
requestId: request.id,
114+
message: `Changed status to "${formatStatus(request.body.status)}".`,
115+
},
105116
});
106117
try {
107-
await fastify.dynamoClient.send(command);
118+
await fastify.dynamoClient.send(
119+
new TransactWriteItemsCommand({
120+
TransactItems: [{ Put: itemPut }, logPut],
121+
}),
122+
);
108123
} catch (e) {
109124
request.log.error(e);
110125
if (e instanceof BaseError) {
@@ -269,11 +284,22 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => {
269284
}
270285
const body = {
271286
...request.body,
287+
eventStart: request.body.eventStart.toUTCString(),
288+
eventEnd: request.body.eventStart.toUTCString(),
272289
requestId,
273290
userId: request.username,
274291
"userId#requestId": `${request.username}#${requestId}`,
275292
semesterId: request.body.semester,
276293
};
294+
const logPut = buildAuditLogTransactPut({
295+
entry: {
296+
module: Modules.ROOM_RESERVATIONS,
297+
actor: request.username!,
298+
target: `${request.body.semester}/${requestId}`,
299+
requestId: request.id,
300+
message: "Created room reservation request.",
301+
},
302+
});
277303
try {
278304
const createdAt = new Date().toISOString();
279305
const transactionCommand = new TransactWriteItemsCommand({
@@ -297,6 +323,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => {
297323
}),
298324
},
299325
},
326+
logPut,
300327
],
301328
});
302329
await fastify.dynamoClient.send(transactionCommand);

src/common/modules.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export enum Modules {
99
LINKRY = "linkry",
1010
AUDIT_LOG = "auditLog",
1111
API_KEY = "apiKey",
12+
ROOM_RESERVATIONS = "roomReservations",
1213
}
1314

1415

@@ -23,4 +24,5 @@ export const ModulesToHumanName: Record<Modules, string> = {
2324
[Modules.LINKRY]: "Link Shortener",
2425
[Modules.AUDIT_LOG]: "Audit Log",
2526
[Modules.API_KEY]: "API Keys",
27+
[Modules.ROOM_RESERVATIONS]: "Room Reservations",
2628
}

src/common/types/roomRequest.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,10 @@ export const roomRequestSchema = roomRequestBaseSchema
184184
// Existing fields
185185
hostingMinors: z.boolean(),
186186
locationType: z.enum(["in-person", "virtual", "both"]),
187-
spaceType: z.string().min(1),
188-
specificRoom: z.string().min(1),
189-
estimatedAttendees: z.number().positive(),
190-
seatsNeeded: z.number().positive(),
187+
spaceType: z.optional(z.string().min(1)),
188+
specificRoom: z.optional(z.string().min(1)),
189+
estimatedAttendees: z.optional(z.number().positive()),
190+
seatsNeeded: z.optional(z.number().positive()),
191191
setupDetails: z.string().min(1).nullable().optional(),
192192
onCampusPartners: z.string().min(1).nullable(),
193193
offCampusPartners: z.string().min(1).nullable(),
@@ -266,10 +266,10 @@ export const roomRequestSchema = roomRequestBaseSchema
266266
)
267267
.refine(
268268
(data) => {
269-
if (data.setupDetails === undefined && specificRoomSetupRooms.includes(data.spaceType)) {
269+
if (data.setupDetails === undefined && specificRoomSetupRooms.includes(data.spaceType || "")) {
270270
return false;
271271
}
272-
if (data.setupDetails && !specificRoomSetupRooms.includes(data.spaceType)) {
272+
if (data.setupDetails && !specificRoomSetupRooms.includes(data.spaceType || "")) {
273273
return false;
274274
}
275275
return true;
@@ -280,33 +280,35 @@ export const roomRequestSchema = roomRequestBaseSchema
280280
},
281281
)
282282
.superRefine((data, ctx) => {
283-
// Additional validation for conditional fields based on locationType
284-
if (data.locationType === "in-person" || data.locationType === "both") {
285-
if (!data.spaceType || data.spaceType.length === 0) {
283+
const isPhysicalLocation = data.locationType === "in-person" || data.locationType === "both";
284+
285+
// Conditional physical location fields
286+
if (isPhysicalLocation) {
287+
if (!data.spaceType || data.spaceType.trim().length === 0) {
286288
ctx.addIssue({
287289
code: z.ZodIssueCode.custom,
288290
message: "Please select a space type",
289291
path: ["spaceType"],
290292
});
291293
}
292294

293-
if (!data.specificRoom || data.specificRoom.length === 0) {
295+
if (!data.specificRoom || data.specificRoom.trim().length === 0) {
294296
ctx.addIssue({
295297
code: z.ZodIssueCode.custom,
296298
message: "Please provide details about the room location",
297299
path: ["specificRoom"],
298300
});
299301
}
300302

301-
if (!data.estimatedAttendees || data.estimatedAttendees <= 0) {
303+
if (data.estimatedAttendees == null || data.estimatedAttendees <= 0) {
302304
ctx.addIssue({
303305
code: z.ZodIssueCode.custom,
304306
message: "Please provide an estimated number of attendees",
305307
path: ["estimatedAttendees"],
306308
});
307309
}
308310

309-
if (!data.seatsNeeded || data.seatsNeeded <= 0) {
311+
if (data.seatsNeeded == null || data.seatsNeeded <= 0) {
310312
ctx.addIssue({
311313
code: z.ZodIssueCode.custom,
312314
message: "Please specify how many seats you need",
@@ -357,8 +359,26 @@ export const roomRequestSchema = roomRequestBaseSchema
357359
path: ["nonIllinoisAttendees"],
358360
});
359361
}
362+
363+
// Setup details logic
364+
if (data.setupDetails === undefined && specificRoomSetupRooms.includes(data.spaceType || "")) {
365+
ctx.addIssue({
366+
code: z.ZodIssueCode.custom,
367+
message: "Invalid setup details response.",
368+
path: ["setupDetails"],
369+
});
370+
}
371+
372+
if (data.setupDetails && !specificRoomSetupRooms.includes(data.spaceType || "")) {
373+
ctx.addIssue({
374+
code: z.ZodIssueCode.custom,
375+
message: "Invalid setup details response.",
376+
path: ["setupDetails"],
377+
});
378+
}
360379
});
361380

381+
362382
export type RoomRequestFormValues = z.infer<typeof roomRequestSchema>;
363383

364384
export const roomRequestGetResponse = z.object({

src/ui/pages/roomRequest/RoomRequestLanding.page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const ManageRoomRequestsPage: React.FC = () => {
2222
const createRoomRequest = async (
2323
payload: RoomRequestFormValues
2424
): Promise<RoomRequestPostResponse> => {
25-
const response = await api.post(`/api/v1/roomRequests/`, payload);
25+
const response = await api.post(`/api/v1/roomRequests`, payload);
2626
return response.data;
2727
};
2828

0 commit comments

Comments
 (0)