From 51e3851ed507553734db1cb167c47d5bc99b7d15 Mon Sep 17 00:00:00 2001 From: 42 <33488710+FortyTwoFortyTwo@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:31:26 +0100 Subject: [PATCH 1/3] Add admin route to update event by id --- api-examples/login.rest | 6 ++-- api-examples/updateEvent.rest | 14 ++++++++ src/controllers/event.controller.ts | 52 +++++++++++++++++++++++------ src/routes/event.routes.ts | 8 +++-- src/services/event.service.ts | 5 +++ 5 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 api-examples/updateEvent.rest diff --git a/api-examples/login.rest b/api-examples/login.rest index e79fc7b..37005eb 100644 --- a/api-examples/login.rest +++ b/api-examples/login.rest @@ -2,7 +2,7 @@ POST http://localhost:3000/auth/login Content-Type: application/json { - "email": "admin@example.com", - "password": "supersecretadminpassword", - "username": "admin" + "email": "bob@builder.com", + "password": "builder", + "username": "bob" } diff --git a/api-examples/updateEvent.rest b/api-examples/updateEvent.rest new file mode 100644 index 0000000..a90fa02 --- /dev/null +++ b/api-examples/updateEvent.rest @@ -0,0 +1,14 @@ +PUT http://localhost:3000/events/683f38cbecb0f8e7404c76a1 +Content-Type: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2ODNkODY0YzQ5YmI1MDYxNTM3YjA1NTMiLCJuYW1lX2ZpcnN0IjoiQm9iIiwibmFtZV9sYXN0IjoiQnVpbGRlciIsImVtYWlsIjoiYm9iQGJ1aWxkZXIuY29tIiwidXNlcm5hbWUiOiJib2IiLCJzYXZlZF9ldmVudHMiOltdLCJyb2xlIjoidXNlciJ9.8f-bElJ6461QVgMFeA0rE72ionI5iuB7JOLrsa159fQ + +{ + "mode": "other", + "name": "Something", + "description": "For something", + "location": "Earth", + "date": "Tomorrow", + "price": 42, + "distance": 0, + "url": "http://localhost:3000" +} \ No newline at end of file diff --git a/src/controllers/event.controller.ts b/src/controllers/event.controller.ts index a452c34..167f4d3 100644 --- a/src/controllers/event.controller.ts +++ b/src/controllers/event.controller.ts @@ -2,7 +2,7 @@ import { Context, ObjectId, RouterContext, Status } from '../../deps.ts'; import { eventService } from '../services/event.service.ts'; import { generateEvents } from '../services/openai.service.ts'; -import { eventFilterSchema } from 'models/event.model.ts'; +import { eventSchema, eventFilterSchema } from 'models/event.model.ts'; export const getAllEvents = async (ctx: Context) => { const params = ctx.request.url.searchParams; @@ -55,6 +55,46 @@ export const getEventById = async (ctx: RouterContext<'/:id'>) => { ctx.response.body = event; }; +export const updateEventById = async (ctx: Context) => { + const user = ctx.state.user; + + if (!user) { + ctx.response.status = Status.Unauthorized; + ctx.response.body = { message: 'User not authenticated' }; + return; + } + + const id: string = ctx.params.id; + const event = await ctx.request.body.json(); + + if (!ObjectId.isValid(id)) { + ctx.response.status = Status.BadRequest; + ctx.response.body = `Invalid event id "${id}"`; + return; + } + + if (!eventService.isEvent(event)) { + ctx.response.status = Status.BadRequest; + ctx.response.body = `Failed to validate event body`; + return; + } + + const result = await eventService.updateEventById(id, event); + + if (!result.acknowledged) { + ctx.response.status = Status.NotFound; + ctx.response.body = `Failed to update event by id "${id}"`; + return; + } + + ctx.response.body = { message: `Updated event id "${id}"` }; +}; + +export const deleteEventById = async (ctx: Context) => { + // TODO: Delete event from DB + ctx.response.body = { message: `Delete event params.id` }; +}; + export const saveNewEvent = async (ctx: Context) => { // Things to note. Will this need admin authorisation? Possible need to implement token auth on this route or admin auth middleware? const event = await ctx.request.body.json(); @@ -97,13 +137,3 @@ export const saveEventsCronHandler = async (ctx: Context) => { console.log('There were no events to save'); } }; - -export const updateEvent = async (ctx: Context) => { - // TODO: Update event in DB - ctx.response.body = { message: `Update event of params.id` }; -}; - -export const deleteEvent = async (ctx: Context) => { - // TODO: Delete event from DB - ctx.response.body = { message: `Delete event params.id` }; -}; diff --git a/src/routes/event.routes.ts b/src/routes/event.routes.ts index eddfb69..d2e8f01 100644 --- a/src/routes/event.routes.ts +++ b/src/routes/event.routes.ts @@ -2,9 +2,13 @@ import { Router } from '../../deps.ts'; import { getAllEvents, getEventById, + updateEventById, + deleteEventById, saveEventsCronHandler, saveNewEvent, } from '../controllers/event.controller.ts'; +import ProtectRoute from '../middleware/protectRoute.ts'; +import protectAdmin from '../middleware/requireAdmin.ts'; const router = new Router(); @@ -13,11 +17,11 @@ const router = new Router(); // -> to controllers router.get('/', getAllEvents); router.get('/:id', getEventById); +router.put("/:id", ProtectRoute, protectAdmin, updateEventById) +router.delete("/:id", deleteEventById) router.post('/save-event', saveNewEvent); router.post('/cron/save-events', saveEventsCronHandler); // router.post("/") -// router.put("/:id", updateEvent) -// router.delete("/:id", deleteEvent) // router.post("/generate", generateEvents) (using the openAi service) diff --git a/src/services/event.service.ts b/src/services/event.service.ts index 67bfa7c..21fcb7d 100644 --- a/src/services/event.service.ts +++ b/src/services/event.service.ts @@ -32,6 +32,10 @@ const getEventById = async (id: string): Promise => { return await events.findOne({ _id: new ObjectId(id) }); }; +const updateEventById = async (id: string, event: FullEvent): Promise => { + return await events.updateOne({ _id: new ObjectId(id) }, { $set: event }); +}; + const databaseIncludes = async (event: FullEvent): Promise => { const existingEvent = await events.findOne({ eventKey: event.eventKey }); return existingEvent !== null; @@ -93,6 +97,7 @@ const saveEvents = async (input: Event | CompleteEventType) => { export const eventService = { getAllEvents, getEventById, + updateEventById, saveEvents, isEvent, }; From 7d1fac82d10a9b3cd7086dcc903b8789a2cc6239 Mon Sep 17 00:00:00 2001 From: 42 <33488710+FortyTwoFortyTwo@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:40:29 +0100 Subject: [PATCH 2/3] Add admin route to delete event by id --- api-examples/deleteEvent.rest | 3 +++ src/controllers/event.controller.ts | 27 +++++++++++++++++++++++++-- src/services/event.service.ts | 7 ++++++- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 api-examples/deleteEvent.rest diff --git a/api-examples/deleteEvent.rest b/api-examples/deleteEvent.rest new file mode 100644 index 0000000..2763467 --- /dev/null +++ b/api-examples/deleteEvent.rest @@ -0,0 +1,3 @@ +DELETE http://localhost:3000/events/683f38cbecb0f8e7404c76a2 +Content-Type: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2ODNkODY0YzQ5YmI1MDYxNTM3YjA1NTMiLCJuYW1lX2ZpcnN0IjoiQm9iIiwibmFtZV9sYXN0IjoiQnVpbGRlciIsImVtYWlsIjoiYm9iQGJ1aWxkZXIuY29tIiwidXNlcm5hbWUiOiJib2IiLCJzYXZlZF9ldmVudHMiOltdLCJyb2xlIjoidXNlciJ9.8f-bElJ6461QVgMFeA0rE72ionI5iuB7JOLrsa159fQ diff --git a/src/controllers/event.controller.ts b/src/controllers/event.controller.ts index 167f4d3..46278dc 100644 --- a/src/controllers/event.controller.ts +++ b/src/controllers/event.controller.ts @@ -91,8 +91,31 @@ export const updateEventById = async (ctx: Context) => { }; export const deleteEventById = async (ctx: Context) => { - // TODO: Delete event from DB - ctx.response.body = { message: `Delete event params.id` }; + const user = ctx.state.user; + + if (!user) { + ctx.response.status = Status.Unauthorized; + ctx.response.body = { message: 'User not authenticated' }; + return; + } + + const id: string = ctx.params.id; + + if (!ObjectId.isValid(id)) { + ctx.response.status = Status.BadRequest; + ctx.response.body = `Invalid event id "${id}"`; + return; + } + + const result = await eventService.deleteEventById(id); + + if (result.deletedCount == 0) { + ctx.response.status = Status.NotFound; + ctx.response.body = `Could not find event id "${id}"`; + return; + } + + ctx.response.body = { message: `Successfully deleted event id "${id}"` }; }; export const saveNewEvent = async (ctx: Context) => { diff --git a/src/services/event.service.ts b/src/services/event.service.ts index 21fcb7d..db9d2ff 100644 --- a/src/services/event.service.ts +++ b/src/services/event.service.ts @@ -32,10 +32,14 @@ const getEventById = async (id: string): Promise => { return await events.findOne({ _id: new ObjectId(id) }); }; -const updateEventById = async (id: string, event: FullEvent): Promise => { +const updateEventById = async (id: string, event: FullEvent) => { return await events.updateOne({ _id: new ObjectId(id) }, { $set: event }); }; +const deleteEventById = async (id: string) => { + return await events.deleteOne({ _id: new ObjectId(id) }); +}; + const databaseIncludes = async (event: FullEvent): Promise => { const existingEvent = await events.findOne({ eventKey: event.eventKey }); return existingEvent !== null; @@ -98,6 +102,7 @@ export const eventService = { getAllEvents, getEventById, updateEventById, + deleteEventById, saveEvents, isEvent, }; From e06f3ba8e8971bbee91ff6e8167f1499656c9aee Mon Sep 17 00:00:00 2001 From: 42 <33488710+FortyTwoFortyTwo@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:46:58 +0100 Subject: [PATCH 3/3] Fix type checking --- src/controllers/event.controller.ts | 9 ++++----- src/routes/event.routes.ts | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/controllers/event.controller.ts b/src/controllers/event.controller.ts index 46278dc..faebce5 100644 --- a/src/controllers/event.controller.ts +++ b/src/controllers/event.controller.ts @@ -1,8 +1,7 @@ -// deno-lint-ignore-file require-await import { Context, ObjectId, RouterContext, Status } from '../../deps.ts'; import { eventService } from '../services/event.service.ts'; import { generateEvents } from '../services/openai.service.ts'; -import { eventSchema, eventFilterSchema } from 'models/event.model.ts'; +import { eventFilterSchema, FullEvent } from 'models/event.model.ts'; export const getAllEvents = async (ctx: Context) => { const params = ctx.request.url.searchParams; @@ -55,7 +54,7 @@ export const getEventById = async (ctx: RouterContext<'/:id'>) => { ctx.response.body = event; }; -export const updateEventById = async (ctx: Context) => { +export const updateEventById = async (ctx: RouterContext<'/:id'>) => { const user = ctx.state.user; if (!user) { @@ -65,7 +64,7 @@ export const updateEventById = async (ctx: Context) => { } const id: string = ctx.params.id; - const event = await ctx.request.body.json(); + const event: FullEvent = await ctx.request.body.json(); if (!ObjectId.isValid(id)) { ctx.response.status = Status.BadRequest; @@ -90,7 +89,7 @@ export const updateEventById = async (ctx: Context) => { ctx.response.body = { message: `Updated event id "${id}"` }; }; -export const deleteEventById = async (ctx: Context) => { +export const deleteEventById = async (ctx: RouterContext<'/:id'>) => { const user = ctx.state.user; if (!user) { diff --git a/src/routes/event.routes.ts b/src/routes/event.routes.ts index d2e8f01..b78f17b 100644 --- a/src/routes/event.routes.ts +++ b/src/routes/event.routes.ts @@ -1,11 +1,11 @@ import { Router } from '../../deps.ts'; import { + deleteEventById, getAllEvents, getEventById, - updateEventById, - deleteEventById, saveEventsCronHandler, saveNewEvent, + updateEventById, } from '../controllers/event.controller.ts'; import ProtectRoute from '../middleware/protectRoute.ts'; import protectAdmin from '../middleware/requireAdmin.ts'; @@ -17,8 +17,8 @@ const router = new Router(); // -> to controllers router.get('/', getAllEvents); router.get('/:id', getEventById); -router.put("/:id", ProtectRoute, protectAdmin, updateEventById) -router.delete("/:id", deleteEventById) +router.put('/:id', ProtectRoute, protectAdmin, updateEventById); +router.delete('/:id', ProtectRoute, protectAdmin, deleteEventById); router.post('/save-event', saveNewEvent); router.post('/cron/save-events', saveEventsCronHandler); // router.post("/")