@dugongjs/core
Version:
63 lines (62 loc) • 3.45 kB
JavaScript
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);
}
}