Skip to content

Commit

Permalink
Apply use case error handling to webhook handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
lkostrowski committed May 17, 2024
1 parent e76eea8 commit ce64d3e
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 71 deletions.
51 changes: 43 additions & 8 deletions apps/smtp/src/pages/api/webhooks/gift-card-sent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { GiftCardSentWebhookPayloadFragment } from "../../../../generated/graphq
import { withOtel } from "@saleor/apps-otel";
import { createLogger } from "../../../logger";
import { SendEventMessagesUseCaseFactory } from "../../../modules/event-handlers/use-case/send-event-messages.use-case.factory";
import { SendEventMessagesUseCase } from "../../../modules/event-handlers/use-case/send-event-messages.use-case";
import { captureException } from "@sentry/nextjs";

const GiftCardSentWebhookPayload = gql`
fragment GiftCardSentWebhookPayload on GiftCardSent {
Expand Down Expand Up @@ -103,14 +105,47 @@ const handler: NextWebhookApiHandler<GiftCardSentWebhookPayloadFragment> = async

const useCase = useCaseFactory.createFromAuthData(authData);

await useCase.sendEventMessages({
channelSlug: channel,
event: "GIFT_CARD_SENT",
payload,
recipientEmail,
});

return res.status(200).json({ message: "The event has been handled" });
return useCase
.sendEventMessages({
channelSlug: channel,
event: "GIFT_CARD_SENT",
payload,
recipientEmail,
})
.then((result) =>
result.match(
(r) => {
logger.info("Successfully sent email(s)");

return res.status(200).json({ message: "The event has been handled" });
},
(err) => {
switch (err[0].constructor) {
case SendEventMessagesUseCase.ServerError: {
logger.error("Failed to send email(s) [server error]", { error: err });

return res.status(500).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.ClientError: {
logger.info("Failed to send email(s) [client error]", { error: err });

return res.status(400).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.NoOpError: {
logger.error("Sending emails aborted [no op]", { error: err });

return res.status(200).json({ message: "The event has been handled [no op]" });
}
default: {
logger.error("Failed to send email(s) [server error]", { error: err });
captureException(new Error("Unhandled useCase error", { cause: err }));

return res.status(500).json({ message: "Failed to send email [unhandled]" });
}
}
},
),
);
};

export default withOtel(giftCardSentWebhook.createHandler(handler), "/api/webhooks/gift-card-sent");
Expand Down
51 changes: 43 additions & 8 deletions apps/smtp/src/pages/api/webhooks/invoice-sent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { withOtel } from "@saleor/apps-otel";
import { createLogger } from "../../../logger";
import { SendEventMessagesUseCaseFactory } from "../../../modules/event-handlers/use-case/send-event-messages.use-case.factory";
import { SendEventMessagesUseCase } from "../../../modules/event-handlers/use-case/send-event-messages.use-case";
import { captureException } from "@sentry/nextjs";

const InvoiceSentWebhookPayload = gql`
${OrderDetailsFragmentDoc}
Expand Down Expand Up @@ -84,14 +86,47 @@ const handler: NextWebhookApiHandler<InvoiceSentWebhookPayloadFragment> = async

const useCase = useCaseFactory.createFromAuthData(authData);

await useCase.sendEventMessages({
channelSlug: channel,
event: "INVOICE_SENT",
payload: { order: payload.order },
recipientEmail,
});

return res.status(200).json({ message: "The event has been handled" });
return useCase
.sendEventMessages({
channelSlug: channel,
event: "INVOICE_SENT",
payload: { order: payload.order },
recipientEmail,
})
.then((result) =>
result.match(
(r) => {
logger.info("Successfully sent email(s)");

return res.status(200).json({ message: "The event has been handled" });
},
(err) => {
switch (err[0].constructor) {
case SendEventMessagesUseCase.ServerError: {
logger.error("Failed to send email(s) [server error]", { error: err });

return res.status(500).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.ClientError: {
logger.info("Failed to send email(s) [client error]", { error: err });

return res.status(400).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.NoOpError: {
logger.error("Sending emails aborted [no op]", { error: err });

return res.status(200).json({ message: "The event has been handled [no op]" });
}
default: {
logger.error("Failed to send email(s) [server error]", { error: err });
captureException(new Error("Unhandled useCase error", { cause: err }));

return res.status(500).json({ message: "Failed to send email [unhandled]" });
}
}
},
),
);
};

export default withOtel(invoiceSentWebhook.createHandler(handler), "api/webhooks/invoice-sent");
Expand Down
49 changes: 42 additions & 7 deletions apps/smtp/src/pages/api/webhooks/notify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { notifyEventMapping, NotifySubscriptionPayload } from "../../../lib/noti
import { withOtel } from "@saleor/apps-otel";
import { createLogger } from "../../../logger";
import { SendEventMessagesUseCaseFactory } from "../../../modules/event-handlers/use-case/send-event-messages.use-case.factory";
import { SendEventMessagesUseCase } from "../../../modules/event-handlers/use-case/send-event-messages.use-case";
import { captureException } from "@sentry/nextjs";

/*
* The Notify webhook is triggered on multiple Saleor events.
Expand Down Expand Up @@ -48,14 +50,47 @@ const handler: NextWebhookApiHandler<NotifySubscriptionPayload> = async (req, re

const useCase = useCaseFactory.createFromAuthData(authData);

await useCase.sendEventMessages({
channelSlug: channel,
event,
payload: payload.payload,
recipientEmail,
});
return useCase
.sendEventMessages({
channelSlug: channel,
event,
payload: payload.payload,
recipientEmail,
})
.then((result) =>
result.match(
(r) => {
logger.info("Successfully sent email(s)");

return res.status(200).json({ message: "The event has been handled" });
return res.status(200).json({ message: "The event has been handled" });
},
(err) => {
switch (err[0].constructor) {
case SendEventMessagesUseCase.ServerError: {
logger.error("Failed to send email(s) [server error]", { error: err });

return res.status(500).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.ClientError: {
logger.info("Failed to send email(s) [client error]", { error: err });

return res.status(400).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.NoOpError: {
logger.error("Sending emails aborted [no op]", { error: err });

return res.status(200).json({ message: "The event has been handled [no op]" });
}
default: {
logger.error("Failed to send email(s) [server error]", { error: err });
captureException(new Error("Unhandled useCase error", { cause: err }));

return res.status(500).json({ message: "Failed to send email [unhandled]" });
}
}
},
),
);
};

export default withOtel(notifyWebhook.createHandler(handler), "api/webhooks/notify");
Expand Down
51 changes: 43 additions & 8 deletions apps/smtp/src/pages/api/webhooks/order-cancelled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { withOtel } from "@saleor/apps-otel";
import { createLogger } from "../../../logger";
import { SendEventMessagesUseCaseFactory } from "../../../modules/event-handlers/use-case/send-event-messages.use-case.factory";
import { SendEventMessagesUseCase } from "../../../modules/event-handlers/use-case/send-event-messages.use-case";
import { captureException } from "@sentry/nextjs";

const OrderCancelledWebhookPayload = gql`
${OrderDetailsFragmentDoc}
Expand Down Expand Up @@ -67,14 +69,47 @@ const handler: NextWebhookApiHandler<OrderCancelledWebhookPayloadFragment> = asy

const useCase = useCaseFactory.createFromAuthData(authData);

await useCase.sendEventMessages({
channelSlug: channel,
event: "ORDER_CANCELLED",
payload: { order: payload.order },
recipientEmail,
});

return res.status(200).json({ message: "The event has been handled" });
return useCase
.sendEventMessages({
channelSlug: channel,
event: "ORDER_CANCELLED",
payload: { order: payload.order },
recipientEmail,
})
.then((result) =>
result.match(
(r) => {
logger.info("Successfully sent email(s)");

return res.status(200).json({ message: "The event has been handled" });
},
(err) => {
switch (err[0].constructor) {
case SendEventMessagesUseCase.ServerError: {
logger.error("Failed to send email(s) [server error]", { error: err });

return res.status(500).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.ClientError: {
logger.info("Failed to send email(s) [client error]", { error: err });

return res.status(400).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.NoOpError: {
logger.error("Sending emails aborted [no op]", { error: err });

return res.status(200).json({ message: "The event has been handled [no op]" });
}
default: {
logger.error("Failed to send email(s) [server error]", { error: err });
captureException(new Error("Unhandled useCase error", { cause: err }));

return res.status(500).json({ message: "Failed to send email [unhandled]" });
}
}
},
),
);
};

export default withOtel(
Expand Down
51 changes: 43 additions & 8 deletions apps/smtp/src/pages/api/webhooks/order-confirmed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { withOtel } from "@saleor/apps-otel";
import { createLogger } from "../../../logger";
import { SendEventMessagesUseCaseFactory } from "../../../modules/event-handlers/use-case/send-event-messages.use-case.factory";
import { SendEventMessagesUseCase } from "../../../modules/event-handlers/use-case/send-event-messages.use-case";
import { captureException } from "@sentry/nextjs";

const OrderConfirmedWebhookPayload = gql`
${OrderDetailsFragmentDoc}
Expand Down Expand Up @@ -68,14 +70,47 @@ const handler: NextWebhookApiHandler<OrderConfirmedWebhookPayloadFragment> = asy

const useCase = useCaseFactory.createFromAuthData(authData);

await useCase.sendEventMessages({
channelSlug: channel,
event: "ORDER_CONFIRMED",
payload: { order: payload.order },
recipientEmail,
});

return res.status(200).json({ message: "The event has been handled" });
return useCase
.sendEventMessages({
channelSlug: channel,
event: "ORDER_CONFIRMED",
payload: { order: payload.order },
recipientEmail,
})
.then((result) =>
result.match(
(r) => {
logger.info("Successfully sent email(s)");

return res.status(200).json({ message: "The event has been handled" });
},
(err) => {
switch (err[0].constructor) {
case SendEventMessagesUseCase.ServerError: {
logger.error("Failed to send email(s) [server error]", { error: err });

return res.status(500).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.ClientError: {
logger.info("Failed to send email(s) [client error]", { error: err });

return res.status(400).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.NoOpError: {
logger.error("Sending emails aborted [no op]", { error: err });

return res.status(200).json({ message: "The event has been handled [no op]" });
}
default: {
logger.error("Failed to send email(s) [server error]", { error: err });
captureException(new Error("Unhandled useCase error", { cause: err }));

return res.status(500).json({ message: "Failed to send email [unhandled]" });
}
}
},
),
);
};

export default withOtel(
Expand Down
51 changes: 43 additions & 8 deletions apps/smtp/src/pages/api/webhooks/order-created.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { OrderCreatedWebhookPayloadFragment } from "../../../../generated/graphq
import { withOtel } from "@saleor/apps-otel";
import { createLogger } from "../../../logger";
import { SendEventMessagesUseCaseFactory } from "../../../modules/event-handlers/use-case/send-event-messages.use-case.factory";
import { SendEventMessagesUseCase } from "../../../modules/event-handlers/use-case/send-event-messages.use-case";
import { captureException } from "@sentry/nextjs";

const OrderCreatedWebhookPayload = gql`
${OrderDetailsFragmentDoc}
Expand Down Expand Up @@ -65,14 +67,47 @@ const handler: NextWebhookApiHandler<OrderCreatedWebhookPayloadFragment> = async

const useCase = useCaseFactory.createFromAuthData(authData);

await useCase.sendEventMessages({
channelSlug: channel,
event: "ORDER_CREATED",
payload: { order: payload.order },
recipientEmail,
});

return res.status(200).json({ message: "The event has been handled" });
return useCase
.sendEventMessages({
channelSlug: channel,
event: "ORDER_CREATED",
payload: { order: payload.order },
recipientEmail,
})
.then((result) =>
result.match(
(r) => {
logger.info("Successfully sent email(s)");

return res.status(200).json({ message: "The event has been handled" });
},
(err) => {
switch (err[0].constructor) {
case SendEventMessagesUseCase.ServerError: {
logger.error("Failed to send email(s) [server error]", { error: err });

return res.status(500).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.ClientError: {
logger.info("Failed to send email(s) [client error]", { error: err });

return res.status(400).json({ message: "Failed to send email" });
}
case SendEventMessagesUseCase.NoOpError: {
logger.error("Sending emails aborted [no op]", { error: err });

return res.status(200).json({ message: "The event has been handled [no op]" });
}
default: {
logger.error("Failed to send email(s) [server error]", { error: err });
captureException(new Error("Unhandled useCase error", { cause: err }));

return res.status(500).json({ message: "Failed to send email [unhandled]" });
}
}
},
),
);
};

export default withOtel(orderCreatedWebhook.createHandler(handler), "api/webhooks/order-created");
Expand Down
Loading

0 comments on commit ce64d3e

Please sign in to comment.