@avtechno/sfr 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/README.md +72 -0
- package/dist/example.mjs +33 -0
- package/dist/index.mjs +84 -0
- package/dist/logger.mjs +37 -0
- package/dist/mq.mjs +217 -0
- package/dist/sfr-pipeline.mjs +465 -0
- package/dist/templates.mjs +47 -0
- package/dist/types/example.d.mts +11 -0
- package/dist/types/index.d.mts +7 -0
- package/dist/types/logger.d.mts +3 -0
- package/dist/types/mq.d.mts +105 -0
- package/dist/types/sfr-pipeline.d.mts +33 -0
- package/dist/types/templates.d.mts +8 -0
- package/dist/types/util.d.mts +7 -0
- package/dist/util.mjs +25 -0
- package/package.json +51 -0
- package/src/example.mts +35 -0
- package/src/index.mts +99 -0
- package/src/logger.mts +52 -0
- package/src/mq.mts +236 -0
- package/src/sfr-pipeline.mts +508 -0
- package/src/templates.mts +55 -0
- package/src/types/index.d.ts +395 -0
- package/src/util.mts +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Single File Router (SFR)
|
|
2
|
+
|
|
3
|
+
### Overview
|
|
4
|
+
SFR allows the declaration of services including its controllers, handlers and validators in one single file.
|
|
5
|
+
|
|
6
|
+
Featuring a configurable Inversion of Control pattern for dependency libraries, It allows developers to easily inject dependencies on either handlers or controllers for convenient and easy access.
|
|
7
|
+
|
|
8
|
+
Due to it's self-contained and structured format, The SFR has allowed for the development of several extensions such as:
|
|
9
|
+
|
|
10
|
+
* OpenAPI Service Translator
|
|
11
|
+
|
|
12
|
+
`A plugin which also uses API-Bundler's ability to extract metadata from individual SFRs
|
|
13
|
+
It's main purpose is to translate and create service-level documentation which follows the OpenAPI Standard, this essentially opens up doors to extensive tooling support, instrumentation, automated endpoint testing, automated documentation generation, just to name a few.`
|
|
14
|
+
|
|
15
|
+
* UAC - ACM Self-Registration Scheme
|
|
16
|
+
|
|
17
|
+
`The in-house API-Bundler was designed to extract useful information from individual SFRs, termed service-artifacts, they are reported to a centralized authority through the well-documented UAC - ACM Self-Registration Scheme, this is prerogative to the grand scheme of Resource Administration.`
|
|
18
|
+
|
|
19
|
+
### Structure
|
|
20
|
+
|
|
21
|
+
A regular SFR is composed of the following objects
|
|
22
|
+
|
|
23
|
+
|Object Name|Description|
|
|
24
|
+
|-|-|
|
|
25
|
+
|CFG| configuration information relayed to the API-bundler (service-parser).|
|
|
26
|
+
|Validators|POJO representation of what values are allowed for each endpoint.|
|
|
27
|
+
|Handlers| Express middlewares which acts as the main logic block for each endpoint.|
|
|
28
|
+
|Controllers|Functions that execute calls to the database.|
|
|
29
|
+
|
|
30
|
+
### Usage
|
|
31
|
+
|
|
32
|
+
``` javascript
|
|
33
|
+
|
|
34
|
+
import sfr from "@hawkstow/sfr";
|
|
35
|
+
import express from "express";
|
|
36
|
+
|
|
37
|
+
const api_path = "api";
|
|
38
|
+
const doc_path = "docs";
|
|
39
|
+
|
|
40
|
+
const app = express();
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
Note:
|
|
44
|
+
SFR Bundler will look for two folders, named "rest" and "ws" inside the "path".
|
|
45
|
+
The placement of SFRs define the protocol that they'll use.
|
|
46
|
+
`
|
|
47
|
+
i.e:rest SFRs reside within "rest", websocket SFRs reside in "ws".
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
sfr({
|
|
51
|
+
root : "dist", //Specifies the working directory
|
|
52
|
+
path : api_path, //Specifies the API directory i.e: where SFR files reside
|
|
53
|
+
out : doc_path //Specifies the directory for the resulting OAS documents
|
|
54
|
+
}, app);
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Error-Handling
|
|
59
|
+
The library automatically handles errors on both handlers and controllers, however, care must be taken on how error-handling is done by the developer, the following table illustrates what error-handling styles are allowed for both cases.
|
|
60
|
+
|
|
61
|
+
|Handlers|Controllers|
|
|
62
|
+
|-|-|
|
|
63
|
+
|Exception Throws|Promise.resolve/reject returns|
|
|
64
|
+
|Passing Exceptions to Next fn||
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Bug Reporting
|
|
68
|
+
If you've found a bug, please file it in our Github Issues Page so we can have a look at it, perhaps fix it XD
|
|
69
|
+
|
|
70
|
+
TODO:
|
|
71
|
+
* Multer Upload Validation
|
|
72
|
+
* Built-in Logging
|
package/dist/example.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Joi from "joi";
|
|
2
|
+
import { REST } from "./templates.mjs";
|
|
3
|
+
export default REST({
|
|
4
|
+
cfg: {
|
|
5
|
+
base_dir: "example",
|
|
6
|
+
public: false,
|
|
7
|
+
},
|
|
8
|
+
validators: {
|
|
9
|
+
"get-something": {
|
|
10
|
+
"name": Joi.string().required(),
|
|
11
|
+
"age": Joi.number().required(),
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
handlers: {
|
|
15
|
+
GET: {
|
|
16
|
+
"get-something": {
|
|
17
|
+
description: "Get something I guess?",
|
|
18
|
+
async fn(req, res) {
|
|
19
|
+
const result = await this.db_call();
|
|
20
|
+
res.status(200).json({
|
|
21
|
+
data: result
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
controllers: {
|
|
28
|
+
async db_call() {
|
|
29
|
+
/* Perform database calls here through accessing the "this" context (if injections are set) */
|
|
30
|
+
return "Something";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/// <reference path="types/index.d.ts" />
|
|
2
|
+
import yaml from "js-yaml";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import { REST, WS, MQ } from "./templates.mjs";
|
|
6
|
+
import { SFRPipeline } from "./sfr-pipeline.mjs";
|
|
7
|
+
import { MQLib } from "./mq.mjs";
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
export default async (cfg, oas_cfg, connectors, base_url) => {
|
|
10
|
+
//TODO: Verify connectors
|
|
11
|
+
const sfr = new SFRPipeline(cfg, oas_cfg, connectors);
|
|
12
|
+
// Returned service artifacts for both OpenAPI and AsyncAPI are written into the server directory
|
|
13
|
+
// An express static endpoint is pointed to this directory for service directory.
|
|
14
|
+
const documents = await sfr.init(base_url);
|
|
15
|
+
write_service_discovery(cfg, documents); //Writes services to output dir (cfg.out)
|
|
16
|
+
return {
|
|
17
|
+
...oas_cfg,
|
|
18
|
+
documents
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
async function write_service_discovery(cfg, documents) {
|
|
22
|
+
//Setup files in case they do not exist.
|
|
23
|
+
//Setup output folder
|
|
24
|
+
await fs.mkdir(path.join(cwd, cfg.out), { recursive: true });
|
|
25
|
+
// Loop over each protocol
|
|
26
|
+
for (let [protocol, documentation] of Object.entries(documents)) {
|
|
27
|
+
//Strictly await for setup to create dirs for each protocol.
|
|
28
|
+
await fs.mkdir(path.join(cwd, cfg.out, protocol.toLowerCase()), { recursive: true });
|
|
29
|
+
//Convert documentation into service manifest.
|
|
30
|
+
//OpenAPI Documents : paths
|
|
31
|
+
//AsyncAPI Documents : channels
|
|
32
|
+
switch (protocol) {
|
|
33
|
+
case "REST":
|
|
34
|
+
{
|
|
35
|
+
documentation = documentation;
|
|
36
|
+
for (const [path, methods] of Object.entries(documentation.paths)) {
|
|
37
|
+
write_to_file(cfg, `rest/${path}`, methods);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
case "WS":
|
|
42
|
+
{
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case "MQ": {
|
|
46
|
+
documentation = documentation;
|
|
47
|
+
write_to_file(cfg, "mq/index.yaml", documentation);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//Tasked with recursively creating directories and spec files.
|
|
53
|
+
async function write_to_file(cfg, dir, data) {
|
|
54
|
+
//Path is a slash(/) delimited string, with the end delimiter indicating the filename to be used.
|
|
55
|
+
const paths = dir.split("/");
|
|
56
|
+
//Indicates a root file
|
|
57
|
+
if (paths.length === 1) {
|
|
58
|
+
await fs.writeFile(path.join(cwd, cfg.out, paths[0]), yaml.dump(data), { flag: "w+" });
|
|
59
|
+
}
|
|
60
|
+
else { //Indicates a nested file
|
|
61
|
+
const file = paths.pop(); //Pops the path array to be used for dir creation
|
|
62
|
+
await fs.mkdir(path.join(cwd, cfg.out, ...paths), { recursive: true });
|
|
63
|
+
fs.writeFile(path.join(cwd, cfg.out, ...paths, `${file}.yml`), yaml.dump(data), { flag: "w+" });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function inject(injections) {
|
|
67
|
+
SFRPipeline.injections.rest.handlers = {
|
|
68
|
+
...SFRPipeline.injections.rest.handlers,
|
|
69
|
+
...injections.handlers
|
|
70
|
+
};
|
|
71
|
+
SFRPipeline.injections.rest.controllers = {
|
|
72
|
+
...SFRPipeline.injections.rest.controllers,
|
|
73
|
+
...injections.controllers
|
|
74
|
+
};
|
|
75
|
+
SFRPipeline.injections.mq.handlers = {
|
|
76
|
+
...SFRPipeline.injections.mq.handlers,
|
|
77
|
+
...injections.handlers
|
|
78
|
+
};
|
|
79
|
+
SFRPipeline.injections.mq.controllers = {
|
|
80
|
+
...SFRPipeline.injections.mq.controllers,
|
|
81
|
+
...injections.controllers
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export { REST, WS, MQ, MQLib, inject };
|
package/dist/logger.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
// Define log levels
|
|
3
|
+
const levels = {
|
|
4
|
+
error: 0,
|
|
5
|
+
warn: 1,
|
|
6
|
+
info: 2,
|
|
7
|
+
http: 3,
|
|
8
|
+
verbose: 4,
|
|
9
|
+
debug: 5,
|
|
10
|
+
silly: 6,
|
|
11
|
+
};
|
|
12
|
+
// Define colors for each level
|
|
13
|
+
const colors = {
|
|
14
|
+
error: 'red',
|
|
15
|
+
warn: 'yellow',
|
|
16
|
+
info: 'green',
|
|
17
|
+
http: 'magenta',
|
|
18
|
+
verbose: 'cyan',
|
|
19
|
+
debug: 'blue',
|
|
20
|
+
silly: 'gray',
|
|
21
|
+
};
|
|
22
|
+
// Add colors to Winston
|
|
23
|
+
winston.addColors(colors);
|
|
24
|
+
// Define the format for logs
|
|
25
|
+
const format = winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), winston.format.colorize({ all: true }), winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}${info.metadata ? ` ${JSON.stringify(info.metadata)}` : ''}`));
|
|
26
|
+
// Create the Winston logger
|
|
27
|
+
const logger = winston.createLogger({
|
|
28
|
+
level: process.env.NODE_ENV === 'development' && Boolean(process.env.DEBUG_SFR) ? 'debug' : 'info',
|
|
29
|
+
levels,
|
|
30
|
+
format,
|
|
31
|
+
transports: [
|
|
32
|
+
// Console transport
|
|
33
|
+
new winston.transports.Console(),
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
// Export the logger
|
|
37
|
+
export { logger };
|
package/dist/mq.mjs
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { connect } from "amqplib";
|
|
2
|
+
/**
|
|
3
|
+
* MQLib class for establishing a connection to the message queue and creating channels.
|
|
4
|
+
*/
|
|
5
|
+
export class MQLib {
|
|
6
|
+
connection;
|
|
7
|
+
channel;
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the connection and channel to the message queue.
|
|
10
|
+
*
|
|
11
|
+
* @param MQ_URL - The URL of the message queue.
|
|
12
|
+
* @example
|
|
13
|
+
* const mqLib = new MQLib();
|
|
14
|
+
* await mqLib.init("amqp://localhost");
|
|
15
|
+
*/
|
|
16
|
+
async init(MQ_URL) {
|
|
17
|
+
await connect(MQ_URL)
|
|
18
|
+
.then(async (v) => {
|
|
19
|
+
//@ts-ignore
|
|
20
|
+
this.connection = v;
|
|
21
|
+
this.channel = await v.createChannel();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns the connection object.
|
|
26
|
+
*
|
|
27
|
+
* @returns The connection object to the message queue.
|
|
28
|
+
*/
|
|
29
|
+
get_connection() {
|
|
30
|
+
return this.connection;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns the channel object.
|
|
34
|
+
*
|
|
35
|
+
* @returns The channel object to the message queue.
|
|
36
|
+
*/
|
|
37
|
+
get_channel() {
|
|
38
|
+
return this.channel;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* BroadcastMQ class to handle different types of broadcast communication patterns: Publish-Subscribe, Routing, and Topic.
|
|
43
|
+
*/
|
|
44
|
+
export class BroadcastMQ {
|
|
45
|
+
channel;
|
|
46
|
+
type;
|
|
47
|
+
constructor(channel, type) {
|
|
48
|
+
this.channel = channel;
|
|
49
|
+
this.type = type;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Publishes a message to the specified exchange based on the communication pattern.
|
|
53
|
+
*
|
|
54
|
+
* @param exchange - The name of the exchange to send the message to.
|
|
55
|
+
* @param key - The routing key or pattern to use, depending on the communication pattern.
|
|
56
|
+
* @param payload - The message to be sent.
|
|
57
|
+
* @param options - Additional publishing options.
|
|
58
|
+
* @example
|
|
59
|
+
* // Publish a message to a 'logsExchange' with a routing key 'error' for a routing pattern.
|
|
60
|
+
* const mq = new BroadcastMQ(channel, "Routing");
|
|
61
|
+
* mq.publish("logsExchange", "error", { level: "error", message: "Something went wrong" });
|
|
62
|
+
*/
|
|
63
|
+
async publish(exchange, key, payload, options) {
|
|
64
|
+
switch (this.type) {
|
|
65
|
+
case "Publish-Subscribe": {
|
|
66
|
+
await this.channel.assertExchange(exchange, "fanout", { durable: false });
|
|
67
|
+
this.channel.publish(exchange, "", encode_payload(payload), options);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "Routing": {
|
|
71
|
+
await this.channel.assertExchange(exchange, "direct", { durable: false });
|
|
72
|
+
this.channel.publish(exchange, key, encode_payload(payload), options);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "Topic": {
|
|
76
|
+
await this.channel.assertExchange(exchange, "topic", { durable: false });
|
|
77
|
+
this.channel.publish(exchange, key, encode_payload(payload), options);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Subscribes to an exchange to receive messages based on the communication pattern.
|
|
84
|
+
*
|
|
85
|
+
* @param exchange - The name of the exchange to subscribe to.
|
|
86
|
+
* @param key - The routing key or pattern to listen for.
|
|
87
|
+
* @param cfg - Configuration options for the consumer, including the callback function.
|
|
88
|
+
* @param options - Additional consumption options.
|
|
89
|
+
* @example
|
|
90
|
+
* // Subscribe to the 'logsExchange' for all 'error' messages in the Routing pattern.
|
|
91
|
+
* const mq = new BroadcastMQ(channel, "Routing");
|
|
92
|
+
* mq.subscribe("logsExchange", "error", { fn: handleErrorLogs, options: { noAck: true } });
|
|
93
|
+
*/
|
|
94
|
+
async subscribe(exchange, key, cfg, options) {
|
|
95
|
+
let fn = cfg.fn.bind({ channel: this.channel, type: this.type });
|
|
96
|
+
switch (this.type) {
|
|
97
|
+
case "Publish-Subscribe": {
|
|
98
|
+
await this.channel.assertExchange(exchange, "fanout", { durable: false });
|
|
99
|
+
const { queue } = await this.channel.assertQueue("", { exclusive: true });
|
|
100
|
+
this.channel.bindQueue(queue, exchange, "");
|
|
101
|
+
this.channel.consume(queue, (v) => fn(parse_payload(v)), cfg.options);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "Routing": {
|
|
105
|
+
await this.channel.assertExchange(exchange, "direct", { durable: false });
|
|
106
|
+
const { queue } = await this.channel.assertQueue("", { exclusive: true });
|
|
107
|
+
await this.channel.bindQueue(queue, exchange, key);
|
|
108
|
+
this.channel.consume(queue, (v) => fn(parse_payload(v)), options);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "Topic": {
|
|
112
|
+
await this.channel.assertExchange(exchange, "topic", { durable: false });
|
|
113
|
+
const { queue } = await this.channel.assertQueue("", { exclusive: true });
|
|
114
|
+
this.channel.bindQueue(queue, exchange, key);
|
|
115
|
+
this.channel.consume(queue, (v) => fn(parse_payload(v)), options);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* TargetedMQ class to handle Point-to-Point and Request-Reply communication patterns.
|
|
123
|
+
*/
|
|
124
|
+
export class TargetedMQ {
|
|
125
|
+
channel;
|
|
126
|
+
type;
|
|
127
|
+
constructor(channel, type) {
|
|
128
|
+
this.channel = channel;
|
|
129
|
+
this.type = type;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Sends a message to the specified queue depending on the communication pattern.
|
|
133
|
+
*
|
|
134
|
+
* @param binding - The name of the queue or binding to send the message to.
|
|
135
|
+
* @param payload - The message to be sent.
|
|
136
|
+
* @param options - Additional publishing options.
|
|
137
|
+
* @example
|
|
138
|
+
* // Send a message to a queue for point-to-point communication
|
|
139
|
+
* const mq = new TargetedMQ(channel, "Point-to-Point");
|
|
140
|
+
* mq.produce("taskQueue", { taskId: 1, action: "process" });
|
|
141
|
+
*/
|
|
142
|
+
async produce(binding, payload, options) {
|
|
143
|
+
/* Alter produce strategy depending on the instance's configured comm pattern */
|
|
144
|
+
switch (this.type) {
|
|
145
|
+
case "Point-to-Point": {
|
|
146
|
+
await this.channel.assertQueue(binding);
|
|
147
|
+
return this.channel.sendToQueue(binding, encode_payload(payload), options);
|
|
148
|
+
}
|
|
149
|
+
case "Request-Reply": {
|
|
150
|
+
await this.channel.assertQueue(binding);
|
|
151
|
+
return this.channel.sendToQueue(binding, encode_payload(payload), options);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Sends a reply to the message sender in a Request-Reply pattern.
|
|
157
|
+
*
|
|
158
|
+
* @param msg - The original message to reply to.
|
|
159
|
+
* @param payload - The reply message to send back to the requester.
|
|
160
|
+
* @example
|
|
161
|
+
* // Send a response back to the client in a Request-Reply pattern
|
|
162
|
+
* const mq = new TargetedMQ(channel, "Request-Reply");
|
|
163
|
+
* mq.reply(msg, { result: "Processed successfully" });
|
|
164
|
+
*/
|
|
165
|
+
async reply(msg, payload) {
|
|
166
|
+
if (this.type !== "Request-Reply")
|
|
167
|
+
return;
|
|
168
|
+
this.channel.sendToQueue(msg.properties.replyTo, encode_payload(payload));
|
|
169
|
+
this.channel.ack(msg);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Consumes messages from the specified queue based on the communication pattern.
|
|
173
|
+
*
|
|
174
|
+
* @param queue - The name of the queue to consume messages from.
|
|
175
|
+
* @param cfg - Configuration options for the consumer, including the callback function.
|
|
176
|
+
* @example
|
|
177
|
+
* // Consume a message from the 'taskQueue' in a Point-to-Point communication pattern
|
|
178
|
+
* const mq = new TargetedMQ(channel, "Point-to-Point");
|
|
179
|
+
* mq.consume("taskQueue", { fn: processTask, options: { noAck: true } });
|
|
180
|
+
*/
|
|
181
|
+
async consume(queue, cfg) {
|
|
182
|
+
let fn = cfg.fn.bind({ channel: this.channel, type: this.type });
|
|
183
|
+
switch (this.type) {
|
|
184
|
+
case "Point-to-Point": {
|
|
185
|
+
await this.channel.assertQueue(queue);
|
|
186
|
+
this.channel.consume(queue, (v) => fn(parse_payload(v)), cfg.options);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case "Request-Reply": {
|
|
190
|
+
await this.channel.assertQueue(queue);
|
|
191
|
+
this.channel.consume(queue, (v) => fn(parse_payload(v)), cfg.options);
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/* Helper Functions */
|
|
198
|
+
/**
|
|
199
|
+
* Parses the payload of a message.
|
|
200
|
+
*
|
|
201
|
+
* @param msg - The consumed message.
|
|
202
|
+
* @returns The parsed message content.
|
|
203
|
+
*/
|
|
204
|
+
function parse_payload(msg) {
|
|
205
|
+
if (!msg)
|
|
206
|
+
return {};
|
|
207
|
+
return { ...msg, content: JSON.parse(msg.content.toString()) };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Encodes a message as a Buffer to be sent over the message queue.
|
|
211
|
+
*
|
|
212
|
+
* @param msg - The message to be encoded.
|
|
213
|
+
* @returns The encoded message as a Buffer.
|
|
214
|
+
*/
|
|
215
|
+
function encode_payload(msg) {
|
|
216
|
+
return Buffer.from(JSON.stringify(msg));
|
|
217
|
+
}
|