rpc-mq 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { ChannelModel, RPCClient } from './types';
2
+ type Client = {
3
+ name: string;
4
+ uuid?: string;
5
+ secretKey?: string;
6
+ };
7
+ type MetaData = {
8
+ client: Client;
9
+ } & Record<string, any>;
10
+ type RPCClientProps = {
11
+ connection: ChannelModel;
12
+ metadata?: MetaData;
13
+ metadataInterceptor?: CallableFunction;
14
+ };
15
+ /**
16
+ * Initialize the rpc client and ready to be called to call
17
+ * server procedure
18
+ * @param {string} service - rpc server name to call to
19
+ * @param {MessageQueue} props - The Message Queue connection configuration
20
+ * @return {makeRequest} callback - The make request callback
21
+ */
22
+ export default function initRpcClient(service: string, props: RPCClientProps): RPCClient;
23
+ export {};
package/client.js ADDED
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = initRpcClient;
4
+ /* eslint-disable no-console */
5
+ const crypto_1 = require("crypto");
6
+ const decodeResult = (content) => {
7
+ const { result } = JSON.parse(content);
8
+ return result;
9
+ };
10
+ const getMetadata = (props) => {
11
+ const { metadata, ...restProps } = props;
12
+ const withMetadata = metadata || false;
13
+ try {
14
+ if (!withMetadata)
15
+ return Promise.resolve({});
16
+ const isFun = typeof metadata === 'function';
17
+ const result = isFun
18
+ ? metadata(restProps)
19
+ : metadata;
20
+ const isPromise = result instanceof Promise;
21
+ if (isPromise)
22
+ return result.then((pResult) => pResult);
23
+ return Promise.resolve(result);
24
+ }
25
+ catch (error) {
26
+ const { message } = error;
27
+ return Promise.reject({ ...error, message });
28
+ }
29
+ };
30
+ const closeChannel = (channel) => {
31
+ const channelInitialized = channel || false;
32
+ if (!channelInitialized)
33
+ return false;
34
+ channel.close();
35
+ return true;
36
+ };
37
+ const waitForResponse = (props) => {
38
+ const { channel, resolve, reject, queue, service, correlationId } = props;
39
+ let hasReceived = false;
40
+ channel.consume(queue.queue, (message) => {
41
+ if (message.properties.correlationId === correlationId) {
42
+ hasReceived = true;
43
+ const result = decodeResult(message.content.toString());
44
+ try {
45
+ if (!(result || false))
46
+ return resolve(result);
47
+ const { error = false } = result;
48
+ if (error)
49
+ return reject(result);
50
+ return resolve(result);
51
+ }
52
+ catch {
53
+ return resolve(result);
54
+ }
55
+ finally {
56
+ closeChannel(channel);
57
+ }
58
+ }
59
+ }, { noAck: true });
60
+ setTimeout(() => {
61
+ if (!hasReceived) {
62
+ closeChannel(channel);
63
+ reject(new Error(`- [rpc-client] Request to ${service} timeout`));
64
+ }
65
+ }, 1000 * 5);
66
+ };
67
+ const consume = (props, procedure, ...params) => {
68
+ const { channel, queue, service, metadata, metadataInterceptor } = props;
69
+ const correlationId = (0, crypto_1.randomUUID)();
70
+ return new Promise((resolve, reject) => {
71
+ const metadataProps = { metadata, service, procedure, params };
72
+ getMetadata(metadataProps)
73
+ .then((cleanMetadata) => {
74
+ const intercepted = metadataInterceptor || false;
75
+ if (!intercepted)
76
+ return cleanMetadata;
77
+ return metadataInterceptor(cleanMetadata);
78
+ })
79
+ .then((metadataData) => {
80
+ waitForResponse({
81
+ channel, resolve, reject,
82
+ queue, service, correlationId,
83
+ });
84
+ const metadata = { ...metadataData, correlationId };
85
+ const data = JSON.stringify({ procedure, params, metadata });
86
+ channel.sendToQueue(service, Buffer.from(data), {
87
+ correlationId,
88
+ replyTo: queue.queue,
89
+ });
90
+ })
91
+ .catch((error) => {
92
+ closeChannel(channel);
93
+ const { message } = error;
94
+ reject({ message, code: 'UNKNOWN_ERROR', status: 400, ...error });
95
+ });
96
+ });
97
+ };
98
+ const initRequest = (props, procedure, ...params) => {
99
+ const { connection, service, metadata, metadataInterceptor } = props;
100
+ return connection.createChannel()
101
+ .then((channel) => {
102
+ console.log('[RPC Client] Channel is created.');
103
+ return channel
104
+ .assertQueue('', {
105
+ exclusive: true,
106
+ autoDelete: true,
107
+ })
108
+ .then((queue) => {
109
+ const consumeProps = { channel, service, queue, metadata, metadataInterceptor };
110
+ return consume(consumeProps, procedure, ...params);
111
+ });
112
+ });
113
+ };
114
+ /**
115
+ * Initialize the rpc client and ready to be called to call
116
+ * server procedure
117
+ * @param {string} service - rpc server name to call to
118
+ * @param {MessageQueue} props - The Message Queue connection configuration
119
+ * @return {makeRequest} callback - The make request callback
120
+ */
121
+ function initRpcClient(service, props) {
122
+ const { connection, metadata, metadataInterceptor } = props;
123
+ return (procedure, ...params) => {
124
+ const reqProps = { connection, service, metadata, metadataInterceptor };
125
+ return initRequest(reqProps, procedure, ...params);
126
+ };
127
+ }
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { default as client } from './client';
2
+ export { default as server } from './server';
3
+ export * from './types';
package/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.server = exports.client = void 0;
21
+ var client_1 = require("./client");
22
+ Object.defineProperty(exports, "client", { enumerable: true, get: function () { return __importDefault(client_1).default; } });
23
+ var server_1 = require("./server");
24
+ Object.defineProperty(exports, "server", { enumerable: true, get: function () { return __importDefault(server_1).default; } });
25
+ __exportStar(require("./types"), exports);
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "rpc-mq",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "build": "tsc --build",
8
+ "test": "jest --roots=./__test__",
9
+ "start:dev": "tsc --build -f ./tsconfig.json -w",
10
+ "test:dev": "npm run test -- --watchAll",
11
+ "dev": "npm run build -- -w",
12
+ "jsdoc": "npm run build && jsdoc build/**/* -d jsdoc",
13
+ "eslint": "eslint src --ext .ts"
14
+ },
15
+ "author": "",
16
+ "license": "ISC"
17
+ }
@@ -0,0 +1,3 @@
1
+ type Handlers = Record<string, CallableFunction>;
2
+ export default function getHandler(handlers: Handlers | any, props: any): Handlers;
3
+ export {};
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = getHandler;
4
+ let singleton;
5
+ function getHandler(handlers, props) {
6
+ if (singleton || false)
7
+ return singleton;
8
+ /* eslint-disable-next-line no-console */
9
+ console.log('[INFO] Registering RPC handlers...');
10
+ const isFunction = handlers instanceof Function;
11
+ if (!isFunction) {
12
+ singleton = handlers;
13
+ return singleton;
14
+ }
15
+ singleton = handlers(props);
16
+ return singleton;
17
+ }
@@ -0,0 +1,32 @@
1
+ import type { ChannelModel } from 'amqplib';
2
+ import { type Channel } from '../types';
3
+ import { type Middleware } from './middleware';
4
+ import type { CallProps } from './type';
5
+ type ConsumeProps = {
6
+ channel: Channel;
7
+ queueName: string;
8
+ handlers: any;
9
+ middlewares?: any;
10
+ };
11
+ /**
12
+ * Starts consuming the message queue. This just
13
+ * consumes on specific queueue (queueName)
14
+ * @param {ConsumeProps} props - Consuming properties
15
+ * @return Proise
16
+ */
17
+ export declare const startConsuming: (props: ConsumeProps) => Promise<import("amqplib").Replies.Consume>;
18
+ type RPCServer = {
19
+ middlewares?: Middleware[];
20
+ connection: ChannelModel;
21
+ logger?: any;
22
+ };
23
+ type Handler = Record<string, CallableFunction>;
24
+ type HandlerWrapper = (_props: CallProps) => Handler;
25
+ /**
26
+ * Starts the Consumer of RabbitMQ
27
+ * @param {string} service - String to rpc server name
28
+ * @param {Record<string, unknown>} exposedServices - The exposed functions to be called
29
+ * @param {MessageQueue} config - The message queue connection configuration
30
+ */
31
+ export default function startRpcServer(service: string, exposedServices: Handler | HandlerWrapper, props: RPCServer): Promise<import("amqplib").Replies.Consume>;
32
+ export {};
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startConsuming = void 0;
7
+ exports.default = startRpcServer;
8
+ const middleware_1 = __importDefault(require("./middleware"));
9
+ const storage_1 = __importDefault(require("../storage"));
10
+ const logger_1 = __importDefault(require("./logger"));
11
+ const handler_1 = __importDefault(require("./handler"));
12
+ let loggerPrv;
13
+ /**
14
+ * Tries to execute the requested function
15
+ * and returns the value back to the client.
16
+ * @param {callback} handler - Callback function of the called function
17
+ * @param {any[]} params - Function parameters
18
+ * @return any | error
19
+ */
20
+ const execute = (handler, params) => {
21
+ try {
22
+ const result = handler(...params);
23
+ const isPromise = result instanceof Promise;
24
+ if (isPromise)
25
+ return result;
26
+ return Promise.resolve(result);
27
+ }
28
+ catch (error) {
29
+ const { message } = error;
30
+ return Promise.resolve({ ...error, error: true, message });
31
+ }
32
+ };
33
+ const sendResponse = (props, result) => {
34
+ const { channel, properties, metadata, params, procedure } = props;
35
+ const loggerProps = { result, metadata, params, procedure };
36
+ (0, logger_1.default)(loggerPrv, loggerProps);
37
+ channel.sendToQueue(properties.replyTo, Buffer.from(JSON.stringify({ result })), { correlationId: properties.correlationId });
38
+ };
39
+ const sendErrorResponse = (props, errorRes) => sendResponse(props, errorRes);
40
+ /**
41
+ * Starts consuming the message queue. This just
42
+ * consumes on specific queueue (queueName)
43
+ * @param {ConsumeProps} props - Consuming properties
44
+ * @return Proise
45
+ */
46
+ const startConsuming = (props) => {
47
+ const { channel, queueName, handlers: pHandlers, middlewares = [] } = props;
48
+ return channel.consume(queueName, (message) => {
49
+ const { procedure, params, metadata: resHeaders } = JSON.parse(message.content.toString());
50
+ const { properties } = message;
51
+ const metadata = (0, storage_1.default)(resHeaders);
52
+ const handlers = (0, handler_1.default)(pHandlers, { metadata });
53
+ const handler = handlers[procedure] || false;
54
+ const resProps = { channel, metadata, params, procedure, properties };
55
+ if (!handler) {
56
+ const notFoundMsg = {
57
+ error: true,
58
+ status: 404,
59
+ message: `Function/Procedure [${procedure}] does not exist`,
60
+ code: 'PROCEDURE_NOT_FOUND',
61
+ };
62
+ sendErrorResponse(resProps, notFoundMsg);
63
+ channel.ack(message);
64
+ return true;
65
+ }
66
+ return (0, middleware_1.default)(middlewares, { procedure, params, metadata })
67
+ .then(() => execute(handler, params))
68
+ .then((promiseResult) => sendResponse(resProps, promiseResult))
69
+ .catch((error) => {
70
+ const errorMsg = {
71
+ error: true,
72
+ message: error.message,
73
+ status: error.status || 400,
74
+ code: error.code || 'UNKNOWN_ERROR',
75
+ };
76
+ return sendErrorResponse(resProps, errorMsg);
77
+ })
78
+ .finally(() => {
79
+ channel.ack(message);
80
+ });
81
+ });
82
+ };
83
+ exports.startConsuming = startConsuming;
84
+ /**
85
+ * Starts the Consumer of RabbitMQ
86
+ * @param {string} service - String to rpc server name
87
+ * @param {Record<string, unknown>} exposedServices - The exposed functions to be called
88
+ * @param {MessageQueue} config - The message queue connection configuration
89
+ */
90
+ function startRpcServer(service, exposedServices, props) {
91
+ const { connection, middlewares, logger } = props;
92
+ loggerPrv = logger || false;
93
+ return connection.createChannel()
94
+ .then((channel) => {
95
+ channel.assertQueue(service, { durable: false });
96
+ channel.prefetch(1);
97
+ return (0, exports.startConsuming)({
98
+ channel,
99
+ middlewares,
100
+ queueName: service,
101
+ handlers: exposedServices,
102
+ });
103
+ });
104
+ }
@@ -0,0 +1 @@
1
+ export default function loggerCallback(logger: any, props: any): boolean;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /* esltin-disable console.log */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.default = loggerCallback;
5
+ const encode = (params) => {
6
+ const content = JSON.stringify(params || []);
7
+ return Buffer.from(content).toString('base64');
8
+ };
9
+ function writeLog(props) {
10
+ return new Promise((resolve) => {
11
+ const { procedure, metadata, result } = props;
12
+ const { client = {}, correlationId } = metadata.get();
13
+ const info = [
14
+ '[REQUEST-LOG]',
15
+ `[${(((result === null || result === void 0 ? void 0 : result.error) || false) ? 'FAILED' : 'SUCCESS')}]`,
16
+ procedure,
17
+ `client/${client.name || 'N/A'}`,
18
+ `correlation/${correlationId}`,
19
+ ];
20
+ /* eslint-disable-next-line no-console */
21
+ console.log(info.join(' '));
22
+ resolve(true);
23
+ });
24
+ }
25
+ function loggerCallback(logger, props) {
26
+ const { result, params, procedure, metadata } = props;
27
+ writeLog({ procedure, metadata, result });
28
+ const hasLogger = logger || false;
29
+ if (!hasLogger)
30
+ return false;
31
+ const { correlationId, client = {} } = metadata.get();
32
+ const encodedParams = encode(params);
33
+ const logInfo = {
34
+ error: (result === null || result === void 0 ? void 0 : result.error) || false,
35
+ procedure,
36
+ params: encodedParams,
37
+ correlation: correlationId,
38
+ client: client.name || 'N/A',
39
+ };
40
+ logger().log(logInfo, { severity: 'request-log' });
41
+ return true;
42
+ }
@@ -0,0 +1,10 @@
1
+ import type { Storage } from '../storage';
2
+ import type { CallProps } from './type';
3
+ export type Middleware = (_props: CallProps) => any | void;
4
+ type MiddlewaresExecutor = {
5
+ procedure: string;
6
+ params: any;
7
+ metadata: Storage;
8
+ };
9
+ export default function executeMiddlewares(middlewares: Middleware[] | undefined, props: MiddlewaresExecutor): any;
10
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = executeMiddlewares;
4
+ function executeHell(callbacks, props) {
5
+ if (callbacks.length === 0)
6
+ return {};
7
+ const [callback, ...rest] = callbacks;
8
+ const isPromise = callback instanceof Promise;
9
+ const handler = isPromise
10
+ ? callback(props)
11
+ : Promise.resolve(callback(props));
12
+ return handler
13
+ .then((result = {}) => {
14
+ if (rest.length === 0)
15
+ return result;
16
+ return executeHell(rest, props);
17
+ });
18
+ }
19
+ function initializerMiddleware() {
20
+ return Promise.resolve(true);
21
+ }
22
+ function executeMiddlewares(middlewares = [], props) {
23
+ const { procedure, params, metadata } = props;
24
+ if (middlewares.length == 0)
25
+ return Promise.resolve({});
26
+ return executeHell([initializerMiddleware, ...middlewares], { procedure, params, metadata });
27
+ }
@@ -0,0 +1,14 @@
1
+ import type { Storage } from '../storage';
2
+ export type CallProps = {
3
+ metadata: Storage;
4
+ };
5
+ export type ErrorResponse = {
6
+ error: boolean;
7
+ status: number;
8
+ message: string;
9
+ code: string;
10
+ };
11
+ export type Response = {
12
+ error: false;
13
+ status: 200;
14
+ };
package/server/type.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/storage.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ type StorageValue = Record<string, any>;
2
+ export type Storage = {
3
+ set: CallableFunction;
4
+ get: CallableFunction;
5
+ setItem: CallableFunction;
6
+ removeItem: CallableFunction;
7
+ };
8
+ export default function headers(value?: StorageValue): Storage;
9
+ export {};
package/storage.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = headers;
4
+ function headers(value = {}) {
5
+ let singleton = value;
6
+ return {
7
+ set: (value) => {
8
+ singleton = { ...singleton, ...value };
9
+ return singleton;
10
+ },
11
+ get: (key = false, fallbackVal = undefined) => {
12
+ if (!key)
13
+ return singleton;
14
+ return (singleton[key.toString()] || fallbackVal);
15
+ },
16
+ setItem: (key, value) => {
17
+ singleton = { ...singleton, [key]: value };
18
+ return singleton;
19
+ },
20
+ removeItem: (key) => {
21
+ delete singleton[key];
22
+ return singleton;
23
+ },
24
+ };
25
+ }
package/types.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export type MessageQueue = {
2
+ host: string;
3
+ port: string | number;
4
+ user: string;
5
+ password: string;
6
+ retryInterval?: number;
7
+ };
8
+ export type RPCClient = (procedureName: string, ...params: any[]) => Promise<any>;
9
+ export { ChannelModel, Channel, } from 'amqplib';
package/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/utils.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function getValue(value: any, props: unknown): Promise<any>;
package/utils.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getValue = getValue;
4
+ function getValue(value, props) {
5
+ const callback = (typeof value === 'function')
6
+ ? value(props)
7
+ : value;
8
+ const isPromise = callback instanceof Promise;
9
+ if (!isPromise)
10
+ return Promise.resolve(callback);
11
+ return callback;
12
+ }