diff --git a/.changeset/eight-windows-turn.md b/.changeset/eight-windows-turn.md new file mode 100644 index 0000000..6380ba7 --- /dev/null +++ b/.changeset/eight-windows-turn.md @@ -0,0 +1,5 @@ +--- +"saleor-app-emails-and-messages": patch +--- + +Added support for the new event: fulfillment updated. diff --git a/apps/emails-and-messages/src/lib/notify-event-types.ts b/apps/emails-and-messages/src/lib/notify-event-types.ts new file mode 100644 index 0000000..06356e2 --- /dev/null +++ b/apps/emails-and-messages/src/lib/notify-event-types.ts @@ -0,0 +1,310 @@ +import { MessageEventTypes } from "../modules/event-handlers/message-event-types"; + +// Notify webhook event groups multiple event types under the one webhook. We need to map it to events recognized by the App +export const notifyEventMapping: Record = { + account_confirmation: "ACCOUNT_CONFIRMATION", + account_delete: "ACCOUNT_DELETE", + account_password_reset: "ACCOUNT_PASSWORD_RESET", + account_change_email_request: "ACCOUNT_CHANGE_EMAIL_REQUEST", + account_change_email_confirm: "ACCOUNT_CHANGE_EMAIL_CONFIRM", + order_fulfillment_update: "ORDER_FULFILLMENT_UPDATE", +}; + +interface IssuingPrincipal { + id?: string; + type?: string; +} + +interface Meta { + issued_at: string; + version: string; + issuing_principal: IssuingPrincipal; +} + +export type NotifySubscriptionPayload = { + meta: Meta; +} & ( + | { + notify_event: "account_confirmation"; + payload: NotifyPayloadAccountConfirmation; + } + | { + notify_event: "account_delete"; + payload: NotifyPayloadAccountDelete; + } + | { + notify_event: "account_password_reset"; + payload: NotifyPayloadAccountPasswordReset; + } + | { + notify_event: "account_change_email"; + payload: NotifyPayloadAccountChangeEmailRequest; + } + | { + notify_event: "account_change_email_confirm"; + payload: NotifyPayloadAccountChangeEmailConfirmation; + } + | { + notify_event: "order_fulfillment_update"; + payload: NotifyPayloadFulfillmentUpdate; + } +); + +export interface NotifyPayloadAccountConfirmation { + channel_slug: string; + confirm_url: string; + domain: string; + logo_url: string; + recipient_email: string; + site_name: string; + token: string; + user: User; +} + +export interface NotifyPayloadAccountDelete { + channel_slug: string; + delete_url: string; + domain: string; + logo_url: string; + recipient_email: string; + site_name: string; + token: string; + user: User; +} + +export interface NotifyPayloadAccountPasswordReset { + channel_slug: string; + domain: string; + logo_url: string; + recipient_email: string; + reset_url: string; + site_name: string; + token: string; + user: User; +} + +export interface NotifyPayloadAccountChangeEmailRequest { + channel_slug: string; + domain: string; + logo_url: string; + new_email: string; + old_email: string; + recipient_email: string; + reset_url: string; + site_name: string; + token: string; + user: User; +} + +export interface NotifyPayloadAccountChangeEmailConfirmation { + channel_slug: string; + domain: string; + logo_url: string; + recipient_email: string; + site_name: string; + token: string; + user: User; +} + +export interface NotifyPayloadFulfillmentUpdate { + channel_slug: string; + digital_lines: DigitalLine[]; + domain: string; + fulfillment: Fulfillment; + logo_url: string; + order: Order; + physical_lines: PhysicalLine[]; + recipient_email: string; + site_name: string; + token: string; +} + +interface User { + email: string; + first_name: string; + id: string; + is_active: boolean; + is_staff: boolean; + language_code: string; + last_name: string; + metadata: Metadata | null; + private_metadata: Metadata | null; +} + +type Metadata = Record; + +interface Order { + billing_address: Address; + channel_slug: string; + collection_point_name: string | null; + created: string; + currency: string; + discount_amount: number; + display_gross_prices: boolean; + email: string; + id: string; + language_code: string; + lines: Line[]; + metadata: Metadata | null; + number: number; + order_details_url: string; + private_metadata: Metadata | null; + shipping_address: Address; + shipping_method_name: string; + shipping_price_gross_amount: string; + shipping_price_net_amount: string; + status: string; + subtotal_gross_amount: string; + subtotal_net_amount: string; + tax_amount: string; + token: string; + total_gross_amount: string; + total_net_amount: string; + undiscounted_total_gross_amount: string; + undiscounted_total_net_amount: string; + voucher_discount: number | null; +} + +interface Line { + currency: string; + digital_url: string | null; + id: string; + is_digital: boolean; + is_shipping_required: boolean; + metadata: Metadata | null; + product_name: string; + product_sku: string; + product_variant_id: string; + product: Product; + quantity_fulfilled: number; + quantity: number; + tax_rate: string; + total_gross_amount: string; + total_net_amount: string; + total_tax_amount: string; + translated_product_name: string; + translated_variant_name: string; + unit_discount_amount: string; + unit_discount_reason: string | null; + unit_discount_type: string; + unit_discount_value: string; + unit_price_gross_amount: string; + unit_price_net_amount: string; + unit_tax_amount: string; + variant_name: string; + variant: Variant; +} + +interface Product { + attributes: AttributeWithAssignment[]; + first_image: Image; + id: string; + images: Image[]; + weight: string; +} + +interface Variant { + first_image: Image; + id: string; + images: Image[]; + is_preorder: boolean; + preorder_end_date: string | null; + weight: string; +} + +interface AttributeWithAssignment { + assignment: AttributeAssignment; + values: AttributeValue[]; +} + +interface AttributeAssignment { + attribute: Attribute; +} + +interface Attribute { + name: string; + slug: string; +} + +interface AttributeValue { + file_url: string | null; + name: string; + slug: string | null; + value: string; +} + +interface ImageSizeMapping { + "32": string; + "64": string; + "128": string; + "256": string; + "512": string; + "1024": string; + "2048": string; + "4096": string; +} + +interface Image { + original: ImageSizeMapping; +} + +interface Address { + first_name: string; + last_name: string; + company_name: string; + street_address_1: string; + street_address_2: string; + city: string; + city_area: string; + postal_code: string; + country: string; + country_area: string; + phone: string; +} + +interface Fulfillment { + tracking_number: string; + is_tracking_number_url: boolean; +} + +interface PhysicalLine { + id: string; + order_line: OrderLine; + quantity: number; +} + +interface DigitalLine { + id: string; + order_line: OrderLine; + quantity: number; +} + +interface OrderLine { + currency: string; + digital_url: string | null; + id: string; + is_digital: boolean; + is_shipping_required: boolean; + metadata: Metadata | null; + product_name: string; + product_sku: string; + product_variant_id: string; + product: Product; + quantity_fulfilled: number; + quantity: number; + tax_rate: string; + total_gross_amount: string; + total_net_amount: string; + total_tax_amount: string; + translated_product_name: string; + translated_variant_name: string; + unit_discount_amount: string; + unit_discount_reason: string | null; + unit_discount_type: string; + unit_discount_value: string; + unit_price_gross_amount: string; + unit_price_net_amount: string; + unit_tax_amount: string; + variant_name: string; + variant: Variant; +} diff --git a/apps/emails-and-messages/src/modules/event-handlers/default-payloads.ts b/apps/emails-and-messages/src/modules/event-handlers/default-payloads.ts index 596d287..6c54398 100644 --- a/apps/emails-and-messages/src/modules/event-handlers/default-payloads.ts +++ b/apps/emails-and-messages/src/modules/event-handlers/default-payloads.ts @@ -10,7 +10,13 @@ import { GiftCardSentWebhookPayloadFragment, OrderRefundedWebhookPayloadFragment, } from "../../../generated/graphql"; -import { NotifyEventPayload } from "../../pages/api/webhooks/notify"; +import { + NotifyPayloadAccountChangeEmailRequest, + NotifyPayloadAccountConfirmation, + NotifyPayloadAccountDelete, + NotifyPayloadAccountPasswordReset, + NotifyPayloadFulfillmentUpdate, +} from "../../lib/notify-event-types"; const exampleOrderPayload: OrderDetailsFragment = { id: "T3JkZXI6NTdiNTBhNDAtYzRmYi00YjQzLWIxODgtM2JhZmRlMTc3MGQ5", @@ -167,7 +173,7 @@ const invoiceSentPayload: InvoiceSentWebhookPayloadFragment = { order: exampleOrderPayload, }; -const accountConfirmationPayload: NotifyEventPayload = { +const accountConfirmationPayload: NotifyPayloadAccountConfirmation = { user: { id: "VXNlcjoxOTY=", email: "user@example.com", @@ -189,7 +195,7 @@ const accountConfirmationPayload: NotifyEventPayload = { logo_url: "", }; -const accountPasswordResetPayload: NotifyEventPayload = { +const accountPasswordResetPayload: NotifyPayloadAccountPasswordReset = { user: { id: "VXNlcjoxOTY=", email: "user@example.com", @@ -211,7 +217,7 @@ const accountPasswordResetPayload: NotifyEventPayload = { logo_url: "", }; -const accountChangeEmailRequestPayload: NotifyEventPayload = { +const accountChangeEmailRequestPayload: NotifyPayloadAccountChangeEmailRequest = { user: { id: "VXNlcjoxOTY=", email: "user@example.com", @@ -227,15 +233,15 @@ const accountChangeEmailRequestPayload: NotifyEventPayload = { token: "bmt4kc-d6e379b762697f6aa357527af36bb9f6", old_email: "test@example.com1", new_email: "new.email@example.com1", - redirect_url: - "http://example.com?email=user%40example.com&token=bmt4kc-d6e379b762697f6aa357527af36bb9f6", + reset_url: + "http://example.com/reset?email=user%40example.com&token=bmt4kc-d6e379b762697f6aa357527af36bb9f6", channel_slug: "default-channel", domain: "demo.saleor.cloud", site_name: "Saleor e-commerce", logo_url: "", }; -const accountChangeEmailConfirmPayload: NotifyEventPayload = { +const accountChangeEmailConfirmPayload: NotifyPayloadAccountChangeEmailRequest = { user: { id: "VXNlcjoxOTY=", email: "user@example.com", @@ -248,14 +254,18 @@ const accountChangeEmailConfirmPayload: NotifyEventPayload = { language_code: "en", }, recipient_email: "user@example.com", + old_email: "old@example.com", + new_email: "new@example.com", token: "bmt4kc-d6e379b762697f6aa357527af36bb9f6", + reset_url: + "http://example.com/reset?email=user%40example.com&token=bmt4kc-d6e379b762697f6aa357527af36bb9f6", channel_slug: "default-channel", domain: "demo.saleor.cloud", site_name: "Saleor e-commerce", logo_url: "", }; -const accountDeletePayload: NotifyEventPayload = { +const accountDeletePayload: NotifyPayloadAccountDelete = { user: { id: "VXNlcjoxOTY=", email: "user@example.com", @@ -277,6 +287,184 @@ const accountDeletePayload: NotifyEventPayload = { logo_url: "", }; +const orderLineMonospaceTeePayloadFragment: NotifyPayloadFulfillmentUpdate["order"]["lines"][0] = { + id: "T3JkZXJMaW5lOjIwMDg4MmMzLWU3NjItNGE0NS05ZjUxLTUyZDAxYTE2ODZjOQ==", + product: { + id: "UHJvZHVjdDoxMzQ=", + attributes: [ + { + assignment: { + attribute: { + slug: "material", + name: "Material", + }, + }, + values: [ + { + name: "Cotton", + value: "", + slug: "cotton", + file_url: null, + }, + ], + }, + ], + weight: "", + first_image: { + original: { + "32": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/32/", + "64": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/64/", + "128": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/128/", + "256": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/256/", + "512": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/512/", + "1024": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/1024/", + "2048": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/2048/", + "4096": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/4096/", + }, + }, + images: [ + { + original: { + "32": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/32/", + "64": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/64/", + "128": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/128/", + "256": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/256/", + "512": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/512/", + "1024": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/1024/", + "2048": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/2048/", + "4096": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE4/4096/", + }, + }, + ], + }, + product_name: "Monospace Tee", + translated_product_name: "Monospace Tee", + variant_name: "S", + variant: { + id: "UHJvZHVjdFZhcmlhbnQ6MzQ4", + weight: "", + is_preorder: false, + preorder_end_date: null, + first_image: { + original: { + "32": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/32/", + "64": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/64/", + "128": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/128/", + "256": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/256/", + "512": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/512/", + "1024": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/1024/", + "2048": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/2048/", + "4096": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/4096/", + }, + }, + images: [ + { + original: { + "32": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/32/", + "64": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/64/", + "128": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/128/", + "256": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/256/", + "512": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/512/", + "1024": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/1024/", + "2048": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/2048/", + "4096": "https://example.eu.saleor.cloud/thumbnail/UHJvZHVjdE1lZGlhOjE3/4096/", + }, + }, + ], + }, + translated_variant_name: "S", + product_sku: "328223580", + product_variant_id: "UHJvZHVjdFZhcmlhbnQ6MzQ4", + quantity: 1, + quantity_fulfilled: 1, + currency: "PLN", + unit_price_net_amount: "90.00", + unit_price_gross_amount: "90.00", + unit_tax_amount: "0.00", + total_gross_amount: "90.00", + total_net_amount: "90.00", + total_tax_amount: "0.00", + tax_rate: "0.0000", + is_shipping_required: true, + is_digital: false, + digital_url: null, + unit_discount_value: "0.000", + unit_discount_reason: null, + unit_discount_type: "fixed", + unit_discount_amount: "0.000", + metadata: {}, +}; + +const addressPayloadFragment: NotifyPayloadFulfillmentUpdate["order"]["billing_address"] = { + first_name: "Caitlin", + last_name: "Johnson", + company_name: "", + street_address_1: "8518 Pamela Track Apt. 164", + street_address_2: "", + city: "APRILSHIRE", + city_area: "", + postal_code: "28290", + country: "US", + country_area: "NC", + phone: "", +}; + +const orderPayloadFragment: NotifyPayloadFulfillmentUpdate["order"] = { + private_metadata: {}, + metadata: {}, + status: "fulfilled", + language_code: "en", + currency: "PLN", + total_net_amount: "468.68", + undiscounted_total_net_amount: "468.68", + total_gross_amount: "468.68", + undiscounted_total_gross_amount: "468.68", + display_gross_prices: true, + id: "T3JkZXI6MzU4YzcxNTktZmZlYy00ODI3LWI2MzYtYTNmYTEwMTA2MTI5", + token: "358c7159-ffec-4827-b636-a3fa10106129", + number: 231, + channel_slug: "channel-pln", + created: "2023-07-13 10:54:32.527314+00:00", + shipping_price_net_amount: "18.680", + shipping_price_gross_amount: "18.680", + order_details_url: "", + email: "caitlin.johnson@example.com", + subtotal_gross_amount: "450.00", + subtotal_net_amount: "450.00", + tax_amount: "0.00", + lines: [orderLineMonospaceTeePayloadFragment], + billing_address: addressPayloadFragment, + shipping_address: addressPayloadFragment, + shipping_method_name: "FedEx", + collection_point_name: null, + voucher_discount: null, + discount_amount: 0, +}; + +const fulfillmentPayloadFragment = { + is_tracking_number_url: false, + tracking_number: "1111-1111-1111-1111", +}; + +const fulfillmentUpdatePayload: NotifyPayloadFulfillmentUpdate = { + fulfillment: fulfillmentPayloadFragment, + order: orderPayloadFragment, + physical_lines: [ + { + id: "XXXXXXXX", + order_line: orderLineMonospaceTeePayloadFragment, + quantity: 1, + }, + ], + digital_lines: [], + recipient_email: "user@example.com", + token: "bmt4kc-d6e379b762697f6aa357527af36bb9f6", + channel_slug: "default-channel", + domain: "demo.saleor.cloud", + site_name: "Saleor e-commerce", + logo_url: "", +}; + // TODO: UPDATE WITH BETTER DATA const giftCardSentPayload: GiftCardSentWebhookPayloadFragment = { channel: "default_channel", @@ -329,5 +517,6 @@ export const examplePayloads: Record = { ORDER_CREATED: orderCreatedPayload, ORDER_FULFILLED: orderFulfilledPayload, ORDER_FULLY_PAID: orderFullyPaidPayload, + ORDER_FULFILLMENT_UPDATE: fulfillmentUpdatePayload, ORDER_REFUNDED: orderRefundedPayload, }; diff --git a/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts b/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts index 00ab398..6b1bb08 100644 --- a/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts +++ b/apps/emails-and-messages/src/modules/event-handlers/message-event-types.ts @@ -10,6 +10,7 @@ export const messageEventTypes = [ "ORDER_CONFIRMED", "ORDER_CREATED", "ORDER_FULFILLED", + "ORDER_FULFILLMENT_UPDATE", "ORDER_FULLY_PAID", "ORDER_REFUNDED", ] as const; @@ -28,6 +29,7 @@ export const messageEventTypesLabels: Record = { ORDER_CONFIRMED: "Order confirmed", ORDER_CREATED: "Order created", ORDER_FULFILLED: "Order fulfilled", + ORDER_FULFILLMENT_UPDATE: "Order fulfillment updated", ORDER_FULLY_PAID: "Order fully paid", ORDER_REFUNDED: "Order refunded", }; diff --git a/apps/emails-and-messages/src/modules/smtp/default-templates.ts b/apps/emails-and-messages/src/modules/smtp/default-templates.ts index 1d2cb69..70c3c74 100644 --- a/apps/emails-and-messages/src/modules/smtp/default-templates.ts +++ b/apps/emails-and-messages/src/modules/smtp/default-templates.ts @@ -36,6 +36,42 @@ const addressSection = ` `; +const addressSectionForNotify = ` + + + + + + Billing address + + + Shipping address + + + + + + + {{#if order.billing_address}} + {{ order.billing_address.street_address_1 }} + {{else}} + No billing address + {{/if}} + + + {{#if order.shipping_address}} + {{ order.shipping_address.street_address_1}} + {{else}} + No shipping required + {{/if}} + + + + + + +`; + const orderLinesSection = ` @@ -95,7 +131,7 @@ const defaultOrderFulfilledMjmlTemplate = ` Hello! - Order {{ order.number}} has been fulfilled. + Order {{ order.number }} has been fulfilled. @@ -129,7 +165,7 @@ const defaultOrderFullyPaidMjmlTemplate = ` Hello! - Order {{ order.number}} has been fully paid. + Order {{ order.number }} has been fully paid. @@ -146,7 +182,7 @@ const defaultOrderRefundedMjmlTemplate = ` Hello! - Order {{ order.number}} has been refunded. + Order {{ order.number }} has been refunded. @@ -163,7 +199,7 @@ const defaultOrderCancelledMjmlTemplate = ` Hello! - Order {{ order.number}} has been cancelled. + Order {{ order.number }} has been cancelled. @@ -290,6 +326,27 @@ const defaultAccountDeleteMjmlTemplate = ` `; +const defaultOrderFulfillmentUpdatedMjmlTemplate = ` + + + + + Hello! + + + Fulfillment for the order {{ order.number }} has been updated. + + {{#if fulfillment.tracking_number }} + + Tracking number: {{ fulfillment.tracking_number }} + + {{/if}} + + + ${addressSectionForNotify} + +`; + export const defaultMjmlTemplates: Record = { ACCOUNT_CHANGE_EMAIL_CONFIRM: defaultAccountChangeEmailConfirmationMjmlTemplate, ACCOUNT_CHANGE_EMAIL_REQUEST: defaultAccountChangeEmailRequestMjmlTemplate, @@ -302,6 +359,7 @@ export const defaultMjmlTemplates: Record = { ORDER_CONFIRMED: defaultOrderConfirmedMjmlTemplate, ORDER_CREATED: defaultOrderCreatedMjmlTemplate, ORDER_FULFILLED: defaultOrderFulfilledMjmlTemplate, + ORDER_FULFILLMENT_UPDATE: defaultOrderFulfillmentUpdatedMjmlTemplate, ORDER_FULLY_PAID: defaultOrderFullyPaidMjmlTemplate, ORDER_REFUNDED: defaultOrderRefundedMjmlTemplate, }; @@ -318,6 +376,7 @@ export const defaultMjmlSubjectTemplates: Record = { ORDER_CONFIRMED: "Order {{ order.number }} has been confirmed", ORDER_CREATED: "Order {{ order.number }} has been created", ORDER_FULFILLED: "Order {{ order.number }} has been fulfilled", + ORDER_FULFILLMENT_UPDATE: "Fulfillment for order {{ order.number }} has been updated", ORDER_FULLY_PAID: "Order {{ order.number }} has been fully paid", ORDER_REFUNDED: "Order {{ order.number }} has been refunded", }; diff --git a/apps/emails-and-messages/src/modules/webhook-management/webhook-management-service.ts b/apps/emails-and-messages/src/modules/webhook-management/webhook-management-service.ts index fb7d5ca..eb1988e 100644 --- a/apps/emails-and-messages/src/modules/webhook-management/webhook-management-service.ts +++ b/apps/emails-and-messages/src/modules/webhook-management/webhook-management-service.ts @@ -42,6 +42,7 @@ export const eventToWebhookMapping: Record = { ORDER_FULFILLED: "orderFulfilledWebhook", ORDER_FULLY_PAID: "orderFullyPaidWebhook", ORDER_REFUNDED: "orderRefundedWebhook", + ORDER_FULFILLMENT_UPDATE: "notifyWebhook", }; const logger = createLogger({ diff --git a/apps/emails-and-messages/src/modules/webhook-management/webhook-status-dict.ts b/apps/emails-and-messages/src/modules/webhook-management/webhook-status-dict.ts index 93f6a75..fec1614 100644 --- a/apps/emails-and-messages/src/modules/webhook-management/webhook-status-dict.ts +++ b/apps/emails-and-messages/src/modules/webhook-management/webhook-status-dict.ts @@ -1,4 +1,4 @@ -import { AppWebhook, AppWebhooks } from "./webhook-management-service"; +import { AppWebhook } from "./webhook-management-service"; export type WebhookStatuses = Record; diff --git a/apps/emails-and-messages/src/pages/api/webhooks/notify.ts b/apps/emails-and-messages/src/pages/api/webhooks/notify.ts index 76046f1..b189c27 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/notify.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/notify.ts @@ -2,67 +2,13 @@ import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handl import { saleorApp } from "../../../saleor-app"; import { createLogger, createGraphQLClient } from "@saleor/apps-shared"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; -import { MessageEventTypes } from "../../../modules/event-handlers/message-event-types"; - -// Notify webhook event groups multiple event types under the one webhook. We need to map it to events recognized by the App -const notifyEventMapping: Record = { - account_confirmation: "ACCOUNT_CONFIRMATION", - account_delete: "ACCOUNT_DELETE", - account_password_reset: "ACCOUNT_PASSWORD_RESET", - account_change_email_request: "ACCOUNT_CHANGE_EMAIL_REQUEST", - account_change_email_confirm: "ACCOUNT_CHANGE_EMAIL_CONFIRM", -}; +import { NotifySubscriptionPayload, notifyEventMapping } from "../../../lib/notify-event-types"; /* - * Notify event handles multiple event types which are recognized based on payload field `notify_event`. - * Handler recognizes if event is one of the supported typed and sends appropriate message. + * The Notify webhook is triggered on multiple Saleor events. + * Type of the message is determined by `notify_event` field in the payload. */ -interface NotifySubscriptionPayload { - notify_event: string; - payload: NotifyEventPayload; - meta: Meta; -} - -interface Meta { - issued_at: Date; - version: string; - issuing_principal: IssuingPrincipal; -} - -interface IssuingPrincipal { - id: null | string; - type: null | string; -} - -export interface NotifyEventPayload { - user: User; - recipient_email: string; - channel_slug: string; - domain: string; - site_name: string; - logo_url: string; - token?: string; - confirm_url?: string; - reset_url?: string; - delete_url?: string; - old_email?: string; - new_email?: string; - redirect_url?: string; -} - -interface User { - id: string; - email: string; - first_name: string; - last_name: string; - is_staff: boolean; - is_active: boolean; - private_metadata: Record; - metadata: Record; - language_code: string; -} - export const notifyWebhook = new SaleorAsyncWebhook({ name: "notify", webhookPath: "api/webhooks/notify", @@ -89,10 +35,10 @@ const handler: NextWebhookApiHandler = async (req, re .json({ error: "Email recipient has not been specified in the event payload." }); } + // Since NOTIFY can be send on events unrelated to this app, lack of mapping means the App does not support it const event = notifyEventMapping[payload.notify_event]; if (!event) { - // NOTIFY webhook sends multiple events to the same endpoint. The app supports only a subset of them. logger.debug(`The type of received notify event (${payload.notify_event}) is not supported.`); return res.status(200).json({ message: `${payload.notify_event} event is not supported.` }); }