UNPKG

@dugongjs/core

Version:

63 lines (62 loc) 3.45 kB
import { AbstractAggregateHandler } from "../abstract-aggregate-handler/abstract-aggregate-handler.js"; /** * Utility class to wait for a message to be consumed. This is primarily intended for testing purposes. */ export class WaitForMessageConsumer extends AbstractAggregateHandler { constructor(options) { super({ ...options, transactionManager: { transaction: (fn) => fn({}) } }); this.domainEventRepository = options.domainEventRepository; this.consumedMessageRepository = options.consumedMessageRepository; this.messageConsumer = options.messageConsumer; this.pollingInterval = options.pollingInterval ?? 100; } /** * Waits for the specified messages to be consumed by the given message consumer. * @param consumerName Name of the message consumer. * @param ids IDs of the messages to wait for. * @returns A promise that resolves when all messages are consumed. */ async waitForMessagesToBeConsumed(consumerName, ...ids) { const messageConsumerId = this.messageConsumer.generateMessageConsumerIdForAggregate(this.currentOrigin, this.aggregateType, consumerName); if (ids.length === 0) { this.logger?.verbose("No messages to wait for"); return; } const remainingIds = new Set(ids); while (true) { const remainingIdsArray = Array.from(remainingIds); const results = await Promise.all(remainingIdsArray.map(async (id) => this.consumedMessageRepository.checkIfMessageIsConsumed(null, id, messageConsumerId))); results.forEach((result, index) => { const id = remainingIdsArray[index]; if (result) { this.logger?.verbose(`Message ${id} consumed`); remainingIds.delete(id); } }); if (remainingIds.size === 0) { this.logger?.verbose("All messages consumed"); return; } this.logger?.verbose(`${messageConsumerId} waiting for domain events to be consumed: ${Array.from(remainingIds).join(", ")}`); await new Promise((resolve) => setTimeout(resolve, this.pollingInterval)); } } /** * Waits for all domain events of a specific aggregate to be consumed by the specified consumer. * This method retrieves the domain events for the specified aggregate and waits for them to be consumed by the given consumer. * @param consumerName Name of the message consumer. * @param aggregateId ID of the aggregate whose domain events are to be consumed. * @param tenantId Optional tenant ID to scope the domain events. * @param fromSequenceNumber Optional sequence number to start from when retrieving domain events. * @returns A promise that resolves when all domain events are consumed. */ async waitForAggregateDomainEventsToBeConsumed(consumerName, aggregateId, tenantId, fromSequenceNumber) { const domainEvents = await this.domainEventRepository.getAggregateDomainEvents(null, this.aggregateOrigin, this.aggregateType, aggregateId, tenantId, fromSequenceNumber); if (domainEvents.length === 0) { this.logger?.verbose("No domain events to wait for"); return; } const ids = domainEvents.map((event) => event.id); await this.waitForMessagesToBeConsumed(consumerName, ...ids); } }