import { DcRouterDb } from '../db/classes.dcrouter-db.js'; import { GatewayMailDomainDoc } from '../db/documents/classes.gateway-mail-domain.doc.js'; import { createGatewayMailDomainId, groupGatewayMailIdentities, normalizeGatewayMailDomain, normalizeGatewayMailDomainOwner, type IGatewayMailDomainOwner, type IStoredWorkAppMailIdentity, } from './gateway-mail-domain-authority.js'; export interface IGatewayMailDomainStore { readAllIdentities(): Promise; writeAllIdentities(identitiesArg: IStoredWorkAppMailIdentity[]): Promise; countDomains(ownerArg: IGatewayMailDomainOwner): Promise; } const validateDocument = (documentArg: GatewayMailDomainDoc): IStoredWorkAppMailIdentity[] => { const owner = normalizeGatewayMailDomainOwner({ gatewayClientType: documentArg.ownerGatewayClientType, gatewayClientId: documentArg.ownerGatewayClientId, }); const domain = normalizeGatewayMailDomain(documentArg.domain); const expectedId = createGatewayMailDomainId(owner, domain); if (documentArg.id !== expectedId) { throw new Error(`GatewayMailDomainDoc has a non-canonical id: ${documentArg.id}`); } const groups = groupGatewayMailIdentities(documentArg.identities || []); if (groups.length !== 1 || groups[0].id !== expectedId) { throw new Error(`GatewayMailDomainDoc identities do not match owner/domain: ${documentArg.id}`); } return groups[0].identities; }; export class GatewayMailDomainStore implements IGatewayMailDomainStore { public async readAllIdentities(): Promise { const documents = await GatewayMailDomainDoc.getInstances({}); const identities = documents.flatMap((documentArg) => validateDocument(documentArg)); return groupGatewayMailIdentities(identities).flatMap((groupArg) => groupArg.identities); } public async writeAllIdentities( identitiesArg: IStoredWorkAppMailIdentity[], ): Promise { const desiredGroups = groupGatewayMailIdentities(identitiesArg); const desiredById = new Map(desiredGroups.map((groupArg) => [groupArg.id, groupArg])); const db = DcRouterDb.getInstance().getDb(); const session = db.startSession(); try { await session.withTransaction(async () => { const existingDocuments = await GatewayMailDomainDoc.getInstances({}, { session }); const existingById = new Map(existingDocuments.map((documentArg) => { validateDocument(documentArg); return [documentArg.id, documentArg]; })); const now = Date.now(); for (const group of desiredGroups) { const document = existingById.get(group.id) || new GatewayMailDomainDoc(); document.id = group.id; document.ownerGatewayClientType = group.gatewayClientType; document.ownerGatewayClientId = group.gatewayClientId; document.domain = group.domain; document.identities = group.identities; document.createdAt = document.createdAt || now; document.updatedAt = now; await document.save({ session }); } for (const existingDocument of existingDocuments) { if (!desiredById.has(existingDocument.id)) { await existingDocument.delete({ session }); } } }); } finally { await session.endSession(); } return await this.readAllIdentities(); } public async countDomains(ownerArg: IGatewayMailDomainOwner): Promise { const owner = normalizeGatewayMailDomainOwner(ownerArg); return await GatewayMailDomainDoc.getCount({ ownerGatewayClientType: owner.gatewayClientType, ownerGatewayClientId: owner.gatewayClientId, }); } }