Skip to content

Commit d397c68

Browse files
committed
Don't allow partial events in patch
1 parent 34e8ab7 commit d397c68

File tree

1 file changed

+29
-65
lines changed

1 file changed

+29
-65
lines changed

src/api/routes/events.ts

Lines changed: 29 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
318318
schema: withRoles(
319319
[AppRoles.EVENTS_MANAGER],
320320
withTags(["Events"], {
321-
body: postRequestSchema.partial(),
321+
body: postRequestSchema,
322322
params: z.object({
323323
id: z.string().min(1).meta({
324324
description: "Event ID to modify.",
@@ -327,12 +327,7 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
327327
}),
328328
response: {
329329
201: {
330-
description: "The event has been modified.",
331-
content: {
332-
"application/json": {
333-
schema: z.null(),
334-
},
335-
},
330+
description: "The event has been modified successfully.",
336331
},
337332
404: notFoundError,
338333
},
@@ -345,64 +340,29 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
345340
if (!request.username) {
346341
throw new UnauthenticatedError({ message: "Username not found." });
347342
}
343+
348344
try {
349-
const updatableFields = Object.keys(postRequestSchema.shape);
350345
const entryUUID = request.params.id;
351-
const requestData = request.body;
352-
353-
const setParts: string[] = [];
354-
const removeParts: string[] = [];
355-
const expressionAttributeNames: Record<string, string> = {};
356-
const expressionAttributeValues: Record<string, any> = {};
357-
358-
setParts.push("#updatedAt = :updatedAt");
359-
expressionAttributeNames["#updatedAt"] = "updatedAt";
360-
expressionAttributeValues[":updatedAt"] = new Date().toISOString();
361-
362-
updatableFields.forEach((key) => {
363-
if (Object.hasOwn(requestData, key)) {
364-
setParts.push(`#${key} = :${key}`);
365-
expressionAttributeNames[`#${key}`] = key;
366-
expressionAttributeValues[`:${key}`] =
367-
requestData[key as keyof typeof requestData];
368-
} else {
369-
removeParts.push(`#${key}`);
370-
expressionAttributeNames[`#${key}`] = key;
371-
}
372-
});
373-
374-
// Construct the final UpdateExpression by combining SET and REMOVE
375-
let updateExpression = `SET ${setParts.join(", ")}`;
376-
if (removeParts.length > 0) {
377-
updateExpression += ` REMOVE ${removeParts.join(", ")}`;
378-
}
346+
const updatedItem = {
347+
...request.body,
348+
id: entryUUID,
349+
updatedAt: new Date().toISOString(),
350+
};
379351

380-
const command = new UpdateItemCommand({
352+
const command = new PutItemCommand({
381353
TableName: genericConfig.EventsDynamoTableName,
382-
Key: { id: { S: entryUUID } },
383-
UpdateExpression: updateExpression,
384-
ExpressionAttributeNames: expressionAttributeNames,
354+
Item: marshall(updatedItem),
385355
ConditionExpression: "attribute_exists(id)",
386-
ExpressionAttributeValues: marshall(expressionAttributeValues),
387356
ReturnValues: "ALL_OLD",
388357
});
358+
389359
let oldAttributes;
390-
let updatedEntry;
391360
try {
392-
oldAttributes = (await fastify.dynamoClient.send(command)).Attributes;
393-
361+
const result = await fastify.dynamoClient.send(command);
362+
oldAttributes = result.Attributes;
394363
if (!oldAttributes) {
395-
throw new DatabaseInsertError({
396-
message: "Item not found or update failed.",
397-
});
364+
throw new NotFoundError({ endpointName: request.url });
398365
}
399-
400-
const oldEntry = oldAttributes ? unmarshall(oldAttributes) : null;
401-
// we know updateData has no undefines because we filtered them out.
402-
updatedEntry = {
403-
...oldEntry,
404-
...requestData,
405-
} as unknown as IUpdateDiscord;
406366
} catch (e: unknown) {
407367
if (
408368
e instanceof Error &&
@@ -414,16 +374,24 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
414374
throw e;
415375
}
416376
request.log.error(e);
417-
throw new DiscordEventError({});
377+
throw new DatabaseInsertError({
378+
message: "Failed to update event in Dynamo table.",
379+
});
418380
}
419-
if (updatedEntry.featured && !updatedEntry.repeats) {
381+
382+
const updatedEntryForDiscord = updatedItem as unknown as IUpdateDiscord;
383+
384+
if (
385+
updatedEntryForDiscord.featured &&
386+
!updatedEntryForDiscord.repeats
387+
) {
420388
try {
421389
await updateDiscord(
422390
{
423391
botToken: fastify.secretConfig.discord_bot_token,
424392
guildId: fastify.environmentConfig.DiscordGuildId,
425393
},
426-
updatedEntry,
394+
updatedEntryForDiscord,
427395
request.username,
428396
false,
429397
request.log,
@@ -437,10 +405,11 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
437405
);
438406

439407
if (e instanceof Error) {
440-
request.log.error(`Failed to publish event to Discord: ${e} `);
408+
request.log.error(`Failed to publish event to Discord: ${e}`);
441409
}
442410
}
443411
}
412+
444413
const postUpdatePromises = [
445414
atomicIncrementCacheCounter(
446415
fastify.dynamoClient,
@@ -467,13 +436,7 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
467436
];
468437
await Promise.all(postUpdatePromises);
469438

470-
reply
471-
.status(201)
472-
.header(
473-
"Location",
474-
`${fastify.environmentConfig.UserFacingUrl}/api/v1/events/${entryUUID}`,
475-
)
476-
.send();
439+
reply.status(201).send();
477440
} catch (e: unknown) {
478441
if (e instanceof Error) {
479442
request.log.error(`Failed to update DynamoDB: ${e.toString()}`);
@@ -487,6 +450,7 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async (
487450
}
488451
},
489452
);
453+
490454
fastify.withTypeProvider<FastifyZodOpenApiTypeProvider>().post(
491455
"",
492456
{

0 commit comments

Comments
 (0)