Skip to content

Commit

Permalink
feat: Session reminder and expiry events
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Oct 3, 2023
1 parent 409d2ca commit 3665eef
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 93 deletions.
76 changes: 0 additions & 76 deletions api.planx.uk/modules/webhooks/_old/lowcalSessionEvents.ts

This file was deleted.

35 changes: 35 additions & 0 deletions api.planx.uk/modules/webhooks/controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ServerError } from "../../errors";
import { CreateSessionEventController } from "./lowcalSessionEvents/schema";
import {
createSessionExpiryEvent,
createSessionReminderEvent,
} from "./lowcalSessionEvents/service";
import { CreatePaymentEventController } from "./paymentRequestEvents/schema";
import {
createPaymentExpiryEvents,
Expand Down Expand Up @@ -80,3 +85,33 @@ export const createPaymentExpiryEventsController: CreatePaymentEventController =
);
}
};

export const createSessionReminderEventController: CreateSessionEventController =
async (req, res, next) => {
try {
const response = await createSessionReminderEvent(req.body);
res.json(response);
} catch (error) {
return next(
new ServerError({
message: "Failed to create session reminder event",
cause: error,
}),
);
}
};

export const createSessionExpiryEventController: CreateSessionEventController =
async (req, res, next) => {
try {
const response = await createSessionExpiryEvent(req.body);
res.json(response);
} catch (error) {
return next(
new ServerError({
message: "Failed to create session expiry event",
cause: error,
}),
);
}
};
45 changes: 44 additions & 1 deletion api.planx.uk/modules/webhooks/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ components:
properties:
paymentRequestId:
type: string
CreateSessionEvent:
type: object
properties:
createdAt:
required: true
type: string
format: date-time
payload:
required: true
type: object
properties:
sessionId:
type: string
responses:
SlackNotificationSuccessMessage:
content:
Expand Down Expand Up @@ -204,4 +217,34 @@ paths:
"200":
$ref: "#/components/responses/ScheduledEvent"
"500":
$ref: "#/components/responses/ErrorMessage"
$ref: "#/components/responses/ErrorMessage"
/webhooks/hasura/create-reminder-event:
post:
tags: ["webhooks"]
summary: Create session reminder event
description: Setup events which will trigger session reminder emails to be generated
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/CreateSessionEvent"
responses:
"200":
$ref: "#/components/responses/ScheduledEvent"
"500":
$ref: "#/components/responses/ErrorMessage"
/webhooks/hasura/create-expiry-event:
post:
tags: ["webhooks"]
summary: Create session expiry event
description: Setup events which will trigger session expiry email to be generated
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/CreateSessionEvent"
responses:
"200":
$ref: "#/components/responses/ScheduledEvent"
"500":
$ref: "#/components/responses/ErrorMessage"
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ describe("Create reminder event webhook", () => {
.set({ Authorization: process.env.HASURA_PLANX_API_KEY })
.send(body)
.expect(400)
.then((response) =>
expect(response.body.error).toEqual("Required value missing"),
);
.then((response) => {
expect(response.body).toHaveProperty("issues");
expect(response.body).toHaveProperty("name", "ZodError");
});
}
});

Expand Down Expand Up @@ -103,7 +104,9 @@ describe("Create reminder event webhook", () => {
.send(body)
.expect(500)
.then((response) => {
expect(response.body.error).toMatch(/Failed to create reminder event/);
expect(response.body.error).toMatch(
/Failed to create session reminder event/,
);
});
});
});
Expand Down Expand Up @@ -132,9 +135,10 @@ describe("Create expiry event webhook", () => {
.set({ Authorization: process.env.HASURA_PLANX_API_KEY })
.send(body)
.expect(400)
.then((response) =>
expect(response.body.error).toEqual("Required value missing"),
);
.then((response) => {
expect(response.body).toHaveProperty("issues");
expect(response.body).toHaveProperty("name", "ZodError");
});
}
});

Expand All @@ -147,7 +151,7 @@ describe("Create expiry event webhook", () => {
.send(body)
.expect(200)
.then((response) => {
expect(response.body).toStrictEqual(mockScheduledEventResponse);
expect(response.body).toStrictEqual([mockScheduledEventResponse]);
});
});

Expand All @@ -160,7 +164,7 @@ describe("Create expiry event webhook", () => {
.send(body)
.expect(200)
.then((response) => {
expect(response.body).toStrictEqual(mockScheduledEventResponse);
expect(response.body).toStrictEqual([mockScheduledEventResponse]);
});
const mockArgs = mockedCreateScheduledEvent.mock.calls[0][0];
expect(mockArgs.webhook).toBe("{{HASURA_PLANX_API_URL}}/send-email/expiry");
Expand All @@ -177,7 +181,9 @@ describe("Create expiry event webhook", () => {
.send(body)
.expect(500)
.then((response) => {
expect(response.body.error).toMatch(/Failed to create expiry event/);
expect(response.body.error).toMatch(
/Failed to create session expiry event/,
);
});
});
});
21 changes: 21 additions & 0 deletions api.planx.uk/modules/webhooks/lowcalSessionEvents/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from "zod";
import { ValidatedRequestHandler } from "../../../shared/middleware/validate";
import { ScheduledEventResponse } from "../../../hasura/metadata";

export const createSessionEventSchema = z.object({
body: z.object({
createdAt: z.string().transform((val) => new Date(val)),
payload: z.object({
sessionId: z.string(),
}),
}),
});

export type CreateSessionEvent = z.infer<
typeof createSessionEventSchema
>["body"];

export type CreateSessionEventController = ValidatedRequestHandler<
typeof createSessionEventSchema,
ScheduledEventResponse[]
>;
44 changes: 44 additions & 0 deletions api.planx.uk/modules/webhooks/lowcalSessionEvents/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { addDays } from "date-fns";

import { createScheduledEvent } from "../../../hasura/metadata";
import {
DAYS_UNTIL_EXPIRY,
REMINDER_DAYS_FROM_EXPIRY,
} from "../../../saveAndReturn/utils";
import { CreateSessionEvent } from "./schema";

/**
* Create "reminder" events for a lowcal_session record
*/
export const createSessionReminderEvent = async ({
createdAt,
payload,
}: CreateSessionEvent) => {
const response = await Promise.all(
REMINDER_DAYS_FROM_EXPIRY.map((day: number) =>
createScheduledEvent({
webhook: "{{HASURA_PLANX_API_URL}}/send-email/reminder",
schedule_at: addDays(createdAt, DAYS_UNTIL_EXPIRY - day),
payload: payload,
comment: `reminder_${payload.sessionId}_${day}day`,
}),
),
);
return response;
};

/**
* Create an "expiry" event for a lowcal_session record
*/
export const createSessionExpiryEvent = async ({
createdAt,
payload,
}: CreateSessionEvent) => {
const response = await createScheduledEvent({
webhook: "{{HASURA_PLANX_API_URL}}/send-email/expiry",
schedule_at: addDays(createdAt, DAYS_UNTIL_EXPIRY),
payload: payload,
comment: `expiry_${payload.sessionId}`,
});
return [response];
};
19 changes: 13 additions & 6 deletions api.planx.uk/modules/webhooks/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ import { Router } from "express";
import { useHasuraAuth } from "../auth/middleware";
import { createPaymentSendEvents } from "../../inviteToPay/createPaymentSendEvents";
import { sanitiseApplicationData } from "./_old/sanitiseApplicationData";
import {
createExpiryEvent,
createReminderEvent,
} from "./_old/lowcalSessionEvents";
import { validate } from "../../shared/middleware/validate";
import {
createPaymentExpiryEventsController,
createPaymentInvitationEventsController,
createPaymentReminderEventsController,
createSessionExpiryEventController,
createSessionReminderEventController,
sendSlackNotificationController,
} from "./controller";
import { sendSlackNotificationSchema } from "./sendNotification/schema";
import { createPaymentEventSchema } from "./paymentRequestEvents/schema";
import { createSessionEventSchema } from "./lowcalSessionEvents/schema";

const router = Router();

Expand All @@ -39,11 +38,19 @@ router.post(
validate(sendSlackNotificationSchema),
sendSlackNotificationController,
);
router.post(
"/hasura/create-reminder-event",
validate(createSessionEventSchema),
createSessionReminderEventController,
);
router.post(
"/hasura/create-expiry-event",
validate(createSessionEventSchema),
createSessionExpiryEventController,
);

// TODO: Convert these routes to the new API module structure
router.post("/hasura/create-payment-send-events", createPaymentSendEvents);
router.post("/hasura/create-reminder-event", createReminderEvent);
router.post("/hasura/create-expiry-event", createExpiryEvent);
router.post("/hasura/sanitise-application-data", sanitiseApplicationData);

export default router;

0 comments on commit 3665eef

Please sign in to comment.