UNPKG

@micro.ts/core

Version:

Microservice framework with Typescript

244 lines (243 loc) 9.23 kB
"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;