From eb9bd700ca2ff891afe2d75c51f497fc9671bb27 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolski Date: Tue, 7 Mar 2023 22:02:37 +0100 Subject: [PATCH] Use configuration service --- ...phq-client.ts => create-graphql-client.ts} | 0 .../app-config-input-schema.ts | 9 + .../app-configuration.router.ts | 55 +++-- .../get-app-configuration.service.ts | 110 ++++++---- .../ui/app-configuration-form.tsx | 2 +- .../ui/channels-configuration-tab.tsx | 196 +++++++++--------- .../src/modules/channels/channels.router.ts | 7 +- .../event-handlers/send-event-messages.ts | 45 ++-- .../get-mjml-configuration.service.ts | 121 ++++++++--- .../configuration/mjml-config-container.ts | 8 +- .../mjml-configuration.router.ts | 133 ++++-------- .../ui/mjml-configuration-tab.tsx | 32 +-- .../modules/mjml/get-active-mjml-settings.ts | 43 ---- .../src/modules/mjml/send-mjml.ts | 36 +--- .../trpc/protected-client-procedure.ts | 2 +- .../src/modules/ui/code-editor.tsx | 1 - .../src/pages/api/webhooks/invoice-sent.ts | 5 + .../src/pages/api/webhooks/order-cancelled.ts | 5 + .../src/pages/api/webhooks/order-confirmed.ts | 5 + .../src/pages/api/webhooks/order-created.ts | 7 +- .../src/pages/api/webhooks/order-fulfilled.ts | 5 + .../pages/api/webhooks/order-fully-paid.ts | 5 + .../[configurationId]/event/[eventType].tsx | 4 +- 23 files changed, 446 insertions(+), 390 deletions(-) rename apps/emails-and-messages/src/lib/{create-graphq-client.ts => create-graphql-client.ts} (100%) delete mode 100644 apps/emails-and-messages/src/modules/mjml/get-active-mjml-settings.ts diff --git a/apps/emails-and-messages/src/lib/create-graphq-client.ts b/apps/emails-and-messages/src/lib/create-graphql-client.ts similarity index 100% rename from apps/emails-and-messages/src/lib/create-graphq-client.ts rename to apps/emails-and-messages/src/lib/create-graphql-client.ts diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts b/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts index 4a242b6..f0b2227 100644 --- a/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts +++ b/apps/emails-and-messages/src/modules/app-configuration/app-config-input-schema.ts @@ -9,3 +9,12 @@ export const appConfigInputSchema = z.object({ }) ), }); + +export const appChannelConfigurationInputSchema = z.object({ + channel: z.string(), + configuration: z.object({ + active: z.boolean(), + mjmlConfigurationId: z.string().optional(), + sendgridConfigurationId: z.string().optional(), + }), +}); diff --git a/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts b/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts index 88e362c..fed3f26 100644 --- a/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts +++ b/apps/emails-and-messages/src/modules/app-configuration/app-configuration.router.ts @@ -1,23 +1,57 @@ -import { PrivateMetadataAppConfigurator } from "./app-configurator"; -import { createSettingsManager } from "./metadata-manager"; import { logger as pinoLogger } from "../../lib/logger"; -import { appConfigInputSchema } from "./app-config-input-schema"; -import { GetAppConfigurationService } from "./get-app-configuration.service"; +import { + appChannelConfigurationInputSchema, + appConfigInputSchema, +} from "./app-config-input-schema"; +import { AppConfigurationService } from "./get-app-configuration.service"; import { router } from "../trpc/trpc-server"; import { protectedClientProcedure } from "../trpc/protected-client-procedure"; +import { z } from "zod"; + +// Allow access only for the dashboard users and attaches the +// configuration service to the context +const protectedWithConfigurationService = protectedClientProcedure.use(({ next, ctx }) => + next({ + ctx: { + ...ctx, + configurationService: new AppConfigurationService({ + apiClient: ctx.apiClient, + saleorApiUrl: ctx.saleorApiUrl, + }), + }, + }) +); export const appConfigurationRouter = router({ - fetch: protectedClientProcedure.query(async ({ ctx, input }) => { + getChannelConfiguration: protectedWithConfigurationService + .input(z.object({ channelSlug: z.string() })) + .query(async ({ ctx, input }) => { + const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); + logger.debug("Get Channel Configuration called"); + + return await ctx.configurationService.getChannelConfiguration(input.channelSlug); + }), + + setChannelConfiguration: protectedWithConfigurationService + .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) + .input(appChannelConfigurationInputSchema) + .mutation(async ({ ctx, input }) => { + const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); + logger.debug("Set channel configuration called"); + + await ctx.configurationService.setChannelConfiguration(input); + }), + fetch: protectedWithConfigurationService.query(async ({ ctx, input }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); logger.debug("appConfigurationRouter.fetch called"); - return new GetAppConfigurationService({ + return new AppConfigurationService({ apiClient: ctx.apiClient, saleorApiUrl: ctx.saleorApiUrl, }).getConfiguration(); }), - setAndReplace: protectedClientProcedure + setAndReplace: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(appConfigInputSchema) .mutation(async ({ ctx, input }) => { @@ -25,12 +59,7 @@ export const appConfigurationRouter = router({ logger.debug(input, "appConfigurationRouter.setAndReplace called with input"); - const appConfigurator = new PrivateMetadataAppConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - await appConfigurator.setConfig(input); + await ctx.configurationService.setConfigurationRoot(input); return null; }), diff --git a/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts b/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts index 08a69b9..70ed61f 100644 --- a/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts +++ b/apps/emails-and-messages/src/modules/app-configuration/get-app-configuration.service.ts @@ -1,65 +1,91 @@ import { PrivateMetadataAppConfigurator } from "./app-configurator"; import { createSettingsManager } from "./metadata-manager"; -import { ChannelsFetcher } from "../channels/channels-fetcher"; -import { ShopInfoFetcher } from "../shop-info/shop-info-fetcher"; -import { FallbackAppConfig } from "./fallback-app-config"; import { Client } from "urql"; import { logger as pinoLogger } from "../../lib/logger"; +import { AppConfig, AppConfigurationPerChannel } from "./app-config"; +import { getDefaultEmptyAppConfiguration } from "./app-config-container"; -// todo test -export class GetAppConfigurationService { - constructor( - private settings: { - apiClient: Client; - saleorApiUrl: string; - } - ) {} +const logger = pinoLogger.child({ + service: "AppConfigurationService", +}); + +export class AppConfigurationService { + private configurationData?: AppConfig; + private metadataConfigurator: PrivateMetadataAppConfigurator; + + constructor(args: { apiClient: Client; saleorApiUrl: string; initialData?: AppConfig }) { + this.metadataConfigurator = new PrivateMetadataAppConfigurator( + createSettingsManager(args.apiClient), + args.saleorApiUrl + ); + } + + // Fetch configuration from Saleor API and cache it + private async pullConfiguration() { + logger.debug("Fetch configuration from Saleor API"); + + const config = await this.metadataConfigurator.getConfig(); + this.configurationData = config; + } + + // Push configuration to Saleor API + private async pushConfiguration() { + logger.debug("Push configuration to Saleor API"); + + await this.metadataConfigurator.setConfig(this.configurationData!); + } async getConfiguration() { - const logger = pinoLogger.child({ - service: "GetAppConfigurationService", - saleorApiUrl: this.settings.saleorApiUrl, - }); + logger.debug("Get configuration"); - const { saleorApiUrl, apiClient } = this.settings; + if (!this.configurationData) { + logger.debug("No configuration found in cache. Will fetch it from Saleor API"); + await this.pullConfiguration(); + } - const appConfigurator = new PrivateMetadataAppConfigurator( - createSettingsManager(apiClient), - saleorApiUrl - ); - - const savedAppConfig = (await appConfigurator.getConfig()) ?? null; + const savedAppConfig = this.configurationData ?? null; logger.debug(savedAppConfig, "Retrieved app config from Metadata. Will return it"); if (savedAppConfig) { return savedAppConfig; } + } - logger.info("App config not found in metadata. Will create default config now."); + // Saves configuration to Saleor API and cache it + async setConfigurationRoot(config: AppConfig) { + logger.debug("Set configuration"); - const channelsFetcher = new ChannelsFetcher(apiClient); - const shopInfoFetcher = new ShopInfoFetcher(apiClient); + this.configurationData = config; + await this.pushConfiguration(); + } - const [channels, shopAppConfiguration] = await Promise.all([ - channelsFetcher.fetchChannels(), - shopInfoFetcher.fetchShopInfo(), - ]); + // Returns channel configuration if existing. Otherwise returns default empty one + async getChannelConfiguration(channel: string) { + logger.debug("Get channel configuration"); + const configurations = await this.getConfiguration(); + if (!configurations) { + return getDefaultEmptyAppConfiguration(); + } - logger.debug(channels, "Fetched channels"); - logger.debug(shopAppConfiguration, "Fetched shop app configuration"); + const channelConfiguration = configurations.configurationsPerChannel[channel]; + return channelConfiguration || getDefaultEmptyAppConfiguration(); + } - const appConfig = FallbackAppConfig.createFallbackConfigFromExistingShopAndChannels( - channels ?? [], - shopAppConfiguration - ); + async setChannelConfiguration({ + channel, + configuration, + }: { + channel: string; + configuration: AppConfigurationPerChannel; + }) { + logger.debug("Set channel configuration"); + let configurations = await this.getConfiguration(); + if (!configurations) { + configurations = { configurationsPerChannel: {} }; + } - logger.debug(appConfig, "Created a fallback AppConfig. Will save it."); - - await appConfigurator.setConfig(appConfig); - - logger.info("Saved initial AppConfig"); - - return appConfig; + configurations.configurationsPerChannel[channel] = configuration; + await this.setConfigurationRoot(configurations); } } diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx index cee7add..5708779 100644 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx +++ b/apps/emails-and-messages/src/modules/app-configuration/ui/app-configuration-form.tsx @@ -83,7 +83,7 @@ export const AppConfigurationForm = (props: AppConfigurationFormProps) => { onChange(button.value)} - activeTab={value.toString()} + activeTab={value?.toString() || "false"} key={button.label} > {button.label} diff --git a/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx b/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx index 2b7e6da..58aab5c 100644 --- a/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx +++ b/apps/emails-and-messages/src/modules/app-configuration/ui/channels-configuration-tab.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { EditIcon, IconButton, makeStyles } from "@saleor/macaw-ui"; -import { AppConfigContainer } from "../app-config-container"; import { AppConfigurationForm } from "./app-configuration-form"; import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; import { AppColumnsLayout } from "../../ui/app-columns-layout"; @@ -28,96 +27,88 @@ const useStyles = makeStyles((theme) => { export const ChannelsConfigurationTab = () => { const styles = useStyles(); const { appBridge } = useAppBridge(); - const [mjmlConfigurationsListData, setMjmlConfigurationsListData] = useState< - { label: string; value: string }[] - >([]); - - const [sendgridConfigurationsListData, setSendgridConfigurationsListData] = useState< - { label: string; value: string }[] - >([]); - - const { data: configurationData, refetch: refetchConfig } = - trpcClient.appConfiguration.fetch.useQuery(); - - trpcClient.mjmlConfiguration.getConfigurations.useQuery( - {}, - { - onSuccess(data) { - setMjmlConfigurationsListData( - data.map((configuration) => ({ - value: configuration.id, - label: configuration.configurationName, - })) - ); - }, - } - ); - - trpcClient.sendgridConfiguration.fetch.useQuery(undefined, { - onSuccess(data) { - const keys = Object.keys(data.availableConfigurations); - - setSendgridConfigurationsListData( - keys.map((key) => ({ - value: key, - label: data.availableConfigurations[key].configurationName, - })) - ); - }, - }); - - const { - data: channels, - isLoading: isChannelsLoading, - isSuccess: isChannelsFetchSuccess, - } = trpcClient.channels.fetch.useQuery(); - - const { mutate, error: saveError } = trpcClient.appConfiguration.setAndReplace.useMutation({ - onSuccess() { - refetchConfig(); - appBridge?.dispatch( - actions.Notification({ - title: "Success", - text: "Saved app configuration", - status: "success", - }) - ); - }, - }); - const [activeChannelSlug, setActiveChannelSlug] = useState(null); - useEffect(() => { - if (isChannelsFetchSuccess) { - setActiveChannelSlug(channels[0].slug ?? null); - } - }, [isChannelsFetchSuccess, channels]); + const { data: channelsData, isLoading: isChannelsDataLoading } = + trpcClient.channels.fetch.useQuery(undefined, { + onSuccess: (data) => { + if (data?.length) { + setActiveChannelSlug(data[0].slug); + } + }, + }); - const activeChannel = useMemo(() => { - try { - return channels!.find((c) => c.slug === activeChannelSlug)!; - } catch (e) { - return null; - } - }, [channels, activeChannelSlug]); + const { + data: configurationData, + refetch: refetchConfig, + isLoading: isConfigurationDataLoading, + } = trpcClient.appConfiguration.getChannelConfiguration.useQuery( + { + channelSlug: activeChannelSlug!, + }, + { enabled: !!activeChannelSlug } + ); - if (isChannelsLoading) { + const { data: mjmlConfigurations, isLoading: isMjmlQueryLoading } = + trpcClient.mjmlConfiguration.getConfigurations.useQuery({}); + + const mjmlConfigurationsListData = useMemo(() => { + return ( + mjmlConfigurations?.map((configuration) => ({ + value: configuration.id, + label: configuration.configurationName, + })) ?? [] + ); + }, [mjmlConfigurations]); + + const { data: sendgridConfigurations, isLoading: isSendgridQueryLoading } = + trpcClient.sendgridConfiguration.fetch.useQuery(); + + const sendgridConfigurationsListData = useMemo(() => { + if (!sendgridConfigurations) { + return []; + } + const keys = Object.keys(sendgridConfigurations.availableConfigurations ?? {}) || []; + + return ( + keys.map((key) => ({ + value: key, + label: sendgridConfigurations.availableConfigurations[key].configurationName, + })) ?? [] + ); + }, [sendgridConfigurations]); + + const { mutate: mutateAppChannelConfiguration, error: saveError } = + trpcClient.appConfiguration.setChannelConfiguration.useMutation({ + onSuccess() { + refetchConfig(); + appBridge?.dispatch( + actions.Notification({ + title: "Success", + text: "Saved app configuration", + status: "success", + }) + ); + }, + }); + + const activeChannel = channelsData?.find((c) => c.slug === activeChannelSlug); + + if (isChannelsDataLoading) { return ; } - - if (!channels?.length) { + if (!channelsData?.length) { return
NO CHANNELS
; } - if (!activeChannel) { - return
Error. No channel available
; - } + const isFormDataLoading = + isConfigurationDataLoading || isMjmlQueryLoading || isSendgridQueryLoading; return ( { } onClick={(id) => setActiveChannelSlug(id)} - items={channels.map((c) => ({ label: c.name, id: c.slug })) || []} + items={channelsData.map((c) => ({ label: c.name, id: c.slug })) || []} /> - {activeChannel ? ( -
- { - const newConfig = AppConfigContainer.setChannelAppConfiguration(configurationData)( - activeChannel.slug - )(data); - - mutate(newConfig); - }} - initialData={AppConfigContainer.getChannelAppConfiguration(configurationData)( - activeChannel.slug - )} - channelName={activeChannel?.name ?? activeChannelSlug} - /> - {saveError && {saveError.message}} -
- ) : null} +
+ {!activeChannel || isFormDataLoading ? ( + + ) : ( + <> + { + mutateAppChannelConfiguration({ + channel: activeChannel.slug, + configuration: data, + }); + }} + initialData={configurationData} + channelName={activeChannel?.name ?? activeChannelSlug} + /> + {saveError && {saveError.message}} + + )} +
); }; diff --git a/apps/emails-and-messages/src/modules/channels/channels.router.ts b/apps/emails-and-messages/src/modules/channels/channels.router.ts index aa8e11e..0c33549 100644 --- a/apps/emails-and-messages/src/modules/channels/channels.router.ts +++ b/apps/emails-and-messages/src/modules/channels/channels.router.ts @@ -1,17 +1,16 @@ import { ChannelsFetcher } from "./channels-fetcher"; -import { ChannelFragment } from "../../../generated/graphql"; -import { createClient } from "../../lib/create-graphq-client"; +import { createClient } from "../../lib/create-graphql-client"; import { router } from "../trpc/trpc-server"; import { protectedClientProcedure } from "../trpc/protected-client-procedure"; export const channelsRouter = router({ - fetch: protectedClientProcedure.query(async ({ ctx, input }): Promise => { + fetch: protectedClientProcedure.query(async ({ ctx }) => { const client = createClient(ctx.saleorApiUrl, async () => Promise.resolve({ token: ctx.appToken }) ); const fetcher = new ChannelsFetcher(client); - return fetcher.fetchChannels().then((channels) => channels ?? []); + return await fetcher.fetchChannels().then((channels) => channels ?? []); }), }); diff --git a/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts b/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts index eaa07f4..141cc94 100644 --- a/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts +++ b/apps/emails-and-messages/src/modules/event-handlers/send-event-messages.ts @@ -1,8 +1,10 @@ import { AuthData } from "@saleor/app-sdk/APL"; +import { Client } from "urql"; import { logger as pinoLogger } from "../../lib/logger"; +import { AppConfigurationService } from "../app-configuration/get-app-configuration.service"; +import { MjmlConfigurationService } from "../mjml/configuration/get-mjml-configuration.service"; import { sendMjml } from "../mjml/send-mjml"; import { sendSendgrid } from "../sendgrid/send-sendgrid"; -import { appRouter } from "../trpc/trpc-app-router"; import { MessageEventTypes } from "./message-event-types"; interface SendEventMessagesArgs { @@ -11,6 +13,7 @@ interface SendEventMessagesArgs { event: MessageEventTypes; authData: AuthData; payload: any; + client: Client; } export const sendEventMessages = async ({ @@ -19,6 +22,7 @@ export const sendEventMessages = async ({ event, authData, payload, + client, }: SendEventMessagesArgs) => { const logger = pinoLogger.child({ fn: "sendEventMessages", @@ -26,16 +30,12 @@ export const sendEventMessages = async ({ logger.debug("Function called"); - // get app configuration - const caller = appRouter.createCaller({ - appId: authData.appId, + const appConfigurationService = new AppConfigurationService({ + apiClient: client, saleorApiUrl: authData.saleorApiUrl, - token: authData.token, - ssr: true, }); - const appConfigurations = await caller.appConfiguration.fetch(); - const channelAppConfiguration = appConfigurations.configurationsPerChannel[channel]; + const channelAppConfiguration = await appConfigurationService.getChannelConfiguration(channel); if (!channelAppConfiguration) { logger.warn("App has no configuration for this channel"); @@ -50,18 +50,27 @@ export const sendEventMessages = async ({ if (channelAppConfiguration.mjmlConfigurationId) { logger.debug("Channel has assigned MJML configuration"); - const mjmlStatus = await sendMjml({ - authData, - channel, - event, - payload, - recipientEmail, - mjmlConfigurationId: channelAppConfiguration.mjmlConfigurationId, + + const mjmlConfigurationService = new MjmlConfigurationService({ + apiClient: client, + saleorApiUrl: authData.saleorApiUrl, }); - if (mjmlStatus?.errors.length) { - logger.error("MJML errors"); - logger.error(mjmlStatus?.errors); + const mjmlConfiguration = await mjmlConfigurationService.getConfiguration({ + id: channelAppConfiguration.mjmlConfigurationId, + }); + if (mjmlConfiguration) { + const mjmlStatus = await sendMjml({ + event, + payload, + recipientEmail, + mjmlConfiguration, + }); + + if (mjmlStatus?.errors.length) { + logger.error("MJML errors"); + logger.error(mjmlStatus?.errors); + } } } const sendgridStatus = await sendSendgrid({ diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/get-mjml-configuration.service.ts b/apps/emails-and-messages/src/modules/mjml/configuration/get-mjml-configuration.service.ts index 7b3dee5..a8285d1 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/get-mjml-configuration.service.ts +++ b/apps/emails-and-messages/src/modules/mjml/configuration/get-mjml-configuration.service.ts @@ -1,36 +1,107 @@ -import { PrivateMetadataMjmlConfigurator } from "./mjml-configurator"; +import { MjmlConfigurator, PrivateMetadataMjmlConfigurator } from "./mjml-configurator"; import { Client } from "urql"; import { logger as pinoLogger } from "../../../lib/logger"; import { createSettingsManager } from "../../app-configuration/metadata-manager"; +import { MjmlConfig, MjmlConfiguration } from "./mjml-config"; +import { FilterConfigurationsArgs, MjmlConfigContainer } from "./mjml-config-container"; -// todo test -export class GetMjmlConfigurationService { - constructor( - private settings: { - apiClient: Client; - saleorApiUrl: string; - } - ) {} +const logger = pinoLogger.child({ + service: "MjmlConfigurationService", +}); - async getConfiguration() { - const logger = pinoLogger.child({ - service: "GetMjmlConfigurationService", - saleorApiUrl: this.settings.saleorApiUrl, - }); +export class MjmlConfigurationService { + private configurationData?: MjmlConfig; + private metadataConfigurator: MjmlConfigurator; - const { saleorApiUrl, apiClient } = this.settings; - - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(apiClient), - saleorApiUrl + constructor(args: { apiClient: Client; saleorApiUrl: string; initialData?: MjmlConfig }) { + this.metadataConfigurator = new PrivateMetadataMjmlConfigurator( + createSettingsManager(args.apiClient), + args.saleorApiUrl ); - const savedMjmlConfig = (await mjmlConfigurator.getConfig()) ?? null; - - logger.debug(savedMjmlConfig, "Retrieved MJML config from Metadata. Will return it"); - - if (savedMjmlConfig) { - return savedMjmlConfig; + if (args.initialData) { + this.configurationData = args.initialData; } } + + // Fetch configuration from Saleor API and cache it + private async pullConfiguration() { + logger.debug("Fetch configuration from Saleor API"); + + const config = await this.metadataConfigurator.getConfig(); + this.configurationData = config; + } + + // Push configuration to Saleor API + private async pushConfiguration() { + logger.debug("Push configuration to Saleor API"); + + await this.metadataConfigurator.setConfig(this.configurationData!); + } + + // Returns configuration from cache or fetches it from Saleor API + async getConfigurationRoot() { + logger.debug("Get configuration root"); + + if (this.configurationData) { + logger.debug("Using cached configuration"); + return this.configurationData; + } + + // No cached data, fetch it from Saleor API + await this.pullConfiguration(); + + if (!this.configurationData) { + logger.warn("No configuration found in Saleor API"); + return; + } + + return this.configurationData; + } + + // Saves configuration to Saleor API and cache it + async setConfigurationRoot(config: MjmlConfig) { + logger.debug("Set configuration root"); + + this.configurationData = config; + await this.pushConfiguration(); + } + + async getConfiguration({ id }: { id: string }) { + logger.debug("Get configuration"); + return MjmlConfigContainer.getConfiguration(await this.getConfigurationRoot())({ id }); + } + + async getConfigurations(filter: FilterConfigurationsArgs) { + logger.debug("Get configuration"); + return MjmlConfigContainer.getConfigurations(await this.getConfigurationRoot())(filter); + } + + async createConfiguration(config: Omit) { + logger.debug("Create configuration"); + const updatedConfigurationRoot = MjmlConfigContainer.createConfiguration( + await this.getConfigurationRoot() + )(config); + await this.setConfigurationRoot(updatedConfigurationRoot); + + return updatedConfigurationRoot.configurations[ + updatedConfigurationRoot.configurations.length - 1 + ]; + } + + async updateConfiguration(config: MjmlConfiguration) { + logger.debug("Update configuration"); + const updatedConfigurationRoot = MjmlConfigContainer.updateConfiguration( + await this.getConfigurationRoot() + )(config); + this.setConfigurationRoot(updatedConfigurationRoot); + } + + async deleteConfiguration({ id }: { id: string }) { + logger.debug("Delete configuration"); + const updatedConfigurationRoot = MjmlConfigContainer.deleteConfiguration( + await this.getConfigurationRoot() + )({ id }); + this.setConfigurationRoot(updatedConfigurationRoot); + } } diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts index 6544a55..7b3e6c5 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts +++ b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-config-container.ts @@ -2,7 +2,9 @@ import { messageEventTypes } from "../../event-handlers/message-event-types"; import { MjmlConfig as MjmlConfigurationRoot, MjmlConfiguration } from "./mjml-config"; import { defaultMjmlTemplates, defaultMjmlSubjectTemplates } from "../default-templates"; -const getDefaultEventsConfiguration = (): MjmlConfiguration["events"] => +export const generateMjmlConfigurationId = () => Date.now().toString(); + +export const getDefaultEventsConfiguration = (): MjmlConfiguration["events"] => messageEventTypes.map((eventType) => ({ active: true, eventType: eventType, @@ -41,7 +43,7 @@ const getConfiguration = return mjmlConfigRoot.configurations.find((c) => c.id === id); }; -interface FilterConfigurationsArgs { +export interface FilterConfigurationsArgs { ids?: string[]; active?: boolean; } @@ -74,7 +76,7 @@ const createConfiguration = // for creating a new configurations, the ID has to be generated const newConfiguration = { ...mjmlConfiguration, - id: Date.now().toString(), + id: generateMjmlConfigurationId(), events: getDefaultEventsConfiguration(), }; mjmlConfigNormalized.configurations.unshift(newConfiguration); diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-configuration.router.ts b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-configuration.router.ts index 25c97c3..294cc61 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/mjml-configuration.router.ts +++ b/apps/emails-and-messages/src/modules/mjml/configuration/mjml-configuration.router.ts @@ -1,4 +1,3 @@ -import { PrivateMetadataMjmlConfigurator } from "./mjml-configurator"; import { logger as pinoLogger } from "../../../lib/logger"; import { mjmlCreateConfigurationSchema, @@ -9,134 +8,96 @@ import { mjmlUpdateEventConfigurationInputSchema, mjmlUpdateOrCreateConfigurationSchema, } from "./mjml-config-input-schema"; -import { GetMjmlConfigurationService } from "./get-mjml-configuration.service"; +import { MjmlConfigurationService } from "./get-mjml-configuration.service"; import { router } from "../../trpc/trpc-server"; import { protectedClientProcedure } from "../../trpc/protected-client-procedure"; -import { createSettingsManager } from "../../app-configuration/metadata-manager"; import { z } from "zod"; import { compileMjml } from "../compile-mjml"; import Handlebars from "handlebars"; -import { MjmlConfigContainer } from "./mjml-config-container"; import { TRPCError } from "@trpc/server"; +// Allow access only for the dashboard users and attaches the +// configuration service to the context +const protectedWithConfigurationService = protectedClientProcedure.use(({ next, ctx }) => + next({ + ctx: { + ...ctx, + configurationService: new MjmlConfigurationService({ + apiClient: ctx.apiClient, + saleorApiUrl: ctx.saleorApiUrl, + }), + }, + }) +); + export const mjmlConfigurationRouter = router({ - fetch: protectedClientProcedure.query(async ({ ctx }) => { + fetch: protectedWithConfigurationService.query(async ({ ctx }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); - logger.debug("mjmlConfigurationRouter.fetch called"); - - return new GetMjmlConfigurationService({ - apiClient: ctx.apiClient, - saleorApiUrl: ctx.saleorApiUrl, - }).getConfiguration(); + return ctx.configurationService.getConfigurationRoot(); }), - getConfiguration: protectedClientProcedure + getConfiguration: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlGetConfigurationInputSchema) .query(async ({ ctx, input }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); - logger.debug(input, "mjmlConfigurationRouter.get called"); - - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - const configRoot = await mjmlConfigurator.getConfig(); - return MjmlConfigContainer.getConfiguration(configRoot)(input); + return ctx.configurationService.getConfiguration(input); }), - getConfigurations: protectedClientProcedure + getConfigurations: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlGetConfigurationsInputSchema) .query(async ({ ctx, input }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); logger.debug(input, "mjmlConfigurationRouter.getConfigurations called"); - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - const configRoot = await mjmlConfigurator.getConfig(); - return MjmlConfigContainer.getConfigurations(configRoot)(input); + return ctx.configurationService.getConfigurations(input); }), - createConfiguration: protectedClientProcedure + createConfiguration: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlCreateConfigurationSchema) .mutation(async ({ ctx, input }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); logger.debug(input, "mjmlConfigurationRouter.create called"); - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - const configRoot = await mjmlConfigurator.getConfig(); - const newConfigurationRoot = MjmlConfigContainer.createConfiguration(configRoot)(input); - - await mjmlConfigurator.setConfig(newConfigurationRoot); - - return null; + return await ctx.configurationService.createConfiguration(input); }), - deleteConfiguration: protectedClientProcedure + deleteConfiguration: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlDeleteConfigurationInputSchema) .mutation(async ({ ctx, input }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); logger.debug(input, "mjmlConfigurationRouter.delete called"); - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - const configRoot = await mjmlConfigurator.getConfig(); - const newConfigurationRoot = MjmlConfigContainer.deleteConfiguration(configRoot)(input); - - await mjmlConfigurator.setConfig(newConfigurationRoot); + await ctx.configurationService.deleteConfiguration(input); return null; }), - updateOrCreateConfiguration: protectedClientProcedure + updateOrCreateConfiguration: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlUpdateOrCreateConfigurationSchema) .mutation(async ({ ctx, input }) => { const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl }); - logger.debug(input, "mjmlConfigurationRouter.update or create called"); - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - const configRoot = await mjmlConfigurator.getConfig(); - const { id } = input; - if (!!id) { - const existingConfiguration = MjmlConfigContainer.getConfiguration(configRoot)({ id }); + if (!id) { + return await ctx.configurationService.createConfiguration(input); + } else { + const existingConfiguration = await ctx.configurationService.getConfiguration({ id }); if (!existingConfiguration) { throw new TRPCError({ code: "BAD_REQUEST", message: "Configuration not found", }); } - // checking typeof id is not enough to satisfy typescript, so need to override id field issue const configuration = { id, ...input, events: existingConfiguration.events, }; - - const newConfigurationRoot = - MjmlConfigContainer.updateConfiguration(configRoot)(configuration); - await mjmlConfigurator.setConfig(newConfigurationRoot); + await ctx.configurationService.updateConfiguration(configuration); return configuration; - } else { - const newConfigurationRoot = MjmlConfigContainer.createConfiguration(configRoot)(input); - await mjmlConfigurator.setConfig(newConfigurationRoot); - return newConfigurationRoot.configurations[newConfigurationRoot.configurations.length - 1]; } }), - getEventConfiguration: protectedClientProcedure + getEventConfiguration: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlGetEventConfigurationInputSchema) .query(async ({ ctx, input }) => { @@ -144,22 +105,17 @@ export const mjmlConfigurationRouter = router({ logger.debug(input, "mjmlConfigurationRouter.getEventConfiguration or create called"); - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - const configRoot = await mjmlConfigurator.getConfig(); - - const configuration = MjmlConfigContainer.getConfiguration(configRoot)({ + const configuration = await ctx.configurationService.getConfiguration({ id: input.configurationId, }); + if (!configuration) { throw new TRPCError({ code: "BAD_REQUEST", message: "Configuration not found", }); } + const event = configuration.events.find((e) => e.eventType === input.eventType); if (!event) { throw new TRPCError({ @@ -169,7 +125,7 @@ export const mjmlConfigurationRouter = router({ } return event; }), - updateEventConfiguration: protectedClientProcedure + updateEventConfiguration: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input(mjmlUpdateEventConfigurationInputSchema) .mutation(async ({ ctx, input }) => { @@ -177,22 +133,17 @@ export const mjmlConfigurationRouter = router({ logger.debug(input, "mjmlConfigurationRouter.updateEventConfiguration or create called"); - const mjmlConfigurator = new PrivateMetadataMjmlConfigurator( - createSettingsManager(ctx.apiClient), - ctx.saleorApiUrl - ); - - const configRoot = await mjmlConfigurator.getConfig(); - - const configuration = MjmlConfigContainer.getConfiguration(configRoot)({ + const configuration = await ctx.configurationService.getConfiguration({ id: input.configurationId, }); + if (!configuration) { throw new TRPCError({ code: "BAD_REQUEST", message: "Configuration not found", }); } + const eventIndex = configuration.events.findIndex((e) => e.eventType === input.eventType); configuration.events[eventIndex] = { active: input.active, @@ -200,13 +151,11 @@ export const mjmlConfigurationRouter = router({ template: input.template, subject: input.subject, }; - const newConfigurationRoot = - MjmlConfigContainer.updateConfiguration(configRoot)(configuration); - await mjmlConfigurator.setConfig(newConfigurationRoot); + await ctx.configurationService.updateConfiguration(configuration); return configuration; }), - renderTemplate: protectedClientProcedure + renderTemplate: protectedWithConfigurationService .meta({ requiredClientPermissions: ["MANAGE_APPS"] }) .input( z.object({ diff --git a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx index 62c4d31..33695c9 100644 --- a/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx +++ b/apps/emails-and-messages/src/modules/mjml/configuration/ui/mjml-configuration-tab.tsx @@ -116,19 +116,25 @@ export const MjmlConfigurationTab = ({ configurationId }: MjmlConfigurationTabPr items={configurations?.map((c) => ({ label: c.configurationName, id: c.id })) || []} />
- refetchConfigurations()} - initialData={configuration || getDefaultEmptyConfiguration()} - configurationId={configurationId} - /> - {!!configurationId && !!configuration && ( - { - refetchConfigurations(); - }} - /> + {configurationsIsLoading ? ( + + ) : ( + <> + refetchConfigurations()} + initialData={configuration || getDefaultEmptyConfiguration()} + configurationId={configurationId} + /> + {!!configurationId && !!configuration && ( + { + refetchConfigurations(); + }} + /> + )} + )}
diff --git a/apps/emails-and-messages/src/modules/mjml/get-active-mjml-settings.ts b/apps/emails-and-messages/src/modules/mjml/get-active-mjml-settings.ts deleted file mode 100644 index 0045353..0000000 --- a/apps/emails-and-messages/src/modules/mjml/get-active-mjml-settings.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { AuthData } from "@saleor/app-sdk/APL"; -import { appRouter } from "../trpc/trpc-app-router"; -import { logger as pinoLogger } from "../../lib/logger"; - -interface GetMjmlSettingsArgs { - authData: AuthData; - channel: string; - configurationId: string; -} - -export const getActiveMjmlSettings = async ({ - authData, - channel, - configurationId, -}: GetMjmlSettingsArgs) => { - const logger = pinoLogger.child({ - fn: "getMjmlSettings", - channel, - }); - - const caller = appRouter.createCaller({ - appId: authData.appId, - saleorApiUrl: authData.saleorApiUrl, - token: authData.token, - ssr: true, - }); - - const configuration = await caller.mjmlConfiguration.getConfiguration({ - id: configurationId, - }); - - if (!configuration) { - logger.warn(`The MJML configuration with id ${configurationId} does not exist`); - return; - } - - if (!configuration.active) { - logger.warn(`The MJML configuration ${configuration.configurationName} is not active`); - return; - } - - return configuration; -}; diff --git a/apps/emails-and-messages/src/modules/mjml/send-mjml.ts b/apps/emails-and-messages/src/modules/mjml/send-mjml.ts index 712b326..1b28539 100644 --- a/apps/emails-and-messages/src/modules/mjml/send-mjml.ts +++ b/apps/emails-and-messages/src/modules/mjml/send-mjml.ts @@ -1,16 +1,13 @@ import { logger as pinoLogger } from "../../lib/logger"; -import { AuthData } from "@saleor/app-sdk/APL"; -import { getActiveMjmlSettings } from "./get-active-mjml-settings"; import { compileMjml } from "./compile-mjml"; import { compileHandlebarsTemplate } from "./compile-handlebars-template"; import { sendEmailWithSmtp } from "./send-email-with-smtp"; import { MessageEventTypes } from "../event-handlers/message-event-types"; import { htmlToPlaintext } from "./html-to-plaintext"; +import { MjmlConfiguration } from "./configuration/mjml-config"; interface SendMjmlArgs { - authData: AuthData; - mjmlConfigurationId: string; - channel: string; + mjmlConfiguration: MjmlConfiguration; recipientEmail: string; event: MessageEventTypes; payload: any; @@ -24,36 +21,17 @@ export interface EmailServiceResponse { } export const sendMjml = async ({ - authData, - channel, payload, recipientEmail, event, - mjmlConfigurationId, + mjmlConfiguration, }: SendMjmlArgs) => { const logger = pinoLogger.child({ fn: "sendMjml", event, }); - const settings = await getActiveMjmlSettings({ - authData, - channel, - configurationId: mjmlConfigurationId, - }); - - if (!settings) { - logger.debug("No active settings, skipping"); - return { - errors: [ - { - message: "No active settings", - }, - ], - }; - } - - const eventSettings = settings.events.find((e) => e.eventType === event); + const eventSettings = mjmlConfiguration.events.find((e) => e.eventType === event); if (!eventSettings) { logger.debug("No active settings for this event, skipping"); return { @@ -154,13 +132,13 @@ export const sendMjml = async ({ mailData: { text: emailBodyPlaintext, html: emailBodyHtml, - from: `${settings.senderName} <${settings.senderEmail}>`, + from: `${mjmlConfiguration.senderName} <${mjmlConfiguration.senderEmail}>`, to: recipientEmail, subject: emailSubject, }, smtpSettings: { - host: settings.smtpHost, - port: parseInt(settings.smtpPort, 10), + host: mjmlConfiguration.smtpHost, + port: parseInt(mjmlConfiguration.smtpPort, 10), }, }); diff --git a/apps/emails-and-messages/src/modules/trpc/protected-client-procedure.ts b/apps/emails-and-messages/src/modules/trpc/protected-client-procedure.ts index e60c207..9e2b680 100644 --- a/apps/emails-and-messages/src/modules/trpc/protected-client-procedure.ts +++ b/apps/emails-and-messages/src/modules/trpc/protected-client-procedure.ts @@ -4,7 +4,7 @@ import { TRPCError } from "@trpc/server"; import { ProtectedHandlerError } from "@saleor/app-sdk/handlers/next"; import { saleorApp } from "../../saleor-app"; import { logger } from "../../lib/logger"; -import { createClient } from "../../lib/create-graphq-client"; +import { createClient } from "../../lib/create-graphql-client"; const attachAppToken = middleware(async ({ ctx, next }) => { logger.debug("attachAppToken middleware"); diff --git a/apps/emails-and-messages/src/modules/ui/code-editor.tsx b/apps/emails-and-messages/src/modules/ui/code-editor.tsx index 4ec35d4..c9a7c4d 100644 --- a/apps/emails-and-messages/src/modules/ui/code-editor.tsx +++ b/apps/emails-and-messages/src/modules/ui/code-editor.tsx @@ -21,7 +21,6 @@ export const CodeEditor = ({ initialTemplate, onChange, value, language }: Props const handleOnChange = useCallback( (value?: string) => { - console.log("ON CHANGE"); onChange(value ?? ""); }, [value] diff --git a/apps/emails-and-messages/src/pages/api/webhooks/invoice-sent.ts b/apps/emails-and-messages/src/pages/api/webhooks/invoice-sent.ts index 7440bae..656b9d3 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/invoice-sent.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/invoice-sent.ts @@ -7,6 +7,7 @@ import { OrderDetailsFragmentDoc, } from "../../../../generated/graphql"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; +import { createClient } from "../../../lib/create-graphql-client"; const InvoiceSentWebhookPayload = gql` ${OrderDetailsFragmentDoc} @@ -71,10 +72,14 @@ const handler: NextWebhookApiHandler = async } const channel = order.channel.slug; + const client = createClient(authData.saleorApiUrl, async () => + Promise.resolve({ token: authData.token }) + ); await sendEventMessages({ authData, channel, + client, event: "INVOICE_SENT", payload: { order: payload.order }, recipientEmail, diff --git a/apps/emails-and-messages/src/pages/api/webhooks/order-cancelled.ts b/apps/emails-and-messages/src/pages/api/webhooks/order-cancelled.ts index be216b2..3d53001 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/order-cancelled.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/order-cancelled.ts @@ -7,6 +7,7 @@ import { OrderDetailsFragmentDoc, } from "../../../../generated/graphql"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; +import { createClient } from "../../../lib/create-graphql-client"; const OrderCancelledWebhookPayload = gql` ${OrderDetailsFragmentDoc} @@ -62,10 +63,14 @@ const handler: NextWebhookApiHandler = asy } const channel = order.channel.slug; + const client = createClient(authData.saleorApiUrl, async () => + Promise.resolve({ token: authData.token }) + ); await sendEventMessages({ authData, channel, + client, event: "ORDER_CANCELLED", payload: { order: payload.order }, recipientEmail, diff --git a/apps/emails-and-messages/src/pages/api/webhooks/order-confirmed.ts b/apps/emails-and-messages/src/pages/api/webhooks/order-confirmed.ts index 1213cba..bebf324 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/order-confirmed.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/order-confirmed.ts @@ -7,6 +7,7 @@ import { OrderDetailsFragmentDoc, } from "../../../../generated/graphql"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; +import { createClient } from "../../../lib/create-graphql-client"; const OrderConfirmedWebhookPayload = gql` ${OrderDetailsFragmentDoc} @@ -63,10 +64,14 @@ const handler: NextWebhookApiHandler = asy } const channel = order.channel.slug; + const client = createClient(authData.saleorApiUrl, async () => + Promise.resolve({ token: authData.token }) + ); await sendEventMessages({ authData, channel, + client, event: "ORDER_CONFIRMED", payload: { order: payload.order }, recipientEmail, diff --git a/apps/emails-and-messages/src/pages/api/webhooks/order-created.ts b/apps/emails-and-messages/src/pages/api/webhooks/order-created.ts index 518075c..cd9c47b 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/order-created.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/order-created.ts @@ -1,10 +1,11 @@ -import { OrderDetailsFragment, OrderDetailsFragmentDoc } from "./../../../../generated/graphql"; +import { OrderDetailsFragmentDoc } from "./../../../../generated/graphql"; import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next"; import { gql } from "urql"; import { saleorApp } from "../../../saleor-app"; import { logger as pinoLogger } from "../../../lib/logger"; import { OrderCreatedWebhookPayloadFragment } from "../../../../generated/graphql"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; +import { createClient } from "../../../lib/create-graphql-client"; const OrderCreatedWebhookPayload = gql` ${OrderDetailsFragmentDoc} @@ -60,10 +61,14 @@ const handler: NextWebhookApiHandler = async } const channel = order.channel.slug; + const client = createClient(authData.saleorApiUrl, async () => + Promise.resolve({ token: authData.token }) + ); await sendEventMessages({ authData, channel, + client, event: "ORDER_CREATED", payload: { order: payload.order }, recipientEmail, diff --git a/apps/emails-and-messages/src/pages/api/webhooks/order-fulfilled.ts b/apps/emails-and-messages/src/pages/api/webhooks/order-fulfilled.ts index 7badaee..5260c25 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/order-fulfilled.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/order-fulfilled.ts @@ -7,6 +7,7 @@ import { OrderFulfilledWebhookPayloadFragment, } from "../../../../generated/graphql"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; +import { createClient } from "../../../lib/create-graphql-client"; const OrderFulfilledWebhookPayload = gql` ${OrderDetailsFragmentDoc} @@ -63,9 +64,13 @@ const handler: NextWebhookApiHandler = asy } const channel = order.channel.slug; + const client = createClient(authData.saleorApiUrl, async () => + Promise.resolve({ token: authData.token }) + ); await sendEventMessages({ authData, + client, channel, event: "ORDER_FULFILLED", payload: { order: payload.order }, diff --git a/apps/emails-and-messages/src/pages/api/webhooks/order-fully-paid.ts b/apps/emails-and-messages/src/pages/api/webhooks/order-fully-paid.ts index 308322c..d129cdb 100644 --- a/apps/emails-and-messages/src/pages/api/webhooks/order-fully-paid.ts +++ b/apps/emails-and-messages/src/pages/api/webhooks/order-fully-paid.ts @@ -7,6 +7,7 @@ import { OrderFullyPaidWebhookPayloadFragment, } from "../../../../generated/graphql"; import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages"; +import { createClient } from "../../../lib/create-graphql-client"; const OrderFullyPaidWebhookPayload = gql` ${OrderDetailsFragmentDoc} @@ -63,10 +64,14 @@ const handler: NextWebhookApiHandler = asy } const channel = order.channel.slug; + const client = createClient(authData.saleorApiUrl, async () => + Promise.resolve({ token: authData.token }) + ); await sendEventMessages({ authData, channel, + client, event: "ORDER_FULLY_PAID", payload: { order: payload.order }, recipientEmail, diff --git a/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx b/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx index eb26859..776b8e6 100644 --- a/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx +++ b/apps/emails-and-messages/src/pages/configuration/mjml/[configurationId]/event/[eventType].tsx @@ -3,7 +3,7 @@ import React from "react"; import { useRouter } from "next/router"; import { trpcClient } from "../../../../../modules/trpc/trpc-client"; -import { checkMessageEventType } from "../../../../../modules/event-handlers/check-message-event-type"; +import { parseMessageEventType } from "../../../../../modules/event-handlers/parse-message-event-type"; import { ConfigurationPageBaseLayout } from "../../../../../modules/ui/configuration-page-base-layout"; import { EventConfigurationForm } from "../../../../../modules/mjml/configuration/ui/mjml-event-configuration-form"; import { LoadingIndicator } from "../../../../../modules/ui/loading-indicator"; @@ -13,7 +13,7 @@ const EventConfigurationPage: NextPage = () => { const configurationId = router.query.configurationId as string; const eventTypeFromQuery = router.query.eventType as string | undefined; - const eventType = checkMessageEventType(eventTypeFromQuery); + const eventType = parseMessageEventType(eventTypeFromQuery); const { data: configuration,