@message-queue-toolkit/core
Version:
Useful utilities, interfaces and base classes for message queue handling. Supports AMQP and SQS with a common abstraction on top currently
103 lines • 3.9 kB
JavaScript
import { randomUUID } from 'node:crypto';
import { isObject } from '@lokalise/node-core';
import { Fifo } from 'toad-cache';
import { objectMatches } from "../utils/matchUtils.js";
export function isHandlerSpy(value) {
return (isObject(value) &&
(value instanceof HandlerSpy || value.name === 'HandlerSpy'));
}
export class HandlerSpy {
name = 'HandlerSpy';
// biome-ignore lint/suspicious/noExplicitAny: This is expected
messageBuffer;
messageIdField;
messageTypeField;
spyPromises;
constructor(params = {}) {
this.messageBuffer = new Fifo(params.bufferSize ?? 100);
// @ts-ignore
this.messageIdField = params.messageIdField ?? 'id';
// @ts-ignore
this.messageTypeField = params.messageTypeField ?? 'type';
this.spyPromises = [];
}
messageMatchesFilter(spyResult, fields, status) {
return (objectMatches(fields, spyResult.message) &&
(!status || spyResult.processingResult.status === status));
}
waitForMessageWithId(id, status) {
return this.waitForMessage(
// @ts-expect-error
{ [this.messageIdField]: id }, status);
}
checkForMessage(expectedFields, status) {
return Object.values(this.messageBuffer.items).find((spyResult) => {
return this.messageMatchesFilter(spyResult.value, expectedFields, status);
})?.value;
}
waitForMessage(expectedFields, status) {
const processedMessageEntry = this.checkForMessage(expectedFields, status);
if (processedMessageEntry) {
return Promise.resolve(processedMessageEntry);
}
let resolve;
const spyPromise = new Promise((_resolve) => {
resolve = _resolve;
});
this.spyPromises.push({
promise: spyPromise,
status,
fields: expectedFields,
// @ts-ignore
resolve,
});
// @ts-ignore
return spyPromise;
}
clear() {
this.messageBuffer.clear();
}
addProcessedMessage(processingResult, messageId) {
const resolvedMessageId = processingResult.message?.[this.messageIdField] ?? messageId ?? randomUUID();
const resolvedMessageType = processingResult.message?.[this.messageTypeField] ?? 'FAILED_TO_RESOLVE';
// If we failed to parse message, let's store id and type at least
const resolvedProcessingResult = processingResult.message
? processingResult
: {
...processingResult,
message: {
[this.messageIdField]: messageId,
[this.messageTypeField]: resolvedMessageType,
},
};
// @ts-ignore
const cacheId = `${resolvedMessageId}-${Date.now()}-${(Math.random() + 1)
.toString(36)
.substring(7)}`;
this.messageBuffer.set(cacheId, resolvedProcessingResult);
const foundPromise = this.spyPromises.find((spyPromise) => {
return this.messageMatchesFilter(resolvedProcessingResult, spyPromise.fields, spyPromise.status);
});
if (foundPromise) {
foundPromise.resolve(processingResult);
const index = this.spyPromises.indexOf(foundPromise);
if (index > -1) {
// only splice array when item is found
this.spyPromises.splice(index, 1); // 2nd parameter means remove one item only
}
}
}
}
export function resolveHandlerSpy(queueOptions) {
if (isHandlerSpy(queueOptions.handlerSpy)) {
return queueOptions.handlerSpy;
}
if (!queueOptions.handlerSpy) {
return undefined;
}
if (queueOptions.handlerSpy === true) {
return new HandlerSpy();
}
return new HandlerSpy(queueOptions.handlerSpy);
}
//# sourceMappingURL=HandlerSpy.js.map