* feat: refactor module joiner config and links generation * improve typings * WIP * WIP * WIP * rename type file * create link config * finish typings and add utils * improve links * WIP typings * finalize ExportModule utils * finalize ExportModule utils * fix: dml tests * improve and fixes * simplify typings with id changes * add toJSON * multiple fixes and entity builder fixes * fix currency searchable * fix tests * medusa service refactoring * cleanup * cleanup and fixes * make module name optional * renaming --------- Co-authored-by: Harminder Virk <virk.officials@gmail.com>
157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
import {
|
|
Context,
|
|
DAL,
|
|
InferEntityType,
|
|
INotificationModuleService,
|
|
InternalModuleDeclaration,
|
|
ModuleJoinerConfig,
|
|
ModulesSdkTypes,
|
|
NotificationTypes,
|
|
} from "@medusajs/types"
|
|
import {
|
|
InjectManager,
|
|
InjectTransactionManager,
|
|
MedusaContext,
|
|
MedusaError,
|
|
MedusaService,
|
|
promiseAll,
|
|
} from "@medusajs/utils"
|
|
import { Notification } from "@models"
|
|
import { joinerConfig } from "../joiner-config"
|
|
import NotificationProviderService from "./notification-provider"
|
|
|
|
type InjectedDependencies = {
|
|
baseRepository: DAL.RepositoryService
|
|
notificationService: ModulesSdkTypes.IMedusaInternalService<
|
|
typeof Notification
|
|
>
|
|
notificationProviderService: NotificationProviderService
|
|
}
|
|
|
|
export default class NotificationModuleService
|
|
extends MedusaService<{
|
|
Notification: { dto: NotificationTypes.NotificationDTO }
|
|
}>({ Notification })
|
|
implements INotificationModuleService
|
|
{
|
|
protected baseRepository_: DAL.RepositoryService
|
|
protected readonly notificationService_: ModulesSdkTypes.IMedusaInternalService<
|
|
typeof Notification
|
|
>
|
|
protected readonly notificationProviderService_: NotificationProviderService
|
|
|
|
constructor(
|
|
{
|
|
baseRepository,
|
|
notificationService,
|
|
notificationProviderService,
|
|
}: InjectedDependencies,
|
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
|
) {
|
|
// @ts-ignore
|
|
super(...arguments)
|
|
this.baseRepository_ = baseRepository
|
|
this.notificationService_ = notificationService
|
|
this.notificationProviderService_ = notificationProviderService
|
|
}
|
|
|
|
__joinerConfig(): ModuleJoinerConfig {
|
|
return joinerConfig
|
|
}
|
|
|
|
// @ts-expect-error
|
|
createNotifications(
|
|
data: NotificationTypes.CreateNotificationDTO[],
|
|
sharedContext?: Context
|
|
): Promise<NotificationTypes.NotificationDTO[]>
|
|
createNotifications(
|
|
data: NotificationTypes.CreateNotificationDTO,
|
|
sharedContext?: Context
|
|
): Promise<NotificationTypes.NotificationDTO>
|
|
|
|
@InjectManager("baseRepository_")
|
|
async createNotifications(
|
|
data:
|
|
| NotificationTypes.CreateNotificationDTO
|
|
| NotificationTypes.CreateNotificationDTO[],
|
|
@MedusaContext() sharedContext: Context = {}
|
|
): Promise<
|
|
NotificationTypes.NotificationDTO | NotificationTypes.NotificationDTO[]
|
|
> {
|
|
const normalized = Array.isArray(data) ? data : [data]
|
|
|
|
const createdNotifications = await this.createNotifications_(
|
|
normalized,
|
|
sharedContext
|
|
)
|
|
|
|
const serialized = await this.baseRepository_.serialize<
|
|
NotificationTypes.NotificationDTO[]
|
|
>(createdNotifications)
|
|
|
|
return Array.isArray(data) ? serialized : serialized[0]
|
|
}
|
|
|
|
@InjectTransactionManager("baseRepository_")
|
|
protected async createNotifications_(
|
|
data: NotificationTypes.CreateNotificationDTO[],
|
|
@MedusaContext() sharedContext: Context = {}
|
|
): Promise<InferEntityType<typeof Notification>[]> {
|
|
if (!data.length) {
|
|
return []
|
|
}
|
|
|
|
// TODO: At this point we should probably take a lock with the idempotency keys so we don't have race conditions.
|
|
// Also, we should probably rely on Redis for this instead of the database.
|
|
const idempotencyKeys = data
|
|
.map((entry) => entry.idempotency_key)
|
|
.filter(Boolean)
|
|
const alreadySentNotifications = await this.notificationService_.list(
|
|
{
|
|
idempotency_key: idempotencyKeys,
|
|
},
|
|
{ take: null },
|
|
sharedContext
|
|
)
|
|
|
|
const existsMap = new Map(
|
|
alreadySentNotifications.map((n) => [n.idempotency_key as string, true])
|
|
)
|
|
|
|
const notificationsToProcess = data.filter(
|
|
(entry) => !entry.idempotency_key || !existsMap.has(entry.idempotency_key)
|
|
)
|
|
|
|
const notificationsToCreate = await promiseAll(
|
|
notificationsToProcess.map(async (entry) => {
|
|
const provider =
|
|
await this.notificationProviderService_.getProviderForChannel(
|
|
entry.channel
|
|
)
|
|
|
|
if (!provider) {
|
|
throw new MedusaError(
|
|
MedusaError.Types.NOT_FOUND,
|
|
`Could not find a notification provider for channel: ${entry.channel}`
|
|
)
|
|
}
|
|
|
|
const res = await this.notificationProviderService_.send(
|
|
provider,
|
|
entry
|
|
)
|
|
return { ...entry, provider_id: provider.id, external_id: res.id }
|
|
})
|
|
)
|
|
|
|
// Currently we store notifications after they are sent, which might result in a notification being sent that is not registered in the database.
|
|
// If necessary, we can switch to a two-step process where we first create the notification, send it, and update it after it being sent.
|
|
const createdNotifications = await this.notificationService_.create(
|
|
notificationsToCreate,
|
|
sharedContext
|
|
)
|
|
|
|
return createdNotifications
|
|
}
|
|
}
|