gcf-common-lib 0.28.62 → 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 +64 -62
- package/src/index.ts +70 -67
- 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.
|
|
@@ -62,26 +66,31 @@ class GcfCommon {
|
|
|
62
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 }) })))
|
|
63
67
|
.then(async (res) => {
|
|
64
68
|
// console.log('res:', res);
|
|
65
|
-
await this.
|
|
69
|
+
await this.response(event, context, res).catch(noop_1.default);
|
|
66
70
|
return res;
|
|
67
71
|
})
|
|
68
72
|
.catch(async (error) => {
|
|
69
|
-
await this.
|
|
70
|
-
throw error;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
static async processOLd(event, context, handler = rxjs_1.identity, timeoutSec = 535) {
|
|
74
|
-
return Promise.race([(0, utils_1.timeoutAfter)(timeoutSec), handler(event, context)])
|
|
75
|
-
.then(async (res) => {
|
|
76
|
-
// console.log('res:', res);
|
|
77
|
-
await this.publish(event, context, res);
|
|
78
|
-
return res;
|
|
79
|
-
})
|
|
80
|
-
.catch(async (error) => {
|
|
81
|
-
await this.publish(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop_1.default);
|
|
73
|
+
await this.response(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop_1.default);
|
|
82
74
|
throw error;
|
|
83
75
|
});
|
|
84
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
|
+
// }
|
|
85
94
|
static buildResponse(error) {
|
|
86
95
|
return {
|
|
87
96
|
error: {
|
|
@@ -91,10 +100,15 @@ class GcfCommon {
|
|
|
91
100
|
},
|
|
92
101
|
};
|
|
93
102
|
}
|
|
94
|
-
static async
|
|
95
|
-
console.time('safeGetAttributes');
|
|
96
|
-
const { topic, exchange, queue, consumer_id, request_id, app_id, env } = await this.safeGetAttributes(
|
|
97
|
-
|
|
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);
|
|
98
112
|
//
|
|
99
113
|
console.time('publish');
|
|
100
114
|
if (topic && !(0, isEmpty_1.default)(topic)) {
|
|
@@ -112,9 +126,9 @@ class GcfCommon {
|
|
|
112
126
|
}
|
|
113
127
|
if (exchange && !(0, isEmpty_1.default)(exchange)) {
|
|
114
128
|
console.log('send:', exchange, queue, app_id, env, json, attributes);
|
|
115
|
-
await
|
|
129
|
+
await amqp_helper_1.AmqpHelper.withAmqpCh(async (ch) => {
|
|
116
130
|
await ch.assertExchange(exchange, 'direct', this.amqpOptions.assertExchange);
|
|
117
|
-
await
|
|
131
|
+
await amqp_helper_1.AmqpHelper.publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
118
132
|
...this.amqpOptions.publishOptions,
|
|
119
133
|
correlationId: request_id,
|
|
120
134
|
});
|
|
@@ -122,9 +136,9 @@ class GcfCommon {
|
|
|
122
136
|
}
|
|
123
137
|
else if (queue && !(0, isEmpty_1.default)(queue)) {
|
|
124
138
|
console.log('send:', queue, app_id, env, json, attributes);
|
|
125
|
-
await
|
|
139
|
+
await amqp_helper_1.AmqpHelper.withAmqpCh(async (ch) => {
|
|
126
140
|
// await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
127
|
-
await
|
|
141
|
+
await amqp_helper_1.AmqpHelper.publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
128
142
|
...this.amqpOptions.publishOptions,
|
|
129
143
|
correlationId: request_id,
|
|
130
144
|
});
|
|
@@ -132,28 +146,31 @@ class GcfCommon {
|
|
|
132
146
|
}
|
|
133
147
|
console.timeEnd('publish');
|
|
134
148
|
}
|
|
135
|
-
static async safeGetAttributes(event, context, props) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
+
// }
|
|
154
170
|
static async getOptions(event, context) {
|
|
155
|
-
const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
156
|
-
|
|
171
|
+
// const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
172
|
+
const { options } = this.getMetadataOrAttribute(event, context);
|
|
173
|
+
return (0, utils_1.safeJsonParse)(options, {});
|
|
157
174
|
}
|
|
158
175
|
static getMetadataOrAttribute(event, context) {
|
|
159
176
|
let metadataOrAttribute;
|
|
@@ -171,20 +188,5 @@ class GcfCommon {
|
|
|
171
188
|
}
|
|
172
189
|
return metadataOrAttribute ?? {};
|
|
173
190
|
}
|
|
174
|
-
static async getSecret(name, version) {
|
|
175
|
-
const projectId = await exports.secretClient.getProjectId();
|
|
176
|
-
const secretName = `projects/${projectId}/secrets/${name}`;
|
|
177
|
-
const secretVersion = `${secretName}/versions/${version ?? 'latest'}`;
|
|
178
|
-
const [response] = await exports.secretClient.accessSecretVersion({ name: secretVersion });
|
|
179
|
-
return response?.payload?.data?.toString();
|
|
180
|
-
}
|
|
181
|
-
static async getSecrets(names, versions) {
|
|
182
|
-
return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch(noop_1.default)));
|
|
183
|
-
}
|
|
184
191
|
}
|
|
185
192
|
exports.GcfCommon = GcfCommon;
|
|
186
|
-
GcfCommon.amqpOptions = {
|
|
187
|
-
assertExchange: { durable: true, autoDelete: false },
|
|
188
|
-
assertOptions: { durable: true, autoDelete: true, expires: (0, utils_1.ms)({ d: 1 }) },
|
|
189
|
-
publishOptions: { persistent: true },
|
|
190
|
-
};
|
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() {}
|
|
@@ -55,32 +54,32 @@ export class GcfCommon {
|
|
|
55
54
|
)
|
|
56
55
|
.then(async res => {
|
|
57
56
|
// console.log('res:', res);
|
|
58
|
-
await this.
|
|
57
|
+
await this.response(event, context, res as T).catch(noop);
|
|
59
58
|
return res;
|
|
60
59
|
})
|
|
61
60
|
.catch(async (error: Error) => {
|
|
62
|
-
await this.
|
|
61
|
+
await this.response(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop);
|
|
63
62
|
throw error;
|
|
64
63
|
});
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
static async processOLd<T extends TResponse, E = TEvent>(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
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
|
+
// }
|
|
84
83
|
|
|
85
84
|
static buildResponse(error: Error) {
|
|
86
85
|
return {
|
|
@@ -92,17 +91,20 @@ export class GcfCommon {
|
|
|
92
91
|
} as TResponse;
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
static async
|
|
96
|
-
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');
|
|
97
102
|
|
|
98
|
-
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(
|
|
99
104
|
event,
|
|
100
105
|
context,
|
|
101
|
-
['consumer_id', 'topic', 'exchange', 'queue'],
|
|
102
106
|
);
|
|
103
107
|
|
|
104
|
-
console.timeEnd('safeGetAttributes');
|
|
105
|
-
|
|
106
108
|
//
|
|
107
109
|
|
|
108
110
|
console.time('publish');
|
|
@@ -123,18 +125,18 @@ export class GcfCommon {
|
|
|
123
125
|
|
|
124
126
|
if (exchange && !isEmpty(exchange)) {
|
|
125
127
|
console.log('send:', exchange, queue, app_id, env, json, attributes);
|
|
126
|
-
await withAmqpCh(async ch => {
|
|
128
|
+
await AmqpHelper.withAmqpCh(async ch => {
|
|
127
129
|
await ch.assertExchange(exchange, 'direct', this.amqpOptions.assertExchange);
|
|
128
|
-
await publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
130
|
+
await AmqpHelper.publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
129
131
|
...this.amqpOptions.publishOptions,
|
|
130
132
|
correlationId: request_id,
|
|
131
133
|
});
|
|
132
134
|
}, this.amqpOptions.url as string);
|
|
133
135
|
} else if (queue && !isEmpty(queue)) {
|
|
134
136
|
console.log('send:', queue, app_id, env, json, attributes);
|
|
135
|
-
await withAmqpCh(async ch => {
|
|
137
|
+
await AmqpHelper.withAmqpCh(async ch => {
|
|
136
138
|
// await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
137
|
-
await publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
139
|
+
await AmqpHelper.publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
138
140
|
...this.amqpOptions.publishOptions,
|
|
139
141
|
correlationId: request_id,
|
|
140
142
|
});
|
|
@@ -144,31 +146,32 @@ export class GcfCommon {
|
|
|
144
146
|
console.timeEnd('publish');
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
static async safeGetAttributes<E = TEvent>(event: E, context: TContext, props: string[]) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
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
|
+
// }
|
|
168
170
|
|
|
169
171
|
static async getOptions(event: TEvent, context: TContext): Promise<Dict<any>> {
|
|
170
|
-
const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
171
|
-
|
|
172
|
+
// const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
173
|
+
const { options } = this.getMetadataOrAttribute(event, context);
|
|
174
|
+
return safeJsonParse(options as any, {} as any);
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
static getMetadataOrAttribute<E = TEvent>(event: E, context: TContext) {
|
|
@@ -190,15 +193,15 @@ export class GcfCommon {
|
|
|
190
193
|
return metadataOrAttribute ?? {};
|
|
191
194
|
}
|
|
192
195
|
|
|
193
|
-
static async getSecret(name: string, version?: string) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
static async getSecrets(names: string[], versions?: string[]) {
|
|
202
|
-
|
|
203
|
-
}
|
|
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
|
+
// }
|
|
204
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