diff --git a/.changeset/rare-spiders-argue.md b/.changeset/rare-spiders-argue.md new file mode 100644 index 0000000..ea71b6f --- /dev/null +++ b/.changeset/rare-spiders-argue.md @@ -0,0 +1,5 @@ +--- +"saleor-app-emails-and-messages": patch +--- + +Added validation for Sendgrid events form. Enabling event without a template is no longer allowed to avoid misconfiguration and undelivered emails. diff --git a/apps/emails-and-messages/src/components/box-footer.tsx b/apps/emails-and-messages/src/components/box-footer.tsx index 22df536..0507615 100644 --- a/apps/emails-and-messages/src/components/box-footer.tsx +++ b/apps/emails-and-messages/src/components/box-footer.tsx @@ -12,6 +12,7 @@ export const BoxFooter = (props: BoxProps) => { gap={defaultPadding} flexDirection="row" justifyContent="flex-end" + alignItems="center" {...props} > {props.children} diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.test.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.test.ts new file mode 100644 index 0000000..a4c088e --- /dev/null +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.test.ts @@ -0,0 +1,58 @@ +import { expect, describe, it } from "vitest"; +import { sendgridUpdateEventArraySchema } from "./sendgrid-config-input-schema"; +import { ZodError } from "zod"; + +describe("sendgridUpdateEventArraySchema", async function () { + it("No errors should be thrown, when active event has specified template", async () => { + sendgridUpdateEventArraySchema.parse({ + configurationId: "123", + events: [ + { + eventType: "ORDER_CREATED", + active: true, + template: "123", + }, + ], + }); + }); + it("No errors should be thrown, when non active event has no template", async () => { + sendgridUpdateEventArraySchema.parse({ + configurationId: "123", + events: [ + { + eventType: "ORDER_CREATED", + active: false, + template: undefined, + }, + ], + }); + }); + + it("Error should be thrown, when any of active events has no template", async () => { + await expect(async () => + sendgridUpdateEventArraySchema.parse({ + configurationId: "123", + events: [ + { + eventType: "ORDER_CREATED", + active: true, + template: "123", + }, + { + eventType: "ORDER_FULFILLED", + active: true, + template: undefined, + }, + ], + }) + ).rejects.toThrow( + new ZodError([ + { + code: "custom", + message: "All active events must have assigned template.", + path: ["events"], + }, + ]) + ); + }); +}); diff --git a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts index de4a492..7386c11 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts +++ b/apps/emails-and-messages/src/modules/sendgrid/configuration/sendgrid-config-input-schema.ts @@ -89,7 +89,19 @@ export type SendgridUpdateEvent = z.infer; export const sendgridUpdateEventArraySchema = z.object({ configurationId: z.string(), - events: z.array(sendgridConfigurationEventSchema), + events: z + .array(sendgridConfigurationEventSchema) + /* + * Pass the validation if all the events are in one of two states: + * 1. Inactive + * 2. Active and have a template + */ + .refine( + (data) => data.every((event) => event.active === false || (event.active && event.template)), + { + message: "All active events must have assigned template.", + } + ), }); export type SendgridUpdateEventArray = z.infer; diff --git a/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx b/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx index e3a066f..7d8a741 100644 --- a/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx +++ b/apps/emails-and-messages/src/modules/sendgrid/ui/sendgrid-events-section.tsx @@ -34,7 +34,13 @@ export const SendgridEventsSection = ({ configuration }: SendgridEventsSectionPr messageEventTypesLabels[a.eventType].localeCompare(messageEventTypesLabels[b.eventType]) ); - const { control, register, handleSubmit, setError } = useForm({ + const { + control, + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ defaultValues: { configurationId: configuration.id, events: eventsSorted, @@ -132,6 +138,7 @@ export const SendgridEventsSection = ({ configuration }: SendgridEventsSectionPr + {errors.events && {errors.events.message}}