Files
medusa-store/packages/modules/notification/src/loaders/providers.ts
2024-09-26 11:14:35 +05:30

136 lines
3.5 KiB
TypeScript

import { moduleProviderLoader } from "@medusajs/framework/modules-sdk"
import {
LoaderOptions,
ModuleProvider,
ModulesSdkTypes,
} from "@medusajs/framework/types"
import {
ContainerRegistrationKeys,
lowerCaseFirst,
promiseAll,
} from "@medusajs/framework/utils"
import { NotificationProvider } from "@models"
import { NotificationProviderService } from "@services"
import {
NotificationIdentifiersRegistrationName,
NotificationProviderRegistrationPrefix,
} from "@types"
import { Lifetime, asFunction, asValue } from "awilix"
const registrationFn = async (klass, container, pluginOptions) => {
container.register({
[NotificationProviderRegistrationPrefix + pluginOptions.id]: asFunction(
(cradle) => new klass(cradle, pluginOptions.options ?? {}),
{
lifetime: klass.LIFE_TIME || Lifetime.SINGLETON,
}
),
})
container.registerAdd(
NotificationIdentifiersRegistrationName,
asValue(pluginOptions.id)
)
}
export default async ({
container,
options,
}: LoaderOptions<
(
| ModulesSdkTypes.ModuleServiceInitializeOptions
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
) & { providers: ModuleProvider[] }
>): Promise<void> => {
await moduleProviderLoader({
container,
providers: options?.providers || [],
registerServiceFn: registrationFn,
})
await syncDatabaseProviders({
container,
providers: options?.providers || [],
})
}
async function syncDatabaseProviders({
container,
providers,
}: {
container: any
providers: ModuleProvider[]
}) {
const providerServiceRegistrationKey = lowerCaseFirst(
NotificationProviderService.name
)
const providerService: ModulesSdkTypes.IMedusaInternalService<
typeof NotificationProvider
> = container.resolve(providerServiceRegistrationKey)
const logger = container.resolve(ContainerRegistrationKeys.LOGGER) ?? console
const normalizedProviders = providers.map((provider) => {
if (!provider.id) {
throw new Error(
"An entry in the provider config is required to initialize notification providers"
)
}
const config = provider.options as { channels: string[] }
return {
id: provider.id,
handle: provider.id,
name: provider.id,
is_enabled: true,
channels: config?.channels ?? [],
}
})
validateProviders(normalizedProviders)
try {
const providersInDb = await providerService.list({})
const providersToDisable = providersInDb.filter(
(dbProvider) =>
!normalizedProviders.some(
(normalizedProvider) => normalizedProvider.id === dbProvider.id
)
)
const promises: Promise<any>[] = []
if (normalizedProviders.length) {
promises.push(providerService.upsert(normalizedProviders))
}
if (providersToDisable.length) {
promises.push(
providerService.update(
providersToDisable.map((p) => ({
id: p.id,
is_enabled: false,
}))
)
)
}
await promiseAll(promises)
} catch (error) {
logger.error(`Error syncing the notification providers: ${error.message}`)
}
}
function validateProviders(providers: { channels: string[] }[]) {
const hasForChannel = {}
providers.forEach((provider) => {
provider.channels.forEach((channel) => {
if (hasForChannel[channel]) {
throw new Error(
`Multiple providers are configured for the same channel: ${channel}`
)
}
hasForChannel[channel] = true
})
})
}