Skip to content

Commit

Permalink
[MS-754] feat: Imporove logging. WIP logic for sending emails
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrgrundas committed Sep 27, 2024
1 parent 6ba5e8d commit b392f34
Show file tree
Hide file tree
Showing 17 changed files with 10,202 additions and 9,379 deletions.
18 changes: 2 additions & 16 deletions src/api/rest/saleor/webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ReceiveMessageCommand, SendMessageCommand } from "@aws-sdk/client-sqs";
import { type FastifyRequest } from "fastify";
import type { FastifyPluginAsync } from "fastify/types/plugin";
import rawBody from "fastify-raw-body";
import type { ZodTypeProvider } from "fastify-type-provider-zod";
Expand All @@ -10,7 +9,7 @@ import {
OrderUpdatedSubscriptionDocument,
} from "@/graphql/operations/subscriptions/generated";
import { type WebhookEventTypeAsyncEnum } from "@/graphql/schema";
import { getJSONFormatHeader } from "@/lib/saleor/apps/utils";
import { serializePayload } from "@/lib/emails/events/helpers";
import { verifyWebhookSignature } from "@/lib/saleor/auth";
import { saleorWebhookHeaders } from "@/lib/saleor/schema";
import { getJWKSProvider } from "@/providers/jwks";
Expand All @@ -32,20 +31,6 @@ export const EVENT_HANDLERS: {
},
];

export const serializePayload = ({
data,
event,
}: {
data: FastifyRequest["body"];
event: Lowercase<WebhookEventTypeAsyncEnum>;
}) => ({
format: getJSONFormatHeader({ name: CONFIG.NAME }),
payload: {
event,
data,
},
});

export const webhooks: FastifyPluginAsync = async (fastify) => {
await fastify.register(rawBody);

Expand All @@ -70,6 +55,7 @@ export const webhooks: FastifyPluginAsync = async (fastify) => {
fastify.log.info(
`Received webhook for '${request.headers["saleor-event"]}'.`
);
fastify.log.debug("Webhook payload:", { payload: request.body });

const payload = serializePayload({
data: request.body,
Expand Down
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const configSchema = z
SERVER_PORT: z.number().default(3000),
PROXY_PORT: z.number().default(3001),
STATIC_URL: z.string().url(),
FROM_EMAIL: z.string().email("[email protected]"),
FROM_NAME: z.string().default("Mirumee"),
})
.and(commonConfigSchema)
.and(appConfigSchema)
Expand Down
12 changes: 8 additions & 4 deletions src/emails-sender-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import http, { type IncomingMessage } from "http";

import { CONFIG } from "@/config";
import { handler } from "@/emails-sender";
import { handler, logger } from "@/emails-sender";
import { getJSONFormatHeader } from "@/lib/saleor/apps/utils";
import { logger } from "@/providers/logger";

/**
{
Expand Down Expand Up @@ -60,8 +59,13 @@ http
response.write("OK");
response.end();
})
.on("error", logger.error)
.on("error", (error) => {
logger.error("Proxy error.", { error });
})
.on("clientError", (error) => {
logger.error("Proxy client error.", { error });
})
.on("listening", () =>
logger.info(`Proxy is listening on port ${CONFIG.PROXY_PORT}`)
logger.info(`Proxy is listening on port ${CONFIG.PROXY_PORT}.`)
)
.listen({ port: CONFIG.PROXY_PORT, host: "0.0.0.0" });
110 changes: 92 additions & 18 deletions src/emails-sender.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,56 @@
// TODO: Mails sender serverless
import { type Context, type SQSBatchResponse, type SQSEvent } from "aws-lambda";
import {
type Context,
type SQSBatchResponse,
type SQSEvent,
type SQSRecord,
} from "aws-lambda";
import { type ComponentType } from "react";

import { CONFIG } from "@/config";
import { EmailParsePayloadError } from "@/lib/emails/errors";
import { getEmailProvider } from "@/providers/email";
import { logger } from "@/providers/logger";
import { getLogger } from "@/providers/logger";
import { OrderCreatedEmail } from "@/templates/OrderCreatedEmail";

import { OrderCreatedEmail } from "./templates/OrderCreatedEmail";
import { type WebhookEventTypeAsyncEnum } from "./graphql/schema";
import { type SerializedPayload } from "./lib/emails/events/helpers";
import { getJSONFormatHeader } from "./lib/saleor/apps/utils";

type OrderEvent = { order: { userEmail: string } };

const extractEmailFromOrder = (data: OrderEvent) => data.order.userEmail;

const TEMPLATES_MAP: {
[key in Lowercase<WebhookEventTypeAsyncEnum>]?: {
extractFn: (data: any) => string;
template: ComponentType<any>;
};
} = {
// order_created: {
// template: OrderCreatedEmail,
// extractFn: extractEmailFromOrder,
// },
order_updated: {
template: OrderCreatedEmail,
extractFn: extractEmailFromOrder,
},
};

export const logger = getLogger("emails-sender");

const parseRecord = (record: SQSRecord) => {
try {
// FIXME: Proxy events has invalid format? Test with real data & localstack.
const data = JSON.parse((record as any).Body);
return data as SerializedPayload;
} catch (error) {
logger.error("Failed to parse record payload.", { record, error });

throw new EmailParsePayloadError("Failed to parse record payload.", {
cause: { source: error as Error },
});
}
};

export const handler = async (event: SQSEvent, context: Context) => {
const failures: string[] = [];
Expand All @@ -16,23 +61,52 @@ export const handler = async (event: SQSEvent, context: Context) => {
/**
* Process event
*/
logger.info({ message: "Processing record", record });
logger.debug("Processing record", { record });

const sender = getEmailProvider({
fromEmail: `piotr.grundas+${CONFIG.NAME}@mirumee.com`,
from: CONFIG.RELEASE,
toEmail: "[email protected]",
});
const {
format,
payload: { data, event },
} = parseRecord(record);

const html = await sender.render({
props: {},
template: OrderCreatedEmail,
});
if (format === getJSONFormatHeader({ version: 1, name: CONFIG.NAME })) {
const match = TEMPLATES_MAP[event];

await sender.send({
html,
subject: "Order created",
});
if (!match) {
return logger.warn("Received payload with unhandled template.", {
format,
data,
event,
});
}

const { extractFn, template } = match;
// const toEmail = extractFn(data);
const toEmail = "[email protected]";
const fromEmail = CONFIG.FROM_EMAIL;
const from = CONFIG.FROM_NAME;

const sender = getEmailProvider({
fromEmail,
from,
toEmail,
});

const html = await sender.render({
props: data,
template,
});

await sender.send({
html,
subject: "Order created",
});
} else {
return logger.warn("Received payload with unsupported format.", {
format,
data,
event,
});
}
}

if (failures.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ subscription OrderCreatedSubscription {
... on OrderCreated {
order {
id
userEmail
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ subscription OrderUpdatedSubscription {
... on OrderUpdated {
order {
id
userEmail
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/graphql/operations/subscriptions/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import type * as Types from '../../schema';

import type { DocumentTypeDecoration } from '@graphql-typed-document-node/core';
export type OrderCreatedSubscription_event_OrderCreated_order_Order = { id: string };
export type OrderCreatedSubscription_event_OrderCreated_order_Order = { id: string, userEmail: string | null };

export type OrderCreatedSubscription_event_OrderCreated = { order: OrderCreatedSubscription_event_OrderCreated_order_Order | null };

Expand All @@ -17,7 +17,7 @@ export type OrderCreatedSubscriptionVariables = Types.Exact<{ [key: string]: nev

export type OrderCreatedSubscription = OrderCreatedSubscription_Subscription;

export type OrderUpdatedSubscription_event_OrderUpdated_order_Order = { id: string };
export type OrderUpdatedSubscription_event_OrderUpdated_order_Order = { id: string, userEmail: string | null };

export type OrderUpdatedSubscription_event_OrderUpdated = { order: OrderUpdatedSubscription_event_OrderUpdated_order_Order | null };

Expand Down Expand Up @@ -50,6 +50,7 @@ export const OrderCreatedSubscriptionDocument = new TypedDocumentString(`
... on OrderCreated {
order {
id
userEmail
}
}
}
Expand All @@ -61,6 +62,7 @@ export const OrderUpdatedSubscriptionDocument = new TypedDocumentString(`
... on OrderUpdated {
order {
id
userEmail
}
}
}
Expand Down
Loading

0 comments on commit b392f34

Please sign in to comment.