gcf-common-lib 0.25.53 → 0.26.55
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/.eslintrc.json +56 -0
- package/.prettierrc +2 -1
- package/package.json +16 -5
- package/src/index.js +89 -92
- package/src/index.ts +34 -34
- package/src/mongo-helper.js +27 -43
- package/src/mongo-helper.ts +13 -9
- package/src/mongo-lock.js +52 -73
- package/src/mongo-lock.ts +10 -12
- package/src/types.ts +9 -8
- package/src/utils.js +37 -58
- package/src/utils.ts +8 -8
- package/tsconfig.json +5 -10
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"env": {
|
|
4
|
+
"es2021": true,
|
|
5
|
+
"commonjs": true,
|
|
6
|
+
"mongo": true
|
|
7
|
+
},
|
|
8
|
+
"parser": "@typescript-eslint/parser",
|
|
9
|
+
"parserOptions": {
|
|
10
|
+
"ecmaVersion": "latest",
|
|
11
|
+
"sourceType": "module",
|
|
12
|
+
"tsconfigRootDir": ".",
|
|
13
|
+
"project": [
|
|
14
|
+
"./tsconfig.json"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"plugins": [
|
|
18
|
+
"@typescript-eslint",
|
|
19
|
+
"import",
|
|
20
|
+
"promise",
|
|
21
|
+
"rxjs",
|
|
22
|
+
"lodash",
|
|
23
|
+
"capture",
|
|
24
|
+
"no-closure",
|
|
25
|
+
"unicorn"
|
|
26
|
+
],
|
|
27
|
+
"extends": [
|
|
28
|
+
"eslint:recommended",
|
|
29
|
+
"plugin:@typescript-eslint/recommended",
|
|
30
|
+
"plugin:import/recommended",
|
|
31
|
+
"plugin:import/typescript",
|
|
32
|
+
"plugin:promise/recommended",
|
|
33
|
+
"plugin:rxjs/recommended",
|
|
34
|
+
"plugin:lodash/recommended",
|
|
35
|
+
"plugin:unicorn/recommended"
|
|
36
|
+
],
|
|
37
|
+
"settings": {
|
|
38
|
+
"import/resolver": {
|
|
39
|
+
"typescript": true,
|
|
40
|
+
"node": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"rules": {
|
|
44
|
+
"block-scoped-var": "error",
|
|
45
|
+
"no-loop-func": "error",
|
|
46
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
47
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
48
|
+
"@typescript-eslint/no-empty-function": "off",
|
|
49
|
+
"@typescript-eslint/no-empty-interface": "off",
|
|
50
|
+
"lodash/prefer-lodash-method": "off",
|
|
51
|
+
"rxjs/no-implicit-any-catch": "off",
|
|
52
|
+
"unicorn/prevent-abbreviations": "off",
|
|
53
|
+
"capture/explicit-closures": "error",
|
|
54
|
+
"no-closure/no-tagged-closures": "error"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/.prettierrc
CHANGED
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.26.55",
|
|
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": "16",
|
|
13
13
|
"npm": "8"
|
|
14
14
|
},
|
|
15
15
|
"main": "src/index",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@google-cloud/pubsub": "^4.0.6",
|
|
25
25
|
"@google-cloud/secret-manager": "^5.0.1",
|
|
26
|
-
"@google-cloud/storage": "^7.
|
|
26
|
+
"@google-cloud/storage": "^7.4.0",
|
|
27
27
|
"amqplib": "^0.10.3",
|
|
28
28
|
"bluebird": "^3.7.2",
|
|
29
29
|
"lodash": "^4.17.21",
|
|
@@ -32,11 +32,22 @@
|
|
|
32
32
|
"rxjs": "^7.8.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
36
|
+
"@typescript-eslint/parser": "^6.8.0",
|
|
37
|
+
"@tsconfig/node16": "^16.1.1",
|
|
36
38
|
"@types/amqplib": "^0.10.3",
|
|
37
39
|
"@types/bluebird": "^3.5.41",
|
|
38
40
|
"@types/lodash": "^4.14.200",
|
|
39
|
-
"@types/node": "^
|
|
41
|
+
"@types/node": "^16.18.59",
|
|
42
|
+
"eslint": "^8.52.0",
|
|
43
|
+
"eslint-import-resolver-typescript": "^3.6.1",
|
|
44
|
+
"eslint-plugin-capture": "^1.1.0",
|
|
45
|
+
"eslint-plugin-import": "^2.29.0",
|
|
46
|
+
"eslint-plugin-lodash": "^7.4.0",
|
|
47
|
+
"eslint-plugin-no-closure": "^1.0.1",
|
|
48
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
49
|
+
"eslint-plugin-rxjs": "^5.0.3",
|
|
50
|
+
"eslint-plugin-unicorn": "^48.0.1"
|
|
40
51
|
},
|
|
41
52
|
"author": "alert83@gmail.com",
|
|
42
53
|
"license": ""
|
package/src/index.js
CHANGED
|
@@ -25,27 +25,21 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
25
25
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
26
26
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
27
|
};
|
|
28
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
29
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
28
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
38
29
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
39
30
|
};
|
|
40
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
32
|
exports.GcfCommon = exports.secretClient = exports.storage = exports.pubSub = exports.MongoDb = exports.RxJs = exports.SecretManager = exports.PubSub = exports.Storage = void 0;
|
|
42
33
|
const pubsub_1 = require("@google-cloud/pubsub");
|
|
34
|
+
const secret_manager_1 = require("@google-cloud/secret-manager");
|
|
35
|
+
const storage_1 = require("@google-cloud/storage");
|
|
36
|
+
const get_1 = __importDefault(require("lodash/get"));
|
|
43
37
|
const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
|
|
38
|
+
const isNil_1 = __importDefault(require("lodash/isNil"));
|
|
39
|
+
const mapValues_1 = __importDefault(require("lodash/mapValues"));
|
|
44
40
|
const noop_1 = __importDefault(require("lodash/noop"));
|
|
41
|
+
const rxjs_1 = require("rxjs");
|
|
45
42
|
const utils_1 = require("./utils");
|
|
46
|
-
const storage_1 = require("@google-cloud/storage");
|
|
47
|
-
const secret_manager_1 = require("@google-cloud/secret-manager");
|
|
48
|
-
const lodash_1 = require("lodash");
|
|
49
43
|
exports.Storage = __importStar(require("@google-cloud/storage"));
|
|
50
44
|
exports.PubSub = __importStar(require("@google-cloud/pubsub"));
|
|
51
45
|
exports.SecretManager = __importStar(require("@google-cloud/secret-manager"));
|
|
@@ -57,6 +51,7 @@ exports.pubSub = new pubsub_1.PubSub();
|
|
|
57
51
|
exports.storage = new storage_1.Storage();
|
|
58
52
|
exports.secretClient = new secret_manager_1.SecretManagerServiceClient();
|
|
59
53
|
class GcfCommon {
|
|
54
|
+
constructor() { }
|
|
60
55
|
/**
|
|
61
56
|
*
|
|
62
57
|
* @param {!TEvent} event Event payload.
|
|
@@ -64,114 +59,116 @@ class GcfCommon {
|
|
|
64
59
|
* @param handler
|
|
65
60
|
* @param timeout Seconds
|
|
66
61
|
*/
|
|
67
|
-
static process(event, context, handler =
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}));
|
|
62
|
+
static async process(event, context, handler = rxjs_1.identity, timeout = 535) {
|
|
63
|
+
return Promise.race([(0, utils_1.timeoutAfter)(timeout), handler(event, context)])
|
|
64
|
+
.then(async (res) => {
|
|
65
|
+
// console.log('res:', res);
|
|
66
|
+
await this.publish(event, context, res);
|
|
67
|
+
return res;
|
|
68
|
+
})
|
|
69
|
+
.catch(async (error) => {
|
|
70
|
+
await this.publish(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop_1.default);
|
|
71
|
+
throw error;
|
|
78
72
|
});
|
|
79
73
|
}
|
|
80
74
|
static buildResponse(error) {
|
|
81
|
-
var _a, _b;
|
|
82
75
|
return {
|
|
83
76
|
error: {
|
|
84
77
|
name: error.name,
|
|
85
|
-
message: `GCF [${
|
|
78
|
+
message: `GCF [${process?.env?.K_SERVICE ?? 'UNKNOWN'}]: ${error.message}`,
|
|
86
79
|
stack: error.stack,
|
|
87
80
|
},
|
|
88
81
|
};
|
|
89
82
|
}
|
|
90
|
-
static publish(event, context, json, attributes) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
83
|
+
static async publish(event, context, json, attributes) {
|
|
84
|
+
console.time('safeGetAttributes');
|
|
85
|
+
const { topic, exchange, queue, consumer_id, request_id, app_id, env } = await this.safeGetAttributes(event, context, ['consumer_id', 'topic', 'exchange', 'queue']);
|
|
86
|
+
console.timeEnd('safeGetAttributes');
|
|
87
|
+
//
|
|
88
|
+
console.time('publish');
|
|
89
|
+
if (topic && !(0, isEmpty_1.default)(topic)) {
|
|
90
|
+
console.log('send:', topic, app_id, env, json, attributes);
|
|
91
|
+
await exports.pubSub.topic(topic).publishMessage({
|
|
92
|
+
json: json ?? {},
|
|
93
|
+
attributes: {
|
|
94
|
+
...(0, mapValues_1.default)(attributes ?? {}, v => '' + v),
|
|
95
|
+
consumer_id: consumer_id ?? '',
|
|
96
|
+
request_id: request_id ?? '',
|
|
97
|
+
app_id: app_id ?? '',
|
|
98
|
+
env: env ?? '',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (exchange && !(0, isEmpty_1.default)(exchange)) {
|
|
103
|
+
console.log('send:', exchange, queue, app_id, env, json, attributes);
|
|
104
|
+
await (0, utils_1.withAmqpCh)(async (ch) => {
|
|
105
|
+
await ch.assertExchange(exchange, 'direct', this.amqpOptions.assertExchange);
|
|
106
|
+
await (0, utils_1.publishAmqp)(ch, exchange, queue ?? '', json ?? {}, {
|
|
107
|
+
...this.amqpOptions.publishOptions,
|
|
108
|
+
correlationId: request_id,
|
|
102
109
|
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}), this.amqpOptions.url);
|
|
117
|
-
}
|
|
118
|
-
console.timeEnd('publish');
|
|
119
|
-
});
|
|
110
|
+
}, this.amqpOptions.url);
|
|
111
|
+
}
|
|
112
|
+
else if (queue && !(0, isEmpty_1.default)(queue)) {
|
|
113
|
+
console.log('send:', queue, app_id, env, json, attributes);
|
|
114
|
+
await (0, utils_1.withAmqpCh)(async (ch) => {
|
|
115
|
+
// await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
116
|
+
await (0, utils_1.publishAmqp)(ch, undefined, queue, json ?? {}, {
|
|
117
|
+
...this.amqpOptions.publishOptions,
|
|
118
|
+
correlationId: request_id,
|
|
119
|
+
});
|
|
120
|
+
}, this.amqpOptions.url);
|
|
121
|
+
}
|
|
122
|
+
console.timeEnd('publish');
|
|
120
123
|
}
|
|
121
|
-
static safeGetAttributes(event, context, props) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const [meta] = yield exports.storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
|
|
133
|
-
metaOrAttr = (_b = meta === null || meta === void 0 ? void 0 : meta.metadata) !== null && _b !== void 0 ? _b : {};
|
|
134
|
-
}
|
|
124
|
+
static async safeGetAttributes(event, context, props) {
|
|
125
|
+
let metaOrAttr = this.getMetadataOrAttribute(event, context);
|
|
126
|
+
// const everyPropIsNil = props.map(prop => get(metaOrAttr, prop)).every(v => isNil(v));
|
|
127
|
+
const someProp = props.map(prop => (0, get_1.default)(metaOrAttr, prop)).some(v => !(0, isNil_1.default)(v));
|
|
128
|
+
// if no prop then check file metadata
|
|
129
|
+
if (!someProp && context?.resource?.type === 'storage#object') {
|
|
130
|
+
console.log('get metadata from file');
|
|
131
|
+
if (context?.eventType === 'google.storage.object.finalize') {
|
|
132
|
+
const gsEvent = event;
|
|
133
|
+
const [meta] = await exports.storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
|
|
134
|
+
metaOrAttr = meta?.metadata ?? {};
|
|
135
135
|
}
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
...metaOrAttr,
|
|
139
|
+
app_id: metaOrAttr.app_id,
|
|
140
|
+
request_id: metaOrAttr.request_id,
|
|
141
|
+
};
|
|
138
142
|
}
|
|
139
|
-
static getOptions(event, context) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return JSON.parse(options !== null && options !== void 0 ? options : '{}');
|
|
143
|
-
});
|
|
143
|
+
static async getOptions(event, context) {
|
|
144
|
+
const { options } = await this.safeGetAttributes(event, context, ['options']);
|
|
145
|
+
return JSON.parse(options ?? '{}');
|
|
144
146
|
}
|
|
145
147
|
static getMetadataOrAttribute(event, context) {
|
|
146
148
|
let metadataOrAttribute;
|
|
147
|
-
switch (context
|
|
149
|
+
switch (context?.eventType) {
|
|
148
150
|
case 'google.storage.object.finalize': {
|
|
149
151
|
const gsEvent = event;
|
|
150
|
-
metadataOrAttribute = gsEvent
|
|
152
|
+
metadataOrAttribute = gsEvent?.metadata;
|
|
151
153
|
break;
|
|
152
154
|
}
|
|
153
155
|
case 'google.pubsub.topic.publish': {
|
|
154
156
|
const psEvent = event;
|
|
155
|
-
metadataOrAttribute = psEvent
|
|
157
|
+
metadataOrAttribute = psEvent?.attributes;
|
|
156
158
|
break;
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
|
-
return metadataOrAttribute
|
|
161
|
+
return metadataOrAttribute ?? {};
|
|
160
162
|
}
|
|
161
|
-
static getSecret(name, version) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const [response] = yield exports.secretClient.accessSecretVersion({ name: secretVersion });
|
|
168
|
-
return (_b = (_a = response === null || response === void 0 ? void 0 : response.payload) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.toString();
|
|
169
|
-
});
|
|
163
|
+
static async getSecret(name, version) {
|
|
164
|
+
const projectId = await exports.secretClient.getProjectId();
|
|
165
|
+
const secretName = `projects/${projectId}/secrets/${name}`;
|
|
166
|
+
const secretVersion = `${secretName}/versions/${version ?? 'latest'}`;
|
|
167
|
+
const [response] = await exports.secretClient.accessSecretVersion({ name: secretVersion });
|
|
168
|
+
return response?.payload?.data?.toString();
|
|
170
169
|
}
|
|
171
|
-
static getSecrets(names, versions) {
|
|
172
|
-
return
|
|
173
|
-
return Promise.all(names.map((name, idx) => __awaiter(this, void 0, void 0, function* () { return this.getSecret(name, versions === null || versions === void 0 ? void 0 : versions[idx]).catch(); })));
|
|
174
|
-
});
|
|
170
|
+
static async getSecrets(names, versions) {
|
|
171
|
+
return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch(noop_1.default)));
|
|
175
172
|
}
|
|
176
173
|
}
|
|
177
174
|
exports.GcfCommon = GcfCommon;
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { PubSub } from '@google-cloud/pubsub';
|
|
2
|
+
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
|
3
|
+
import { Storage } from '@google-cloud/storage';
|
|
4
|
+
import { Options } from 'amqplib';
|
|
5
|
+
import get from 'lodash/get';
|
|
2
6
|
import isEmpty from 'lodash/isEmpty';
|
|
7
|
+
import isNil from 'lodash/isNil';
|
|
8
|
+
import mapValues from 'lodash/mapValues';
|
|
3
9
|
import noop from 'lodash/noop';
|
|
4
|
-
import {
|
|
5
|
-
import { Storage } from '@google-cloud/storage';
|
|
6
|
-
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
|
10
|
+
import { identity } from 'rxjs';
|
|
7
11
|
import { TContext, TEvent, TGSEvent, TMetadataOrAttributes, TPSEvent, TResponse } from './types';
|
|
8
|
-
import {
|
|
9
|
-
import { Options } from 'amqplib';
|
|
12
|
+
import { ms, publishAmqp, timeoutAfter, withAmqpCh } from './utils';
|
|
10
13
|
import Dict = NodeJS.Dict;
|
|
11
14
|
|
|
12
15
|
export * as Storage from '@google-cloud/storage';
|
|
@@ -22,6 +25,8 @@ export const storage = new Storage();
|
|
|
22
25
|
export const secretClient = new SecretManagerServiceClient();
|
|
23
26
|
|
|
24
27
|
export class GcfCommon {
|
|
28
|
+
constructor() {}
|
|
29
|
+
|
|
25
30
|
static amqpOptions: {
|
|
26
31
|
url?: string;
|
|
27
32
|
assertExchange?: Options.AssertExchange;
|
|
@@ -43,17 +48,18 @@ export class GcfCommon {
|
|
|
43
48
|
static async process<T extends TResponse, E = TEvent>(
|
|
44
49
|
event: E,
|
|
45
50
|
context: TContext,
|
|
46
|
-
handler: (event: E, context: TContext) => Promise<T
|
|
51
|
+
handler: (event: E, context: TContext) => Promise<T | E> | T | E = identity,
|
|
47
52
|
timeout = 535,
|
|
48
53
|
) {
|
|
49
54
|
return Promise.race([timeoutAfter(timeout), handler(event, context)])
|
|
50
|
-
.then(async
|
|
55
|
+
.then(async res => {
|
|
51
56
|
// console.log('res:', res);
|
|
52
|
-
await this.publish(event, context, res);
|
|
57
|
+
await this.publish(event, context, res as T);
|
|
58
|
+
return res;
|
|
53
59
|
})
|
|
54
|
-
.catch(async (
|
|
55
|
-
await this.publish(event, context, GcfCommon.buildResponse(
|
|
56
|
-
throw
|
|
60
|
+
.catch(async (error: Error) => {
|
|
61
|
+
await this.publish(event, context, GcfCommon.buildResponse(error), { error: '1' }).catch(noop);
|
|
62
|
+
throw error;
|
|
57
63
|
});
|
|
58
64
|
}
|
|
59
65
|
|
|
@@ -70,14 +76,11 @@ export class GcfCommon {
|
|
|
70
76
|
static async publish<E = TEvent>(event: E, context: TContext, json?: TResponse, attributes?: Dict<any>) {
|
|
71
77
|
console.time('safeGetAttributes');
|
|
72
78
|
|
|
73
|
-
const {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
queue,
|
|
77
|
-
|
|
78
|
-
env,
|
|
79
|
-
'request-id': requestId,
|
|
80
|
-
} = await this.safeGetAttributes(event, context, ['topic', 'exchange', 'queue']);
|
|
79
|
+
const { topic, exchange, queue, consumer_id, request_id, app_id, env } = await this.safeGetAttributes(
|
|
80
|
+
event,
|
|
81
|
+
context,
|
|
82
|
+
['consumer_id', 'topic', 'exchange', 'queue'],
|
|
83
|
+
);
|
|
81
84
|
|
|
82
85
|
console.timeEnd('safeGetAttributes');
|
|
83
86
|
|
|
@@ -86,36 +89,35 @@ export class GcfCommon {
|
|
|
86
89
|
console.time('publish');
|
|
87
90
|
|
|
88
91
|
if (topic && !isEmpty(topic)) {
|
|
89
|
-
console.log('send:', topic,
|
|
92
|
+
console.log('send:', topic, app_id, env, json, attributes);
|
|
90
93
|
await pubSub.topic(topic).publishMessage({
|
|
91
94
|
json: json ?? {},
|
|
92
95
|
attributes: {
|
|
93
96
|
...mapValues(attributes ?? {}, v => '' + v),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
appId: appId ?? '',
|
|
97
|
+
consumer_id: consumer_id ?? '',
|
|
98
|
+
request_id: request_id ?? '',
|
|
99
|
+
app_id: app_id ?? '',
|
|
98
100
|
env: env ?? '',
|
|
99
101
|
} as TMetadataOrAttributes,
|
|
100
102
|
});
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
if (exchange && !isEmpty(exchange)) {
|
|
104
|
-
console.log('send:', exchange, queue,
|
|
106
|
+
console.log('send:', exchange, queue, app_id, env, json, attributes);
|
|
105
107
|
await withAmqpCh(async ch => {
|
|
106
108
|
await ch.assertExchange(exchange, 'direct', this.amqpOptions.assertExchange);
|
|
107
109
|
await publishAmqp(ch, exchange, queue ?? '', json ?? {}, {
|
|
108
110
|
...this.amqpOptions.publishOptions,
|
|
109
|
-
correlationId:
|
|
111
|
+
correlationId: request_id,
|
|
110
112
|
});
|
|
111
113
|
}, this.amqpOptions.url as string);
|
|
112
114
|
} else if (queue && !isEmpty(queue)) {
|
|
113
|
-
console.log('send:', queue,
|
|
115
|
+
console.log('send:', queue, app_id, env, json, attributes);
|
|
114
116
|
await withAmqpCh(async ch => {
|
|
115
|
-
await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
117
|
+
// await ch.assertQueue(queue, this.amqpOptions.assertOptions);
|
|
116
118
|
await publishAmqp(ch, undefined, queue, json ?? {}, {
|
|
117
119
|
...this.amqpOptions.publishOptions,
|
|
118
|
-
correlationId:
|
|
120
|
+
correlationId: request_id,
|
|
119
121
|
});
|
|
120
122
|
}, this.amqpOptions.url as string);
|
|
121
123
|
}
|
|
@@ -140,10 +142,8 @@ export class GcfCommon {
|
|
|
140
142
|
|
|
141
143
|
return {
|
|
142
144
|
...metaOrAttr,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
'request-id': metaOrAttr['request-id'] ?? (metaOrAttr as any).requestId,
|
|
146
|
-
requestId: metaOrAttr['request-id'] ?? (metaOrAttr as any).requestId,
|
|
145
|
+
app_id: metaOrAttr.app_id,
|
|
146
|
+
request_id: metaOrAttr.request_id,
|
|
147
147
|
} as TMetadataOrAttributes;
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -180,6 +180,6 @@ export class GcfCommon {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
static async getSecrets(names: string[], versions?: string[]) {
|
|
183
|
-
return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch()));
|
|
183
|
+
return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch(noop)));
|
|
184
184
|
}
|
|
185
185
|
}
|
package/src/mongo-helper.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -15,37 +6,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
6
|
exports.MongoHelper = void 0;
|
|
16
7
|
const mongodb_1 = require("mongodb");
|
|
17
8
|
const bluebird_1 = __importDefault(require("bluebird"));
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}))().disposer((client, promise) => client.close());
|
|
46
|
-
}
|
|
47
|
-
return bluebird_1.default.using(withDisposer(), (client) => fn(client));
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
exports.MongoHelper = MongoHelper;
|
|
9
|
+
exports.MongoHelper = {
|
|
10
|
+
async collectionExists(client, name) {
|
|
11
|
+
const db = client.db();
|
|
12
|
+
const collections = await db.listCollections({ name }, { nameOnly: true }).toArray();
|
|
13
|
+
return collections.length > 0;
|
|
14
|
+
},
|
|
15
|
+
async collectionSafeGet(client, name, options, afterCreate) {
|
|
16
|
+
const db = client.db();
|
|
17
|
+
let coll;
|
|
18
|
+
if (await this.collectionExists(client, name)) {
|
|
19
|
+
coll = db.collection(name);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
coll = await db.createCollection(name, options);
|
|
23
|
+
afterCreate && (await afterCreate(coll));
|
|
24
|
+
}
|
|
25
|
+
return coll;
|
|
26
|
+
},
|
|
27
|
+
async withMongoClient(fn, url, mongoClientOptions) {
|
|
28
|
+
function withDisposer() {
|
|
29
|
+
return bluebird_1.default.method(async () => {
|
|
30
|
+
return new mongodb_1.MongoClient(url, mongoClientOptions).connect();
|
|
31
|
+
})().disposer((client, promise) => client.close());
|
|
32
|
+
}
|
|
33
|
+
return bluebird_1.default.using(withDisposer(), client => fn(client));
|
|
34
|
+
},
|
|
35
|
+
};
|
package/src/mongo-helper.ts
CHANGED
|
@@ -2,14 +2,14 @@ import { Collection, CreateCollectionOptions, MongoClient, MongoClientOptions }
|
|
|
2
2
|
import { Document } from 'bson';
|
|
3
3
|
import Bluebird from 'bluebird';
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
5
|
+
export const MongoHelper = {
|
|
6
|
+
async collectionExists(client: MongoClient, name: string) {
|
|
7
7
|
const db = client.db();
|
|
8
8
|
const collections = await db.listCollections({ name }, { nameOnly: true }).toArray();
|
|
9
9
|
return collections.length > 0;
|
|
10
|
-
}
|
|
10
|
+
},
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
async collectionSafeGet<TSchema extends Document = Document>(
|
|
13
13
|
client: MongoClient,
|
|
14
14
|
name: string,
|
|
15
15
|
options?: CreateCollectionOptions,
|
|
@@ -26,15 +26,19 @@ export class MongoHelper {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return coll;
|
|
29
|
-
}
|
|
29
|
+
},
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
async withMongoClient<T = any>(
|
|
32
|
+
fn: (mongoClient: MongoClient) => Promise<T>,
|
|
33
|
+
url: string,
|
|
34
|
+
mongoClientOptions?: MongoClientOptions,
|
|
35
|
+
) {
|
|
32
36
|
function withDisposer() {
|
|
33
37
|
return Bluebird.method(async () => {
|
|
34
38
|
return new MongoClient(url, mongoClientOptions).connect();
|
|
35
39
|
})().disposer((client, promise) => client.close());
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
return Bluebird.using(withDisposer(),
|
|
39
|
-
}
|
|
40
|
-
}
|
|
42
|
+
return Bluebird.using(withDisposer(), client => fn(client));
|
|
43
|
+
},
|
|
44
|
+
};
|
package/src/mongo-lock.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -22,15 +13,14 @@ class MongoLock {
|
|
|
22
13
|
constructor(client) {
|
|
23
14
|
this.client = client;
|
|
24
15
|
}
|
|
25
|
-
getCollection() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}));
|
|
32
|
-
return locksColl;
|
|
16
|
+
async getCollection() {
|
|
17
|
+
const locksColl = await mongo_helper_1.MongoHelper.collectionSafeGet(this.client, 'locks');
|
|
18
|
+
await locksColl.indexExists('expiresAt').then(async (exists) => {
|
|
19
|
+
if (!exists)
|
|
20
|
+
await locksColl.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
21
|
+
return;
|
|
33
22
|
});
|
|
23
|
+
return locksColl;
|
|
34
24
|
}
|
|
35
25
|
/**
|
|
36
26
|
* Create the MongoDB collection and an expiring index on a field named "expiresAt".
|
|
@@ -38,66 +28,55 @@ class MongoLock {
|
|
|
38
28
|
* db.createCollection('locks');
|
|
39
29
|
* db.locks.createIndex( { "expiresAt": 1 }, { expireAfterSeconds: 0 } )
|
|
40
30
|
**/
|
|
41
|
-
acquireLock(name, ttlSeconds) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
throw ex;
|
|
58
|
-
}
|
|
59
|
-
// As we got a duplicate key exception, no lock could be acquired
|
|
60
|
-
return false;
|
|
31
|
+
async acquireLock(name, ttlSeconds) {
|
|
32
|
+
const collection = await this.getCollection();
|
|
33
|
+
// Entry gets removed automatically due to an expiry index in Mongo
|
|
34
|
+
const expiresAt = (0, moment_1.default)().add(ttlSeconds, 'seconds').toDate();
|
|
35
|
+
try {
|
|
36
|
+
await collection.insertOne({
|
|
37
|
+
_id: name,
|
|
38
|
+
expiresAt,
|
|
39
|
+
});
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
// 11000 means duplicate key error, which is expected on an active lock
|
|
44
|
+
if (error?.code !== 11000) {
|
|
45
|
+
// Unexpected error, what happened here :o
|
|
46
|
+
throw error;
|
|
61
47
|
}
|
|
62
|
-
|
|
48
|
+
// As we got a duplicate key exception, no lock could be acquired
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
63
51
|
}
|
|
64
|
-
releaseLock(name) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return collection.deleteOne({ _id: name });
|
|
68
|
-
});
|
|
52
|
+
async releaseLock(name) {
|
|
53
|
+
const collection = await this.getCollection();
|
|
54
|
+
return collection.deleteOne({ _id: name });
|
|
69
55
|
}
|
|
70
|
-
acquireAndExecute(name, ttlSeconds, wait, fn) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
return bluebird_1.default.using(withDisposer(), () => fn());
|
|
87
|
-
}))));
|
|
88
|
-
});
|
|
56
|
+
async acquireAndExecute(name, ttlSeconds, wait, fn) {
|
|
57
|
+
const delay = 200;
|
|
58
|
+
const count = Math.round((1000 / delay) * ttlSeconds);
|
|
59
|
+
return (0, rxjs_1.lastValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(this.acquireLock(name, ttlSeconds))).pipe((0, rxjs_1.map)(locked => {
|
|
60
|
+
// console.log(locked);
|
|
61
|
+
if (!locked)
|
|
62
|
+
throw new Error('Could not acquire lock');
|
|
63
|
+
}), wait ? (0, rxjs_1.retry)({ count, delay }) : (0, rxjs_1.tap)(rxjs_1.identity), (0, rxjs_1.switchMap)(async () => {
|
|
64
|
+
const method = bluebird_1.default.method(rxjs_1.identity);
|
|
65
|
+
const withDisposer = () => method(this).disposer(mongoLock => {
|
|
66
|
+
mongoLock.releaseLock(name).catch(noop_1.default);
|
|
67
|
+
});
|
|
68
|
+
return bluebird_1.default.using(withDisposer(), () => fn());
|
|
69
|
+
})));
|
|
89
70
|
}
|
|
90
|
-
checkLock(name, waitSeconds = 0) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}), waitSeconds > 0 ? (0, rxjs_1.retry)({ count, delay }) : (0, rxjs_1.tap)(rxjs_1.identity)));
|
|
100
|
-
});
|
|
71
|
+
async checkLock(name, waitSeconds = 0) {
|
|
72
|
+
const delay = 200;
|
|
73
|
+
const count = Math.round((1000 / delay) * waitSeconds);
|
|
74
|
+
const collection = await this.getCollection();
|
|
75
|
+
return (0, rxjs_1.lastValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(collection.findOne({ _id: name }))).pipe((0, rxjs_1.map)(lock => {
|
|
76
|
+
// console.log(lock);
|
|
77
|
+
if (lock)
|
|
78
|
+
throw lock;
|
|
79
|
+
}), waitSeconds > 0 ? (0, rxjs_1.retry)({ count, delay }) : (0, rxjs_1.tap)(rxjs_1.identity)));
|
|
101
80
|
}
|
|
102
81
|
}
|
|
103
82
|
exports.MongoLock = MongoLock;
|
package/src/mongo-lock.ts
CHANGED
|
@@ -6,13 +6,13 @@ import { MongoHelper } from './mongo-helper';
|
|
|
6
6
|
import Bluebird from 'bluebird';
|
|
7
7
|
|
|
8
8
|
export class MongoLock {
|
|
9
|
-
constructor(private client: MongoClient) {
|
|
10
|
-
}
|
|
9
|
+
constructor(private client: MongoClient) {}
|
|
11
10
|
|
|
12
11
|
async getCollection() {
|
|
13
12
|
const locksColl = await MongoHelper.collectionSafeGet(this.client, 'locks');
|
|
14
13
|
await locksColl.indexExists('expiresAt').then(async exists => {
|
|
15
14
|
if (!exists) await locksColl.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
15
|
+
return;
|
|
16
16
|
});
|
|
17
17
|
return locksColl;
|
|
18
18
|
}
|
|
@@ -36,11 +36,11 @@ export class MongoLock {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
return true;
|
|
39
|
-
} catch (
|
|
39
|
+
} catch (error) {
|
|
40
40
|
// 11000 means duplicate key error, which is expected on an active lock
|
|
41
|
-
if ((
|
|
41
|
+
if ((error as MongoError)?.code !== 11_000) {
|
|
42
42
|
// Unexpected error, what happened here :o
|
|
43
|
-
throw
|
|
43
|
+
throw error;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// As we got a duplicate key exception, no lock could be acquired
|
|
@@ -65,14 +65,12 @@ export class MongoLock {
|
|
|
65
65
|
}),
|
|
66
66
|
wait ? retry({ count, delay }) : tap(identity),
|
|
67
67
|
switchMap(async () => {
|
|
68
|
-
const
|
|
68
|
+
const method = Bluebird.method(identity);
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
}
|
|
70
|
+
const withDisposer = () =>
|
|
71
|
+
method(this).disposer(mongoLock => {
|
|
72
|
+
mongoLock.releaseLock(name).catch(noop);
|
|
73
|
+
});
|
|
76
74
|
|
|
77
75
|
return Bluebird.using(withDisposer(), () => fn());
|
|
78
76
|
}),
|
package/src/types.ts
CHANGED
|
@@ -31,26 +31,27 @@ export type TPSEvent<A = TMetadataOrAttributes> = {
|
|
|
31
31
|
export type TEvent = TGSEvent | TPSEvent;
|
|
32
32
|
|
|
33
33
|
export type TMetadataOrAttributes = {
|
|
34
|
-
|
|
34
|
+
request_id?: string; // for rpc response [GUID]
|
|
35
|
+
consumer_id?: string; // for rpc response [GUID]
|
|
35
36
|
topic?: string; // response PubSub topic [t_{GUID}__{YYYY-MM-DD}]
|
|
36
37
|
exchange?: string; // response amqp exchange
|
|
37
38
|
queue?: string; // response amqp queue
|
|
38
39
|
//
|
|
39
40
|
filename?: string;
|
|
40
41
|
referer?: string;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
remote_address?: string;
|
|
43
|
+
upload_id?: string;
|
|
44
|
+
user_agent?: string;
|
|
44
45
|
timestamp?: string;
|
|
45
46
|
//
|
|
46
47
|
action?: string;
|
|
47
48
|
pipeline?: string;
|
|
48
49
|
options?: string;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
job_uid?: string;
|
|
51
|
+
user_id?: string;
|
|
52
|
+
tenant_id?: string;
|
|
52
53
|
//
|
|
53
|
-
|
|
54
|
+
app_id?: string; // app id
|
|
54
55
|
env?: string; // app environment
|
|
55
56
|
};
|
|
56
57
|
|
package/src/utils.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -15,21 +6,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
6
|
exports.sendToQueueConfAmqp = exports.publishAmqp = exports.withAmqpCh = exports.withAmqpConn = exports.safeJsonParse = exports.A1ToColNum = exports.colNumToA1 = exports.A1ToIndex = exports.indexToA1 = exports.sec = exports.ms = exports.delay = exports.timeoutAfter = void 0;
|
|
16
7
|
const amqplib_1 = require("amqplib");
|
|
17
8
|
const bluebird_1 = __importDefault(require("bluebird"));
|
|
18
|
-
const
|
|
9
|
+
const chain_1 = __importDefault(require("lodash/chain"));
|
|
19
10
|
/**
|
|
20
11
|
*
|
|
21
12
|
* @param seconds Google function v1 timeout limit (max: 9 min)
|
|
22
13
|
*/
|
|
23
|
-
function timeoutAfter(seconds = 540) {
|
|
24
|
-
return
|
|
25
|
-
return new Promise((resolve, reject) => setTimeout(() => reject(new Error(`${seconds} seconds timeout exceeded`)), seconds * 1000));
|
|
26
|
-
});
|
|
14
|
+
async function timeoutAfter(seconds = 540) {
|
|
15
|
+
return new Promise((resolve, reject) => setTimeout(() => reject(new Error(`${seconds} seconds timeout exceeded`)), seconds * 1000));
|
|
27
16
|
}
|
|
28
17
|
exports.timeoutAfter = timeoutAfter;
|
|
29
|
-
function delay(seconds) {
|
|
30
|
-
return
|
|
31
|
-
return new Promise(resolve => setTimeout(() => resolve(undefined), seconds * 1000));
|
|
32
|
-
});
|
|
18
|
+
async function delay(seconds) {
|
|
19
|
+
return new Promise(resolve => setTimeout(() => resolve(), seconds * 1000));
|
|
33
20
|
}
|
|
34
21
|
exports.delay = delay;
|
|
35
22
|
//
|
|
@@ -44,8 +31,8 @@ function sec(o) {
|
|
|
44
31
|
multiMap.h = multiMap.m * 60;
|
|
45
32
|
multiMap.d = multiMap.h * 24;
|
|
46
33
|
multiMap.w = multiMap.d * 7;
|
|
47
|
-
return (0,
|
|
48
|
-
.map(([k, v]) =>
|
|
34
|
+
return (0, chain_1.default)(Object.entries(o))
|
|
35
|
+
.map(([k, v]) => (multiMap[k] ?? 0) * (v ?? 0))
|
|
49
36
|
.reduce((sum, v) => sum + v)
|
|
50
37
|
.value();
|
|
51
38
|
}
|
|
@@ -87,7 +74,7 @@ exports.colNumToA1 = colNumToA1;
|
|
|
87
74
|
function A1ToColNum(value) {
|
|
88
75
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
89
76
|
let result = 0;
|
|
90
|
-
//
|
|
77
|
+
// eslint-disable-next-line unicorn/no-for-loop
|
|
91
78
|
for (let i = 0; i < value.length; i++) {
|
|
92
79
|
result *= chars.length;
|
|
93
80
|
result += chars.indexOf(value[i]) + 1;
|
|
@@ -99,53 +86,45 @@ function safeJsonParse(value, fallbackValue) {
|
|
|
99
86
|
try {
|
|
100
87
|
return JSON.parse(value);
|
|
101
88
|
}
|
|
102
|
-
catch
|
|
89
|
+
catch {
|
|
103
90
|
return fallbackValue;
|
|
104
91
|
}
|
|
105
92
|
}
|
|
106
93
|
exports.safeJsonParse = safeJsonParse;
|
|
107
94
|
//
|
|
108
|
-
function withAmqpConn(fn, url) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return bluebird_1.default.using(withDisposer(), conn => fn(conn));
|
|
118
|
-
});
|
|
95
|
+
async function withAmqpConn(fn, url) {
|
|
96
|
+
function withDisposer() {
|
|
97
|
+
return bluebird_1.default.method(async () => {
|
|
98
|
+
const amqpConn = await (0, amqplib_1.connect)(url);
|
|
99
|
+
amqpConn.on('close', () => console.info('Amqp connection closed!'));
|
|
100
|
+
return amqpConn;
|
|
101
|
+
})().disposer((conn, promise) => conn.close());
|
|
102
|
+
}
|
|
103
|
+
return bluebird_1.default.using(withDisposer(), conn => fn(conn));
|
|
119
104
|
}
|
|
120
105
|
exports.withAmqpConn = withAmqpConn;
|
|
121
|
-
function withAmqpCh(fn, url, useConfirmChannel = false) {
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}), url);
|
|
131
|
-
});
|
|
106
|
+
async function withAmqpCh(fn, url, useConfirmChannel = false) {
|
|
107
|
+
return withAmqpConn(async (conn) => {
|
|
108
|
+
function withDisposer() {
|
|
109
|
+
return bluebird_1.default.method(async () => {
|
|
110
|
+
return useConfirmChannel ? conn.createConfirmChannel() : conn.createChannel();
|
|
111
|
+
})().disposer((ch, promise) => ch.close());
|
|
112
|
+
}
|
|
113
|
+
return bluebird_1.default.using(withDisposer(), ch => fn(ch));
|
|
114
|
+
}, url);
|
|
132
115
|
}
|
|
133
116
|
exports.withAmqpCh = withAmqpCh;
|
|
134
|
-
function publishAmqp(ch, exchange, routingKey, json, options) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
yield new Promise(resolve => ch.once('drain', () => resolve(undefined)));
|
|
142
|
-
});
|
|
117
|
+
async function publishAmqp(ch, exchange, routingKey, json, options) {
|
|
118
|
+
const payload = Buffer.from(JSON.stringify(json));
|
|
119
|
+
const keepSending = exchange
|
|
120
|
+
? ch.publish(exchange, routingKey, payload, options)
|
|
121
|
+
: ch.sendToQueue(routingKey, payload, options);
|
|
122
|
+
if (!keepSending)
|
|
123
|
+
await new Promise(resolve => ch.once('drain', () => resolve()));
|
|
143
124
|
}
|
|
144
125
|
exports.publishAmqp = publishAmqp;
|
|
145
|
-
function sendToQueueConfAmqp(ch, queue, json, options) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
yield new Promise((resolve, reject) => ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))));
|
|
149
|
-
});
|
|
126
|
+
async function sendToQueueConfAmqp(ch, queue, json, options) {
|
|
127
|
+
const payload = Buffer.from(JSON.stringify(json));
|
|
128
|
+
await new Promise((resolve, reject) => ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))));
|
|
150
129
|
}
|
|
151
130
|
exports.sendToQueueConfAmqp = sendToQueueConfAmqp;
|
package/src/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Channel, ConfirmChannel, connect, Connection, Options } from 'amqplib';
|
|
2
2
|
import Bluebird from 'bluebird';
|
|
3
|
-
import
|
|
3
|
+
import chain from 'lodash/chain';
|
|
4
4
|
import Dict = NodeJS.Dict;
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -8,13 +8,13 @@ import Dict = NodeJS.Dict;
|
|
|
8
8
|
* @param seconds Google function v1 timeout limit (max: 9 min)
|
|
9
9
|
*/
|
|
10
10
|
export async function timeoutAfter(seconds: number = 540) {
|
|
11
|
-
return new Promise<
|
|
11
|
+
return new Promise<void>((resolve, reject) =>
|
|
12
12
|
setTimeout(() => reject(new Error(`${seconds} seconds timeout exceeded`)), seconds * 1000),
|
|
13
13
|
);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export async function delay(seconds: number) {
|
|
17
|
-
return new Promise(resolve => setTimeout(() => resolve(
|
|
17
|
+
return new Promise<void>(resolve => setTimeout(() => resolve(), seconds * 1000));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
//
|
|
@@ -77,7 +77,7 @@ export function colNumToA1(columnNumber: number) {
|
|
|
77
77
|
export function A1ToColNum(value: string) {
|
|
78
78
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
79
79
|
let result = 0;
|
|
80
|
-
//
|
|
80
|
+
// eslint-disable-next-line unicorn/no-for-loop
|
|
81
81
|
for (let i = 0; i < value.length; i++) {
|
|
82
82
|
result *= chars.length;
|
|
83
83
|
result += chars.indexOf(value[i]) + 1;
|
|
@@ -88,7 +88,7 @@ export function A1ToColNum(value: string) {
|
|
|
88
88
|
export function safeJsonParse<T = Dict<any>>(value: string, fallbackValue?: T) {
|
|
89
89
|
try {
|
|
90
90
|
return JSON.parse(value) as T;
|
|
91
|
-
} catch
|
|
91
|
+
} catch {
|
|
92
92
|
return fallbackValue;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -123,20 +123,20 @@ export async function publishAmqp(
|
|
|
123
123
|
ch: Channel,
|
|
124
124
|
exchange: string | undefined,
|
|
125
125
|
routingKey: string,
|
|
126
|
-
json:
|
|
126
|
+
json: Dict<any>,
|
|
127
127
|
options?: Options.Publish,
|
|
128
128
|
) {
|
|
129
129
|
const payload = Buffer.from(JSON.stringify(json));
|
|
130
130
|
const keepSending = exchange
|
|
131
131
|
? ch.publish(exchange, routingKey, payload, options)
|
|
132
132
|
: ch.sendToQueue(routingKey, payload, options);
|
|
133
|
-
if (!keepSending) await new Promise(resolve => ch.once('drain', () => resolve(
|
|
133
|
+
if (!keepSending) await new Promise<void>(resolve => ch.once('drain', () => resolve()));
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
export async function sendToQueueConfAmqp(
|
|
137
137
|
ch: ConfirmChannel,
|
|
138
138
|
queue: string,
|
|
139
|
-
json:
|
|
139
|
+
json: Dict<any>,
|
|
140
140
|
options?: Options.Publish,
|
|
141
141
|
) {
|
|
142
142
|
const payload = Buffer.from(JSON.stringify(json));
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json.schemastore.org/tsconfig",
|
|
3
|
-
"extends": "@tsconfig/
|
|
3
|
+
"extends": "@tsconfig/node16/tsconfig.json",
|
|
4
4
|
"compilerOptions": {
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"ES2020",
|
|
10
|
-
"ES2021",
|
|
11
|
-
"ES2022",
|
|
12
|
-
"ESNext"
|
|
13
|
-
]
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"target": "es2021",
|
|
8
|
+
"lib": ["es2022"]
|
|
14
9
|
}
|
|
15
10
|
}
|