@micro.ts/core
Version:
Microservice framework with Typescript
244 lines (243 loc) • 9.23 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AmqpClient = void 0;
const uuid_1 = require("uuid");
const di_1 = require("../../di");
const Logger_1 = require("../../server/Logger");
const BaseHelpers_1 = require("../../helpers/BaseHelpers");
class AmqpClient {
constructor(existingHooks, clientOptions) {
this.existingHooks = existingHooks;
this.clientOptions = clientOptions;
this.rpcCallbacks = {};
}
getPayload(r) {
return __awaiter(this, void 0, void 0, function* () {
const headers = r.properties.headers || {};
const isJson = !!headers['json'];
const isGzip = headers['Content-Encoding'] === 'gzip';
const messageBytes = r.content;
let messageString = r.content.toString();
if (isGzip) {
const unzippedBytes = yield (0, BaseHelpers_1.unzipAsync)(messageBytes);
messageString = unzippedBytes.toString();
}
if (isJson) {
return JSON.parse(messageString);
}
return messageString;
});
}
convertPayload(payload, requestHeaders) {
return __awaiter(this, void 0, void 0, function* () {
const isJson = requestHeaders['json'] || (!!payload && payload instanceof Object);
if (isJson && !requestHeaders['json']) {
requestHeaders['json'] = true;
}
const isGzip = requestHeaders['Content-Encoding'] === 'gzip';
let payloadString = '';
if (isJson) {
payloadString = JSON.stringify(payload);
}
else {
payloadString = payload.toString();
}
if (isGzip) {
const gzipBytes = yield (0, BaseHelpers_1.zipAsync)(payloadString);
return gzipBytes;
}
return Buffer.from(payloadString);
});
}
/**
* Return full rpcQueue name
*/
get baseRpcQueue() {
if (this.clientOptions.unique) {
if (!this.uniqueId) {
this.uniqueId = (0, uuid_1.v4)();
}
/**
* Default queue name + uuid4
*/
return `${this.clientOptions.rpcQueue || 'rpc'}.${this.uniqueId}`;
}
/**
* If not unique, return simple rpc
*/
return this.clientOptions.rpcQueue || 'rpc';
}
init() {
return __awaiter(this, void 0, void 0, function* () {
if (this.clientOptions.newChannel) {
this.channel = yield this.existingHooks
.getConnection()
.createChannel();
}
else {
this.channel = this.existingHooks.getChannel();
}
const defaultQueueOptions = {
durable: false,
autoDelete: true,
};
const queueOptions = Object.assign(Object.assign({}, defaultQueueOptions), (this.clientOptions.rpcQueueOptions || {}));
yield this.channel.assertQueue(this.baseRpcQueue, queueOptions);
yield this.channel.consume(this.baseRpcQueue, (msg) => {
this.consumeRpcMessage(msg);
});
di_1.Container.get(Logger_1.LoggerKey).info('AMQP Client Started');
});
}
/**
* Handle if received a message on the default RPC queue
* @param msg
*/
consumeRpcMessage(msg) {
if (!msg) {
return;
}
if (msg.properties.correlationId) {
const correlationId = msg.properties.correlationId;
/**
* Check if callback exists
*/
if (this.rpcCallbacks[correlationId]) {
try {
/**
* Check message headers for error
*/
if (msg.properties.headers.error) {
// Return error
this.rpcCallbacks[correlationId](this.getPayload(msg));
}
else {
/**
* Return success
*/
this.rpcCallbacks[correlationId](null, this.getPayload(msg));
}
}
catch (err) {
/**
* Catch if any other exception is thrown during parsing
*/
this.rpcCallbacks[correlationId](err);
}
/**
* Acknowledge message if handled
*/
this.channel.ack(msg);
/**
* Delete the callback after is handled
*/
delete this.rpcCallbacks[correlationId];
}
else {
/**
* Acknowledge message if the callback to handle it is removed
*/
if (this.clientOptions.unique) {
this.channel.ack(msg);
}
else {
/**
* Requeue message if not handled and the queue is not unique
*/
this.channel.nack(msg, false, true);
}
}
}
else {
/**
* Acknowledge message if it doesn't have correlation id
*/
this.channel.ack(msg);
}
}
/**
* RPC requests, publishes message on the given queue and waits for a response on the default RPC queue
* @param exchange
* @param routingKey
* @param payload
* @param timeout
* @param options
*/
rpc(exchange, routingKey, payload, options) {
return __awaiter(this, void 0, void 0, function* () {
const correlationId = (0, uuid_1.v4)();
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
/**
* Callback to register on rpc reply or timeout error
* @param err
* @param payload
*/
this.rpcCallbacks[correlationId] = (err, payload) => {
if (err) {
reject(err);
}
else {
resolve(payload);
}
};
// const timeout = options!.
let timeout = 10 * 60 * 1000;
if (options && options.expiration) {
timeout = options.expiration;
}
/**
* After called check if the callback still exists and reject it with the timeout message
*/
setTimeout(() => {
if (this.rpcCallbacks[correlationId]) {
this.rpcCallbacks[correlationId](new Error('RPC TIMEOUT'));
delete this.rpcCallbacks[correlationId];
}
}, timeout);
/**
* Send message and wait for reply
*/
yield this.publish(exchange, routingKey, payload, Object.assign(Object.assign({}, (options || {})), { replyTo: this.baseRpcQueue, correlationId: correlationId }));
}));
});
}
/**
* Send message directly to the specified queue
* @param queue
* @param payload
* @param options
*/
sendToQueue(queue, payload, options) {
return __awaiter(this, void 0, void 0, function* () {
options = options || {};
options.headers = options.headers || {};
const bytes = yield this.convertPayload(payload, options.headers);
this.channel.sendToQueue(queue, bytes, options);
});
}
/**
* Publish message to an exchange
* @param exchange
* @param payload
* @param routingKey
* @param options
*/
publish(exchange, routingKey = '', payload, options = undefined) {
return __awaiter(this, void 0, void 0, function* () {
options = options || {};
options.headers = options.headers || {};
const bytes = yield this.convertPayload(payload, options.headers);
this.channel.publish(exchange, routingKey, bytes, options);
});
}
}
exports.AmqpClient = AmqpClient;