diff --git a/client/src/models/__generated__/schema.d.ts b/client/src/models/__generated__/schema.d.ts index 45964e77..d6f06ba2 100644 --- a/client/src/models/__generated__/schema.d.ts +++ b/client/src/models/__generated__/schema.d.ts @@ -162,6 +162,10 @@ export interface paths { /** @description Endpoint for admin to create a new event */ post: operations["CreateNewEvent"]; }; + "/admin/events/{id}": { + /** @description Endpoint for admints to edit an event. */ + patch: operations["EditEvent"]; + }; } export type webhooks = Record; @@ -716,6 +720,48 @@ export interface components { CreateEventBody: { data: components["schemas"]["Event"]; }; + /** @description Make all properties in T optional */ + Partial_Event_: { + /** @description The title of this event */ + title?: string; + /** + * @description An optional description for this event + * This should be in markdown + */ + description?: string; + /** @description The link for the image to display on the event page (essentially a thumbnail) */ + image_url?: string; + /** @description The location of this event */ + location?: string; + /** + * @description The signup period start date. + * Note that this date is in UTC time. + * Use the same start and end date to indicate a 1 day signup period. + */ + start_date?: components["schemas"]["FirebaseFirestore.Timestamp"]; + /** + * @description The signup period end date. + * Note that this date is in UTC time. + */ + end_date?: components["schemas"]["FirebaseFirestore.Timestamp"]; + /** + * @description Event start date for the event i.e the day members should meet at shads, + * **NOT** the signups, refer to {@link start_date} for signup start + */ + physical_start_date?: components["schemas"]["FirebaseFirestore.Timestamp"]; + /** + * @description Event end time for the event i.e the last day members will be at the lodge, + * is optionial in case of one day events. **NOT** the signups, refer to + * {@link end_date} for signup end date + */ + physical_end_date?: components["schemas"]["FirebaseFirestore.Timestamp"]; + /** + * Format: double + * @description Max number of attendees at this event, left as optional for uncapped + * @example 30 + */ + max_occupancy?: number; + }; }; responses: { }; @@ -1244,4 +1290,23 @@ export interface operations { }; }; }; + /** @description Endpoint for admints to edit an event. */ + EditEvent: { + parameters: { + path: { + id: string; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["Partial_Event_"]; + }; + }; + responses: { + /** @description Successfully edited the event! */ + 200: { + content: never; + }; + }; + }; } diff --git a/server/src/middleware/__generated__/routes.ts b/server/src/middleware/__generated__/routes.ts index ba834cd3..ec44ff08 100644 --- a/server/src/middleware/__generated__/routes.ts +++ b/server/src/middleware/__generated__/routes.ts @@ -566,6 +566,11 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + "Partial_Event_": { + "dataType": "refAlias", + "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"title":{"dataType":"string"},"description":{"dataType":"string"},"image_url":{"dataType":"string"},"location":{"dataType":"string"},"start_date":{"ref":"FirebaseFirestore.Timestamp"},"end_date":{"ref":"FirebaseFirestore.Timestamp"},"physical_start_date":{"ref":"FirebaseFirestore.Timestamp"},"physical_end_date":{"ref":"FirebaseFirestore.Timestamp"},"max_occupancy":{"dataType":"double"}},"validators":{}}, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa }; const templateService = new ExpressTemplateService(models, {"noImplicitAdditionalProperties":"throw-on-extras","bodyCoercion":true}); @@ -1502,6 +1507,38 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.patch('/admin/events/:id', + authenticateMiddleware([{"jwt":["admin"]}]), + ...(fetchMiddlewares(AdminController)), + ...(fetchMiddlewares(AdminController.prototype.editEvent)), + + function AdminController_editEvent(request: ExRequest, response: ExResponse, next: any) { + const args: Record = { + id: {"in":"path","name":"id","required":true,"dataType":"string"}, + requestBody: {"in":"body","name":"requestBody","required":true,"ref":"Partial_Event_"}, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ args, request, response }); + + const controller = new AdminController(); + + templateService.apiHandler({ + methodName: 'editEvent', + controller, + response, + next, + validatedArgs, + successStatus: 200, + }); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/server/src/middleware/__generated__/swagger.json b/server/src/middleware/__generated__/swagger.json index 55b8a49b..cf914607 100644 --- a/server/src/middleware/__generated__/swagger.json +++ b/server/src/middleware/__generated__/swagger.json @@ -1417,6 +1417,50 @@ ], "type": "object", "additionalProperties": false + }, + "Partial_Event_": { + "properties": { + "title": { + "type": "string", + "description": "The title of this event" + }, + "description": { + "type": "string", + "description": "An optional description for this event\nThis should be in markdown" + }, + "image_url": { + "type": "string", + "description": "The link for the image to display on the event page (essentially a thumbnail)" + }, + "location": { + "type": "string", + "description": "The location of this event" + }, + "start_date": { + "$ref": "#/components/schemas/FirebaseFirestore.Timestamp", + "description": "The signup period start date.\nNote that this date is in UTC time.\nUse the same start and end date to indicate a 1 day signup period." + }, + "end_date": { + "$ref": "#/components/schemas/FirebaseFirestore.Timestamp", + "description": "The signup period end date.\nNote that this date is in UTC time." + }, + "physical_start_date": { + "$ref": "#/components/schemas/FirebaseFirestore.Timestamp", + "description": "Event start date for the event i.e the day members should meet at shads,\n**NOT** the signups, refer to {@link start_date} for signup start" + }, + "physical_end_date": { + "$ref": "#/components/schemas/FirebaseFirestore.Timestamp", + "description": "Event end time for the event i.e the last day members will be at the lodge,\nis optionial in case of one day events. **NOT** the signups, refer to\n{@link end_date} for signup end date" + }, + "max_occupancy": { + "type": "number", + "format": "double", + "description": "Max number of attendees at this event, left as optional for uncapped", + "example": 30 + } + }, + "type": "object", + "description": "Make all properties in T optional" } }, "securitySchemes": { @@ -2469,6 +2513,44 @@ } } } + }, + "/admin/events/{id}": { + "patch": { + "operationId": "EditEvent", + "responses": { + "200": { + "description": "Successfully edited the event!" + } + }, + "description": "Endpoint for admints to edit an event.", + "security": [ + { + "jwt": [ + "admin" + ] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Partial_Event_" + } + } + } + } + } } }, "servers": [ diff --git a/server/src/middleware/tests/AdminController.test.ts b/server/src/middleware/tests/AdminController.test.ts index ff25b358..6a492f4a 100644 --- a/server/src/middleware/tests/AdminController.test.ts +++ b/server/src/middleware/tests/AdminController.test.ts @@ -817,6 +817,7 @@ describe("AdminController endpoint tests", () => { start_date: dateToFirestoreTimeStamp(new Date()), end_date: dateToFirestoreTimeStamp(new Date()) } + const eventService = new EventService() it("should let admins create an event", async () => { const res = await request @@ -827,7 +828,18 @@ describe("AdminController endpoint tests", () => { expect(res.status).toEqual(201) // There should not be more than 1, even if we request more - expect((await new EventService().getAllEvents(69)).events).toHaveLength(1) + expect((await eventService.getAllEvents(69)).events).toHaveLength(1) + }) + it("should let admins edit an event", async () => { + const newEvent = await eventService.createEvent(event1) + const res = await request + .patch("/admin/events/" + newEvent.id) + .set("Authorization", `Bearer ${adminToken}`) + .send({ title: "Cool event!", location: "UoA" } as Partial) + expect(res.status).toEqual(200) + const fetchedEvent = await eventService.getEventById(newEvent.id) + expect(fetchedEvent.title).toEqual("Cool event!") + expect(fetchedEvent.location).toEqual("UoA") }) }) }) diff --git a/server/src/service-layer/controllers/AdminController.ts b/server/src/service-layer/controllers/AdminController.ts index ffa160cb..ea727188 100644 --- a/server/src/service-layer/controllers/AdminController.ts +++ b/server/src/service-layer/controllers/AdminController.ts @@ -8,7 +8,7 @@ import { firestoreTimestampToDate, timestampsInRange } from "data-layer/adapters/DateUtils" -import { UserAdditionalInfo } from "data-layer/models/firebase" +import { UserAdditionalInfo, Event } from "data-layer/models/firebase" import BookingDataService from "data-layer/services/BookingDataService" import BookingSlotService from "data-layer/services/BookingSlotsService" import UserDataService from "data-layer/services/UserDataService" @@ -710,4 +710,26 @@ export class AdminController extends Controller { this.setStatus(500) } } + + /** + * Endpoint for admints to edit an event. + */ + @SuccessResponse("200", "Successfully edited the event!") + @Patch("events/{id}") + public async editEvent( + @Path() id: string, + @Body() requestBody: Partial + ) { + const eventService = new EventService() + const fetchedEvent = await eventService.getEventById(id) + if (!fetchedEvent) { + this.setStatus(404) + } + try { + eventService.updateEvent(id, requestBody) + } catch (e) { + this.setStatus(500) + } + this.setStatus(200) + } }