gcf-common-lib 0.28.61 → 0.29.62
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/.github/workflows/npm-publish.yml +6 -6
- package/package.json +14 -15
- package/src/amqp-helper.js +44 -0
- package/src/amqp-helper.ts +52 -0
- package/src/index.js +65 -54
- package/src/index.ts +72 -60
- package/src/mongo-lock.js +1 -0
- package/src/utils.js +1 -42
- package/src/utils.ts +0 -52
- package/tsconfig.json +1 -1
|
@@ -12,10 +12,10 @@ jobs:
|
|
|
12
12
|
build:
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
16
|
-
- uses: actions/setup-node@
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
17
|
with:
|
|
18
|
-
node-version:
|
|
18
|
+
node-version: 18
|
|
19
19
|
- run: npm ci
|
|
20
20
|
- run: npm test
|
|
21
21
|
|
|
@@ -23,10 +23,10 @@ jobs:
|
|
|
23
23
|
needs: build
|
|
24
24
|
runs-on: ubuntu-latest
|
|
25
25
|
steps:
|
|
26
|
-
- uses: actions/checkout@
|
|
27
|
-
- uses: actions/setup-node@
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-node@v4
|
|
28
28
|
with:
|
|
29
|
-
node-version:
|
|
29
|
+
node-version: 18
|
|
30
30
|
registry-url: https://registry.npmjs.org/
|
|
31
31
|
- run: npm ci
|
|
32
32
|
- run: npm publish
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gcf-common-lib",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.29.62",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"branches": [
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
]
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=
|
|
12
|
+
"node": ">=18"
|
|
13
13
|
},
|
|
14
14
|
"main": "src/index",
|
|
15
15
|
"scripts": {
|
|
@@ -20,31 +20,30 @@
|
|
|
20
20
|
"url": "https://github.com/TopTechnologies/gcf-common.git"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@types/amqplib": "^0.10.
|
|
23
|
+
"@types/amqplib": "^0.10.5",
|
|
24
24
|
"@types/bluebird": "^3.5.42",
|
|
25
|
-
"@types/lodash": "^4.
|
|
26
|
-
"@types/node": "^
|
|
27
|
-
"@google-cloud/pubsub": "^4.
|
|
28
|
-
"@google-cloud/
|
|
29
|
-
"@google-cloud/storage": "^7.7.0",
|
|
25
|
+
"@types/lodash": "^4.17.0",
|
|
26
|
+
"@types/node": "^18.19.26",
|
|
27
|
+
"@google-cloud/pubsub": "^4.3.3",
|
|
28
|
+
"@google-cloud/storage": "^7.9.0",
|
|
30
29
|
"amqplib": "^0.10.3",
|
|
31
30
|
"bluebird": "^3.7.2",
|
|
32
31
|
"lodash": "^4.17.21",
|
|
33
32
|
"moment": "^2.30.1",
|
|
34
|
-
"mongodb": "^4.17.
|
|
33
|
+
"mongodb": "^4.17.2",
|
|
35
34
|
"rxjs": "^7.8.1"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
39
|
-
"@typescript-eslint/parser": "^
|
|
40
|
-
"@tsconfig/
|
|
41
|
-
"eslint": "^8.
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
|
38
|
+
"@typescript-eslint/parser": "^7.3.1",
|
|
39
|
+
"@tsconfig/node18": "^18.2.2",
|
|
40
|
+
"eslint": "^8.57.0",
|
|
42
41
|
"eslint-import-resolver-typescript": "^3.6.1",
|
|
43
|
-
"eslint-plugin-import": "^2.29.
|
|
42
|
+
"eslint-plugin-import": "^2.29.1",
|
|
44
43
|
"eslint-plugin-lodash": "^7.4.0",
|
|
45
44
|
"eslint-plugin-promise": "^6.1.1",
|
|
46
45
|
"eslint-plugin-rxjs": "^5.0.3",
|
|
47
|
-
"eslint-plugin-unicorn": "^
|
|
46
|
+
"eslint-plugin-unicorn": "^51.0.1"
|
|
48
47
|
},
|
|
49
48
|
"author": "alert83@gmail.com",
|
|
50
49
|
"license": ""
|
|
@@ -0,0 +1,44 @@
|
|
|
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.AmqpHelper = void 0;
|
|
7
|
+
const amqplib_1 = require("amqplib");
|
|
8
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
9
|
+
exports.AmqpHelper = {
|
|
10
|
+
async withAmqpConn(fn, url) {
|
|
11
|
+
function withDisposer() {
|
|
12
|
+
return bluebird_1.default.method(async () => {
|
|
13
|
+
const amqpConn = await (0, amqplib_1.connect)(url);
|
|
14
|
+
amqpConn.on('close', () => console.info('Amqp connection closed!'));
|
|
15
|
+
return amqpConn;
|
|
16
|
+
})().disposer((conn, promise) => conn.close());
|
|
17
|
+
}
|
|
18
|
+
return bluebird_1.default.using(withDisposer(), conn => fn(conn));
|
|
19
|
+
},
|
|
20
|
+
async withAmqpCh(fn, url, useConfirmChannel = false, prefetch = 1) {
|
|
21
|
+
return exports.AmqpHelper.withAmqpConn(async (conn) => {
|
|
22
|
+
function withDisposer() {
|
|
23
|
+
return bluebird_1.default.method(async () => {
|
|
24
|
+
const ch = useConfirmChannel ? await conn.createConfirmChannel() : await conn.createChannel();
|
|
25
|
+
await ch.prefetch(prefetch);
|
|
26
|
+
return ch;
|
|
27
|
+
})().disposer((ch, promise) => ch.close());
|
|
28
|
+
}
|
|
29
|
+
return bluebird_1.default.using(withDisposer(), ch => fn(ch));
|
|
30
|
+
}, url);
|
|
31
|
+
},
|
|
32
|
+
async publishAmqp(ch, exchange, routingKey, json, options) {
|
|
33
|
+
const payload = Buffer.from(JSON.stringify(json));
|
|
34
|
+
const keepSending = exchange
|
|
35
|
+
? ch.publish(exchange, routingKey, payload, options)
|
|
36
|
+
: ch.sendToQueue(routingKey, payload, options);
|
|
37
|
+
if (!keepSending)
|
|
38
|
+
await new Promise(resolve => ch.once('drain', () => resolve()));
|
|
39
|
+
},
|
|
40
|
+
async sendToQueueConfAmqp(ch, queue, json, options) {
|
|
41
|
+
const payload = Buffer.from(JSON.stringify(json));
|
|
42
|
+
await new Promise((resolve, reject) => ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))));
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Channel, ConfirmChannel, connect, Connection, Options } from 'amqplib';
|
|
2
|
+
import Bluebird from 'bluebird';
|
|
3
|
+
import Dict = NodeJS.Dict;
|
|
4
|
+
|
|
5
|
+
export const AmqpHelper = {
|
|
6
|
+
async withAmqpConn(fn: (conn: Connection) => Promise<any>, url: string) {
|
|
7
|
+
function withDisposer() {
|
|
8
|
+
return Bluebird.method(async () => {
|
|
9
|
+
const amqpConn = await connect(url);
|
|
10
|
+
amqpConn.on('close', () => console.info('Amqp connection closed!'));
|
|
11
|
+
return amqpConn;
|
|
12
|
+
})().disposer((conn, promise) => conn.close());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return Bluebird.using(withDisposer(), conn => fn(conn));
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async withAmqpCh(fn: (ch: Channel) => Promise<any>, url: string, useConfirmChannel = false, prefetch = 1) {
|
|
19
|
+
return AmqpHelper.withAmqpConn(async conn => {
|
|
20
|
+
function withDisposer() {
|
|
21
|
+
return Bluebird.method(async () => {
|
|
22
|
+
const ch = useConfirmChannel ? await conn.createConfirmChannel() : await conn.createChannel();
|
|
23
|
+
await ch.prefetch(prefetch);
|
|
24
|
+
return ch;
|
|
25
|
+
})().disposer((ch, promise) => ch.close());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Bluebird.using(withDisposer(), ch => fn(ch));
|
|
29
|
+
}, url);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async publishAmqp(
|
|
33
|
+
ch: Channel,
|
|
34
|
+
exchange: string | undefined,
|
|
35
|
+
routingKey: string,
|
|
36
|
+
json: Dict<any>,
|
|
37
|
+
options?: Options.Publish,
|
|
38
|
+
) {
|
|
39
|
+
const payload = Buffer.from(JSON.stringify(json));
|
|
40
|
+
const keepSending = exchange
|
|
41
|
+
? ch.publish(exchange, routingKey, payload, options)
|
|
42
|
+
: ch.sendToQueue(routingKey, payload, options);
|
|
43
|
+
if (!keepSending) await new Promise<void>(resolve => ch.once('drain', () => resolve()));
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async sendToQueueConfAmqp(ch: ConfirmChannel, queue: string, json: Dict<any>, options?: Options.Publish) {
|
|
47
|
+
const payload = Buffer.from(JSON.stringify(json));
|
|
48
|
+
await new Promise((resolve, reject) =>
|
|
49
|
+
ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))),
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
};
|
package/src/index.js
CHANGED
|
@@ -29,27 +29,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
29
29
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
30
|
};
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.GcfCommon = exports.
|
|
32
|
+
exports.GcfCommon = exports.storage = exports.pubSub = exports.MongoDb = exports.RxJs = exports.PubSub = exports.Storage = void 0;
|
|
33
33
|
const pubsub_1 = require("@google-cloud/pubsub");
|
|
34
|
-
const secret_manager_1 = require("@google-cloud/secret-manager");
|
|
35
34
|
const storage_1 = require("@google-cloud/storage");
|
|
36
35
|
const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
|
|
37
36
|
const mapValues_1 = __importDefault(require("lodash/mapValues"));
|
|
38
37
|
const noop_1 = __importDefault(require("lodash/noop"));
|
|
39
38
|
const rxjs_1 = require("rxjs");
|
|
39
|
+
const amqp_helper_1 = require("./amqp-helper");
|
|
40
40
|
const utils_1 = require("./utils");
|
|
41
41
|
exports.Storage = __importStar(require("@google-cloud/storage"));
|
|
42
42
|
exports.PubSub = __importStar(require("@google-cloud/pubsub"));
|
|
43
|
-
exports.SecretManager = __importStar(require("@google-cloud/secret-manager"));
|
|
44
43
|
exports.RxJs = __importStar(require("rxjs"));
|
|
45
44
|
exports.MongoDb = __importStar(require("mongodb"));
|
|
46
45
|
__exportStar(require("./types"), exports);
|
|
47
46
|
__exportStar(require("./utils"), exports);
|
|
48
47
|
exports.pubSub = new pubsub_1.PubSub();
|
|
49
48
|
exports.storage = new storage_1.Storage();
|
|
50
|
-
|
|
49
|
+
// export const secretClient = new SecretManagerServiceClient();
|
|
51
50
|
class GcfCommon {
|
|
52
51
|
constructor() { }
|
|
52
|
+
static amqpOptions = {
|
|
53
|
+
assertExchange: { durable: true, autoDelete: false },
|
|
54
|
+
assertOptions: { durable: true, autoDelete: true, expires: (0, utils_1.ms)({ d: 1 }) },
|
|
55
|
+
publishOptions: { persistent: true },
|
|
56
|
+
};
|
|
53
57
|
/**
|
|
54
58
|
*
|
|
55
59
|
* @param {!TEvent} event Event payload.
|
|
@@ -59,20 +63,34 @@ class GcfCommon {
|
|
|
59
63
|
*/
|
|
60
64
|
static async process(event, context, handler = rxjs_1.identity, timeoutSec = 535) {
|
|
61
65
|
const asyncHandler = async (_event, _context) => await handler(_event, _context);
|
|
62
|
-
return (0, rxjs_1.firstValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(asyncHandler(event, context))).pipe((0, rxjs_1.first)(), (0, rxjs_1.timeout)({ first: (0, utils_1.ms)({ s: timeoutSec }) })))
|
|
63
|
-
}
|
|
64
|
-
static async processOLd(event, context, handler = rxjs_1.identity, timeoutSec = 535) {
|
|
65
|
-
return Promise.race([(0, utils_1.timeoutAfter)(timeoutSec), handler(event, context)])
|
|
66
|
+
return (0, rxjs_1.firstValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(asyncHandler(event, context))).pipe((0, rxjs_1.first)(), (0, rxjs_1.timeout)({ first: (0, utils_1.ms)({ s: timeoutSec }) })))
|
|
66
67
|
.then(async (res) => {
|
|
67
68
|
// console.log('res:', res);
|
|
68
|
-
await this.
|
|
69
|
+
await this.response(event, context, res).catch(noop_1.default);
|
|
69
70
|
return res;
|
|
70
71
|
})
|
|
71
72
|
.catch(async (error) => {
|
|
72
|
-
await this.
|
|
73
|
+
await this.response(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop_1.default);
|
|
73
74
|
throw error;
|
|
74
75
|
});
|
|
75
76
|
}
|
|
77
|
+
// static async processOLd<T extends TResponse, E = TEvent>(
|
|
78
|
+
// event: E,
|
|
79
|
+
// context: TContext,
|
|
80
|
+
// handler: (event: E, context: TContext) => Promise<T | E> | T | E = identity,
|
|
81
|
+
// timeoutSec = 535,
|
|
82
|
+
// ) {
|
|
83
|
+
// return Promise.race([timeoutAfter(timeoutSec), handler(event, context)])
|
|
84
|
+
// .then(async res => {
|
|
85
|
+
// // console.log('res:', res);
|
|
86
|
+
// await this.response(event, context, res as T);
|
|
87
|
+
// return res;
|
|
88
|
+
// })
|
|
89
|
+
// .catch(async (error: Error) => {
|
|
90
|
+
// await this.response(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop);
|
|
91
|
+
// throw error;
|
|
92
|
+
// });
|
|
93
|
+
// }
|
|
76
94
|
static buildResponse(error) {
|
|
77
95
|
return {
|
|
78
96
|
error: {
|
|
@@ -82,10 +100,15 @@ class GcfCommon {
|
|
|
82
100
|
},
|
|
83
101
|
};
|
|
84
102
|
}
|
|
85
|
-
static async
|
|
86
|
-
console.time('safeGetAttributes');
|
|
87
|
-
const { topic, exchange, queue, consumer_id, request_id, app_id, env } = await this.safeGetAttributes(
|
|
88
|
-
|
|
103
|
+
static async response(event, context, json, attributes) {
|
|
104
|
+
// console.time('safeGetAttributes');
|
|
105
|
+
// const { topic, exchange, queue, consumer_id, request_id, app_id, env } = await this.safeGetAttributes(
|
|
106
|
+
// event,
|
|
107
|
+
// context,
|
|
108
|
+
// ['consumer_id', 'topic', 'exchange', 'queue'],
|
|
109
|
+
// );
|
|
110
|
+
// console.timeEnd('safeGetAttributes');
|
|
111
|
+
const { topic, exchange, queue, consumer_id, request_id, app_id, env } = this.getMetadataOrAttribute(event, context);
|
|
89
112
|
//
|
|
90
113
|
console.time('publish');
|
|
91
114
|
if (topic && !(0, isEmpty_1.default)(topic)) {
|
|
@@ -103,9 +126,9 @@ class GcfCommon {
|
|
|
103
126
|
}
|
|
104
127
|
if (exchange && !(0, isEmpty_1.default)(exchange)) {
|
|
105
128
|
console.log('send:', exchange, queue, app_id, env, json, attributes);
|
|
106
|
-
await
|
|
129
|
+
await amqp_helper_1.AmqpHelper.withAmqpCh(async (ch) => {
|
|
107
130
|
await ch.assertExchange(exchange, 'direct', this.amqpOptions.assertExchange);
|
|
108
|
-
await
|
|
131
|
+
await amqp_helper_1.AmqpHelper.publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
109
132
|
...this.amqpOptions.publishOptions,
|
|
110
133
|
correlationId: request_id,
|
|
111
134
|
});
|
|
@@ -113,9 +136,9 @@ class GcfCommon {
|
|
|
113
136
|
}
|
|
114
137
|
else if (queue && !(0, isEmpty_1.default)(queue)) {
|
|
115
138
|
console.log('send:', queue, app_id, env, json, attributes);
|
|
116
|
-
await
|
|
139
|
+
await amqp_helper_1.AmqpHelper.withAmqpCh(async (ch) => {
|
|
117
140
|
// await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
118
|
-
await
|
|
141
|
+
await amqp_helper_1.AmqpHelper.publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
119
142
|
...this.amqpOptions.publishOptions,
|
|
120
143
|
correlationId: request_id,
|
|
121
144
|
});
|
|
@@ -123,28 +146,31 @@ class GcfCommon {
|
|
|
123
146
|
}
|
|
124
147
|
console.timeEnd('publish');
|
|
125
148
|
}
|
|
126
|
-
static async safeGetAttributes(event, context, props) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
// static async safeGetAttributes<E = TEvent>(event: E, context: TContext, props: string[]) {
|
|
150
|
+
// let metaOrAttr = this.getMetadataOrAttribute(event, context);
|
|
151
|
+
// // const everyPropIsNil = props.map(prop => get(metaOrAttr, prop)).every(v => isNil(v));
|
|
152
|
+
//
|
|
153
|
+
// const someProp = props.map(prop => get(metaOrAttr, prop)).some(v => !isNil(v));
|
|
154
|
+
// // if no prop then check file metadata
|
|
155
|
+
// if (!someProp && context?.resource?.type === 'storage#object') {
|
|
156
|
+
// console.log('get metadata from file');
|
|
157
|
+
// if (context?.eventType === 'google.storage.object.finalize') {
|
|
158
|
+
// const gsEvent = event as TGSEvent;
|
|
159
|
+
// const [meta] = await new Storage().bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
|
|
160
|
+
// metaOrAttr = meta?.metadata ?? {};
|
|
161
|
+
// }
|
|
162
|
+
// }
|
|
163
|
+
//
|
|
164
|
+
// return {
|
|
165
|
+
// ...metaOrAttr,
|
|
166
|
+
// app_id: metaOrAttr.app_id,
|
|
167
|
+
// request_id: metaOrAttr.request_id,
|
|
168
|
+
// } as TMetadataOrAttributes;
|
|
169
|
+
// }
|
|
145
170
|
static async getOptions(event, context) {
|
|
146
|
-
const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
147
|
-
|
|
171
|
+
// const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
172
|
+
const { options } = this.getMetadataOrAttribute(event, context);
|
|
173
|
+
return (0, utils_1.safeJsonParse)(options, {});
|
|
148
174
|
}
|
|
149
175
|
static getMetadataOrAttribute(event, context) {
|
|
150
176
|
let metadataOrAttribute;
|
|
@@ -162,20 +188,5 @@ class GcfCommon {
|
|
|
162
188
|
}
|
|
163
189
|
return metadataOrAttribute ?? {};
|
|
164
190
|
}
|
|
165
|
-
static async getSecret(name, version) {
|
|
166
|
-
const projectId = await exports.secretClient.getProjectId();
|
|
167
|
-
const secretName = `projects/${projectId}/secrets/${name}`;
|
|
168
|
-
const secretVersion = `${secretName}/versions/${version ?? 'latest'}`;
|
|
169
|
-
const [response] = await exports.secretClient.accessSecretVersion({ name: secretVersion });
|
|
170
|
-
return response?.payload?.data?.toString();
|
|
171
|
-
}
|
|
172
|
-
static async getSecrets(names, versions) {
|
|
173
|
-
return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch(noop_1.default)));
|
|
174
|
-
}
|
|
175
191
|
}
|
|
176
192
|
exports.GcfCommon = GcfCommon;
|
|
177
|
-
GcfCommon.amqpOptions = {
|
|
178
|
-
assertExchange: { durable: true, autoDelete: false },
|
|
179
|
-
assertOptions: { durable: true, autoDelete: true, expires: (0, utils_1.ms)({ d: 1 }) },
|
|
180
|
-
publishOptions: { persistent: true },
|
|
181
|
-
};
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { PubSub } from '@google-cloud/pubsub';
|
|
2
|
-
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
|
3
2
|
import { Storage } from '@google-cloud/storage';
|
|
4
3
|
import { Options } from 'amqplib';
|
|
5
4
|
import isEmpty from 'lodash/isEmpty';
|
|
6
5
|
import mapValues from 'lodash/mapValues';
|
|
7
6
|
import noop from 'lodash/noop';
|
|
8
7
|
import { defer, first, firstValueFrom, from, identity, timeout } from 'rxjs';
|
|
8
|
+
import { AmqpHelper } from './amqp-helper';
|
|
9
9
|
import { TContext, TEvent, TGSEvent, TMetadataOrAttributes, TPSEvent, TResponse } from './types';
|
|
10
|
-
import { ms,
|
|
10
|
+
import { ms, safeJsonParse } from './utils';
|
|
11
11
|
import Dict = NodeJS.Dict;
|
|
12
12
|
|
|
13
13
|
export * as Storage from '@google-cloud/storage';
|
|
14
14
|
export * as PubSub from '@google-cloud/pubsub';
|
|
15
|
-
export * as SecretManager from '@google-cloud/secret-manager';
|
|
16
15
|
export * as RxJs from 'rxjs';
|
|
17
16
|
export * as MongoDb from 'mongodb';
|
|
18
17
|
export * from './types';
|
|
@@ -20,7 +19,7 @@ export * from './utils';
|
|
|
20
19
|
|
|
21
20
|
export const pubSub = new PubSub();
|
|
22
21
|
export const storage = new Storage();
|
|
23
|
-
export const secretClient = new SecretManagerServiceClient();
|
|
22
|
+
// export const secretClient = new SecretManagerServiceClient();
|
|
24
23
|
|
|
25
24
|
export class GcfCommon {
|
|
26
25
|
constructor() {}
|
|
@@ -52,27 +51,36 @@ export class GcfCommon {
|
|
|
52
51
|
const asyncHandler = async (_event: any, _context: any) => await handler(_event, _context);
|
|
53
52
|
return firstValueFrom(
|
|
54
53
|
defer(() => from(asyncHandler(event, context))).pipe(first(), timeout({ first: ms({ s: timeoutSec }) })),
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static async processOLd<T extends TResponse, E = TEvent>(
|
|
59
|
-
event: E,
|
|
60
|
-
context: TContext,
|
|
61
|
-
handler: (event: E, context: TContext) => Promise<T | E> | T | E = identity,
|
|
62
|
-
timeoutSec = 535,
|
|
63
|
-
) {
|
|
64
|
-
return Promise.race([timeoutAfter(timeoutSec), handler(event, context)])
|
|
54
|
+
)
|
|
65
55
|
.then(async res => {
|
|
66
56
|
// console.log('res:', res);
|
|
67
|
-
await this.
|
|
57
|
+
await this.response(event, context, res as T).catch(noop);
|
|
68
58
|
return res;
|
|
69
59
|
})
|
|
70
60
|
.catch(async (error: Error) => {
|
|
71
|
-
await this.
|
|
61
|
+
await this.response(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop);
|
|
72
62
|
throw error;
|
|
73
63
|
});
|
|
74
64
|
}
|
|
75
65
|
|
|
66
|
+
// static async processOLd<T extends TResponse, E = TEvent>(
|
|
67
|
+
// event: E,
|
|
68
|
+
// context: TContext,
|
|
69
|
+
// handler: (event: E, context: TContext) => Promise<T | E> | T | E = identity,
|
|
70
|
+
// timeoutSec = 535,
|
|
71
|
+
// ) {
|
|
72
|
+
// return Promise.race([timeoutAfter(timeoutSec), handler(event, context)])
|
|
73
|
+
// .then(async res => {
|
|
74
|
+
// // console.log('res:', res);
|
|
75
|
+
// await this.response(event, context, res as T);
|
|
76
|
+
// return res;
|
|
77
|
+
// })
|
|
78
|
+
// .catch(async (error: Error) => {
|
|
79
|
+
// await this.response(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop);
|
|
80
|
+
// throw error;
|
|
81
|
+
// });
|
|
82
|
+
// }
|
|
83
|
+
|
|
76
84
|
static buildResponse(error: Error) {
|
|
77
85
|
return {
|
|
78
86
|
error: {
|
|
@@ -83,17 +91,20 @@ export class GcfCommon {
|
|
|
83
91
|
} as TResponse;
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
static async
|
|
87
|
-
console.time('safeGetAttributes');
|
|
94
|
+
static async response<E = TEvent>(event: E, context: TContext, json?: TResponse, attributes?: Dict<any>) {
|
|
95
|
+
// console.time('safeGetAttributes');
|
|
96
|
+
// const { topic, exchange, queue, consumer_id, request_id, app_id, env } = await this.safeGetAttributes(
|
|
97
|
+
// event,
|
|
98
|
+
// context,
|
|
99
|
+
// ['consumer_id', 'topic', 'exchange', 'queue'],
|
|
100
|
+
// );
|
|
101
|
+
// console.timeEnd('safeGetAttributes');
|
|
88
102
|
|
|
89
|
-
const { topic, exchange, queue, consumer_id, request_id, app_id, env } =
|
|
103
|
+
const { topic, exchange, queue, consumer_id, request_id, app_id, env } = this.getMetadataOrAttribute(
|
|
90
104
|
event,
|
|
91
105
|
context,
|
|
92
|
-
['consumer_id', 'topic', 'exchange', 'queue'],
|
|
93
106
|
);
|
|
94
107
|
|
|
95
|
-
console.timeEnd('safeGetAttributes');
|
|
96
|
-
|
|
97
108
|
//
|
|
98
109
|
|
|
99
110
|
console.time('publish');
|
|
@@ -114,18 +125,18 @@ export class GcfCommon {
|
|
|
114
125
|
|
|
115
126
|
if (exchange && !isEmpty(exchange)) {
|
|
116
127
|
console.log('send:', exchange, queue, app_id, env, json, attributes);
|
|
117
|
-
await withAmqpCh(async ch => {
|
|
128
|
+
await AmqpHelper.withAmqpCh(async ch => {
|
|
118
129
|
await ch.assertExchange(exchange, 'direct', this.amqpOptions.assertExchange);
|
|
119
|
-
await publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
130
|
+
await AmqpHelper.publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
120
131
|
...this.amqpOptions.publishOptions,
|
|
121
132
|
correlationId: request_id,
|
|
122
133
|
});
|
|
123
134
|
}, this.amqpOptions.url as string);
|
|
124
135
|
} else if (queue && !isEmpty(queue)) {
|
|
125
136
|
console.log('send:', queue, app_id, env, json, attributes);
|
|
126
|
-
await withAmqpCh(async ch => {
|
|
137
|
+
await AmqpHelper.withAmqpCh(async ch => {
|
|
127
138
|
// await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
128
|
-
await publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
139
|
+
await AmqpHelper.publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
129
140
|
...this.amqpOptions.publishOptions,
|
|
130
141
|
correlationId: request_id,
|
|
131
142
|
});
|
|
@@ -135,31 +146,32 @@ export class GcfCommon {
|
|
|
135
146
|
console.timeEnd('publish');
|
|
136
147
|
}
|
|
137
148
|
|
|
138
|
-
static async safeGetAttributes<E = TEvent>(event: E, context: TContext, props: string[]) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
149
|
+
// static async safeGetAttributes<E = TEvent>(event: E, context: TContext, props: string[]) {
|
|
150
|
+
// let metaOrAttr = this.getMetadataOrAttribute(event, context);
|
|
151
|
+
// // const everyPropIsNil = props.map(prop => get(metaOrAttr, prop)).every(v => isNil(v));
|
|
152
|
+
//
|
|
153
|
+
// const someProp = props.map(prop => get(metaOrAttr, prop)).some(v => !isNil(v));
|
|
154
|
+
// // if no prop then check file metadata
|
|
155
|
+
// if (!someProp && context?.resource?.type === 'storage#object') {
|
|
156
|
+
// console.log('get metadata from file');
|
|
157
|
+
// if (context?.eventType === 'google.storage.object.finalize') {
|
|
158
|
+
// const gsEvent = event as TGSEvent;
|
|
159
|
+
// const [meta] = await new Storage().bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
|
|
160
|
+
// metaOrAttr = meta?.metadata ?? {};
|
|
161
|
+
// }
|
|
162
|
+
// }
|
|
163
|
+
//
|
|
164
|
+
// return {
|
|
165
|
+
// ...metaOrAttr,
|
|
166
|
+
// app_id: metaOrAttr.app_id,
|
|
167
|
+
// request_id: metaOrAttr.request_id,
|
|
168
|
+
// } as TMetadataOrAttributes;
|
|
169
|
+
// }
|
|
159
170
|
|
|
160
171
|
static async getOptions(event: TEvent, context: TContext): Promise<Dict<any>> {
|
|
161
|
-
const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
162
|
-
|
|
172
|
+
// const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
173
|
+
const { options } = this.getMetadataOrAttribute(event, context);
|
|
174
|
+
return safeJsonParse(options as any, {} as any);
|
|
163
175
|
}
|
|
164
176
|
|
|
165
177
|
static getMetadataOrAttribute<E = TEvent>(event: E, context: TContext) {
|
|
@@ -181,15 +193,15 @@ export class GcfCommon {
|
|
|
181
193
|
return metadataOrAttribute ?? {};
|
|
182
194
|
}
|
|
183
195
|
|
|
184
|
-
static async getSecret(name: string, version?: string) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
static async getSecrets(names: string[], versions?: string[]) {
|
|
193
|
-
|
|
194
|
-
}
|
|
196
|
+
// static async getSecret(name: string, version?: string) {
|
|
197
|
+
// const projectId = await secretClient.getProjectId();
|
|
198
|
+
// const secretName = `projects/${projectId}/secrets/${name}`;
|
|
199
|
+
// const secretVersion = `${secretName}/versions/${version ?? 'latest'}`;
|
|
200
|
+
// const [response] = await secretClient.accessSecretVersion({ name: secretVersion });
|
|
201
|
+
// return response?.payload?.data?.toString();
|
|
202
|
+
// }
|
|
203
|
+
//
|
|
204
|
+
// static async getSecrets(names: string[], versions?: string[]) {
|
|
205
|
+
// return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch(noop)));
|
|
206
|
+
// }
|
|
195
207
|
}
|
package/src/mongo-lock.js
CHANGED
package/src/utils.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
const amqplib_1 = require("amqplib");
|
|
8
|
-
const bluebird_1 = __importDefault(require("bluebird"));
|
|
3
|
+
exports.safeJsonParse = exports.A1ToColNum = exports.colNumToA1 = exports.A1ToIndex = exports.indexToA1 = exports.sec = exports.ms = exports.delay = exports.timeoutAfter = void 0;
|
|
9
4
|
/**
|
|
10
5
|
*
|
|
11
6
|
* @param seconds Google function v1 timeout limit (max: 9 min)
|
|
@@ -90,39 +85,3 @@ function safeJsonParse(value, fallbackValue) {
|
|
|
90
85
|
}
|
|
91
86
|
exports.safeJsonParse = safeJsonParse;
|
|
92
87
|
//
|
|
93
|
-
async function withAmqpConn(fn, url) {
|
|
94
|
-
function withDisposer() {
|
|
95
|
-
return bluebird_1.default.method(async () => {
|
|
96
|
-
const amqpConn = await (0, amqplib_1.connect)(url);
|
|
97
|
-
amqpConn.on('close', () => console.info('Amqp connection closed!'));
|
|
98
|
-
return amqpConn;
|
|
99
|
-
})().disposer((conn, promise) => conn.close());
|
|
100
|
-
}
|
|
101
|
-
return bluebird_1.default.using(withDisposer(), conn => fn(conn));
|
|
102
|
-
}
|
|
103
|
-
exports.withAmqpConn = withAmqpConn;
|
|
104
|
-
async function withAmqpCh(fn, url, useConfirmChannel = false) {
|
|
105
|
-
return withAmqpConn(async (conn) => {
|
|
106
|
-
function withDisposer() {
|
|
107
|
-
return bluebird_1.default.method(async () => {
|
|
108
|
-
return useConfirmChannel ? conn.createConfirmChannel() : conn.createChannel();
|
|
109
|
-
})().disposer((ch, promise) => ch.close());
|
|
110
|
-
}
|
|
111
|
-
return bluebird_1.default.using(withDisposer(), ch => fn(ch));
|
|
112
|
-
}, url);
|
|
113
|
-
}
|
|
114
|
-
exports.withAmqpCh = withAmqpCh;
|
|
115
|
-
async function publishAmqp(ch, exchange, routingKey, json, options) {
|
|
116
|
-
const payload = Buffer.from(JSON.stringify(json));
|
|
117
|
-
const keepSending = exchange
|
|
118
|
-
? ch.publish(exchange, routingKey, payload, options)
|
|
119
|
-
: ch.sendToQueue(routingKey, payload, options);
|
|
120
|
-
if (!keepSending)
|
|
121
|
-
await new Promise(resolve => ch.once('drain', () => resolve()));
|
|
122
|
-
}
|
|
123
|
-
exports.publishAmqp = publishAmqp;
|
|
124
|
-
async function sendToQueueConfAmqp(ch, queue, json, options) {
|
|
125
|
-
const payload = Buffer.from(JSON.stringify(json));
|
|
126
|
-
await new Promise((resolve, reject) => ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))));
|
|
127
|
-
}
|
|
128
|
-
exports.sendToQueueConfAmqp = sendToQueueConfAmqp;
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Channel, ConfirmChannel, connect, Connection, Options } from 'amqplib';
|
|
2
|
-
import Bluebird from 'bluebird';
|
|
3
1
|
import Dict = NodeJS.Dict;
|
|
4
2
|
|
|
5
3
|
/**
|
|
@@ -92,53 +90,3 @@ export function safeJsonParse<T = Dict<any>>(value: string, fallbackValue?: T) {
|
|
|
92
90
|
}
|
|
93
91
|
|
|
94
92
|
//
|
|
95
|
-
|
|
96
|
-
export async function withAmqpConn(fn: (conn: Connection) => Promise<any>, url: string) {
|
|
97
|
-
function withDisposer() {
|
|
98
|
-
return Bluebird.method(async () => {
|
|
99
|
-
const amqpConn = await connect(url);
|
|
100
|
-
amqpConn.on('close', () => console.info('Amqp connection closed!'));
|
|
101
|
-
return amqpConn;
|
|
102
|
-
})().disposer((conn, promise) => conn.close());
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return Bluebird.using(withDisposer(), conn => fn(conn));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export async function withAmqpCh(fn: (ch: Channel) => Promise<any>, url: string, useConfirmChannel = false) {
|
|
109
|
-
return withAmqpConn(async conn => {
|
|
110
|
-
function withDisposer() {
|
|
111
|
-
return Bluebird.method(async () => {
|
|
112
|
-
return useConfirmChannel ? conn.createConfirmChannel() : conn.createChannel();
|
|
113
|
-
})().disposer((ch, promise) => ch.close());
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return Bluebird.using(withDisposer(), ch => fn(ch));
|
|
117
|
-
}, url);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export async function publishAmqp(
|
|
121
|
-
ch: Channel,
|
|
122
|
-
exchange: string | undefined,
|
|
123
|
-
routingKey: string,
|
|
124
|
-
json: Dict<any>,
|
|
125
|
-
options?: Options.Publish,
|
|
126
|
-
) {
|
|
127
|
-
const payload = Buffer.from(JSON.stringify(json));
|
|
128
|
-
const keepSending = exchange
|
|
129
|
-
? ch.publish(exchange, routingKey, payload, options)
|
|
130
|
-
: ch.sendToQueue(routingKey, payload, options);
|
|
131
|
-
if (!keepSending) await new Promise<void>(resolve => ch.once('drain', () => resolve()));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export async function sendToQueueConfAmqp(
|
|
135
|
-
ch: ConfirmChannel,
|
|
136
|
-
queue: string,
|
|
137
|
-
json: Dict<any>,
|
|
138
|
-
options?: Options.Publish,
|
|
139
|
-
) {
|
|
140
|
-
const payload = Buffer.from(JSON.stringify(json));
|
|
141
|
-
await new Promise((resolve, reject) =>
|
|
142
|
-
ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))),
|
|
143
|
-
);
|
|
144
|
-
}
|
package/tsconfig.json
CHANGED