gcf-common-lib 0.21.3 → 0.22.4
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/package.json +12 -11
- package/src/index.js +6 -6
- package/src/index.ts +3 -3
- package/src/mongo-lock.js +75 -0
- package/src/mongo-lock.ts +66 -0
package/package.json
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gcf-common-lib",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.22.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"branches": [
|
|
8
8
|
"master"
|
|
9
9
|
]
|
|
10
10
|
},
|
|
11
|
-
"engines": {
|
|
12
|
-
"node": ">=14",
|
|
13
|
-
"npm": ">=8"
|
|
14
|
-
},
|
|
11
|
+
"engines": {},
|
|
15
12
|
"main": "src/index",
|
|
16
13
|
"scripts": {
|
|
17
14
|
"test": "echo \"Error: no test specified\" || exit 0"
|
|
@@ -21,15 +18,19 @@
|
|
|
21
18
|
"url": "https://github.com/TopTechnologies/gcf-common.git"
|
|
22
19
|
},
|
|
23
20
|
"dependencies": {
|
|
24
|
-
"@google-cloud/pubsub": "^3.2.
|
|
25
|
-
"@google-cloud/secret-manager": "^4.
|
|
26
|
-
"@google-cloud/storage": "^6.
|
|
27
|
-
"
|
|
21
|
+
"@google-cloud/pubsub": "^3.2.1",
|
|
22
|
+
"@google-cloud/secret-manager": "^4.2.0",
|
|
23
|
+
"@google-cloud/storage": "^6.9.0",
|
|
24
|
+
"bluebird": "^3.7.2",
|
|
25
|
+
"lodash": "^4.17.21",
|
|
26
|
+
"moment": "^2.29.4",
|
|
27
|
+
"mongodb": "^4.13.0",
|
|
28
|
+
"rxjs": "^7.8.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@tsconfig/node14": "^1.0.3",
|
|
31
|
-
"@types/
|
|
32
|
-
"@types/
|
|
32
|
+
"@types/lodash": "^4.14.191",
|
|
33
|
+
"@types/node": "^14.18.36"
|
|
33
34
|
},
|
|
34
35
|
"author": "alert83@gmail.com",
|
|
35
36
|
"license": "MIT"
|
package/src/index.js
CHANGED
|
@@ -12,17 +12,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.GcfCommon = exports.storage = exports.pubSub = void 0;
|
|
15
|
+
exports.GcfCommon = exports.secretClient = exports.storage = exports.pubSub = void 0;
|
|
16
16
|
const pubsub_1 = require("@google-cloud/pubsub");
|
|
17
|
-
const secret_manager_1 = require("@google-cloud/secret-manager");
|
|
18
|
-
const storage_1 = require("@google-cloud/storage");
|
|
19
17
|
const fromPairs_1 = __importDefault(require("lodash/fromPairs"));
|
|
20
18
|
const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
|
|
21
19
|
const noop_1 = __importDefault(require("lodash/noop"));
|
|
22
20
|
const utils_1 = require("./utils");
|
|
21
|
+
const storage_1 = require("@google-cloud/storage");
|
|
22
|
+
const secret_manager_1 = require("@google-cloud/secret-manager");
|
|
23
23
|
exports.pubSub = new pubsub_1.PubSub();
|
|
24
24
|
exports.storage = new storage_1.Storage();
|
|
25
|
-
|
|
25
|
+
exports.secretClient = new secret_manager_1.SecretManagerServiceClient();
|
|
26
26
|
class GcfCommon {
|
|
27
27
|
/**
|
|
28
28
|
*
|
|
@@ -120,10 +120,10 @@ class GcfCommon {
|
|
|
120
120
|
static getSecret(name, version) {
|
|
121
121
|
var _a, _b;
|
|
122
122
|
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
-
const projectId = yield secretClient.getProjectId();
|
|
123
|
+
const projectId = yield exports.secretClient.getProjectId();
|
|
124
124
|
const secretName = `projects/${projectId}/secrets/${name}`;
|
|
125
125
|
const secretVersion = `${secretName}/versions/${version !== null && version !== void 0 ? version : 'latest'}`;
|
|
126
|
-
const [response] = yield secretClient.accessSecretVersion({ name: secretVersion });
|
|
126
|
+
const [response] = yield exports.secretClient.accessSecretVersion({ name: secretVersion });
|
|
127
127
|
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();
|
|
128
128
|
});
|
|
129
129
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {PubSub} from "@google-cloud/pubsub";
|
|
2
|
-
import {SecretManagerServiceClient} from "@google-cloud/secret-manager";
|
|
3
|
-
import {File, Storage} from "@google-cloud/storage";
|
|
4
2
|
import fromPairs from "lodash/fromPairs";
|
|
5
3
|
import isEmpty from "lodash/isEmpty";
|
|
6
4
|
import noop from "lodash/noop";
|
|
7
5
|
import {timeoutAfter} from "./utils";
|
|
6
|
+
import {File, Storage} from "@google-cloud/storage";
|
|
7
|
+
import {SecretManagerServiceClient} from "@google-cloud/secret-manager";
|
|
8
8
|
import Dict = NodeJS.Dict;
|
|
9
9
|
|
|
10
10
|
export type TGSEvent = {
|
|
@@ -70,7 +70,7 @@ export type TResponse = {
|
|
|
70
70
|
|
|
71
71
|
export const pubSub = new PubSub();
|
|
72
72
|
export const storage = new Storage();
|
|
73
|
-
const secretClient = new SecretManagerServiceClient();
|
|
73
|
+
export const secretClient = new SecretManagerServiceClient();
|
|
74
74
|
|
|
75
75
|
export class GcfCommon {
|
|
76
76
|
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.MongoLock = void 0;
|
|
16
|
+
const moment_1 = __importDefault(require("moment"));
|
|
17
|
+
const noop_1 = __importDefault(require("lodash/noop"));
|
|
18
|
+
const rxjs_1 = require("rxjs");
|
|
19
|
+
class MongoLock {
|
|
20
|
+
constructor(client) {
|
|
21
|
+
this.client = client;
|
|
22
|
+
}
|
|
23
|
+
getCollection() {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
return this.client.db().collection('locks');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create the MongoDB collection and an expiring index on a field named "expiresAt".
|
|
30
|
+
*
|
|
31
|
+
* db.createCollection('locks');
|
|
32
|
+
* db.locks.createIndex( { "expiresAt": 1 }, { expireAfterSeconds: 0 } )
|
|
33
|
+
**/
|
|
34
|
+
acquireLock(name, ttlSeconds) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const collection = yield this.getCollection();
|
|
37
|
+
// Entry gets removed automatically due to an expiry index in Mongo
|
|
38
|
+
const expiresAt = (0, moment_1.default)().add(ttlSeconds, 'seconds').toDate();
|
|
39
|
+
try {
|
|
40
|
+
yield collection.insertOne({
|
|
41
|
+
_id: name,
|
|
42
|
+
expiresAt,
|
|
43
|
+
});
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch (ex) {
|
|
47
|
+
// 11000 means duplicate key error, which is expected on an active lock
|
|
48
|
+
if ((ex === null || ex === void 0 ? void 0 : ex.code) !== 11000) {
|
|
49
|
+
// Unexpected error, what happened here :o
|
|
50
|
+
throw ex;
|
|
51
|
+
}
|
|
52
|
+
// As we got a duplicate key exception, no lock could be acquired
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
releaseLock(name) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
const collection = yield this.getCollection();
|
|
60
|
+
return collection.deleteOne({ _id: name });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
acquireAndExecute(name, ttlSeconds, wait, fn) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
const delay = 200;
|
|
66
|
+
const count = Math.round((1000 / delay) * ttlSeconds);
|
|
67
|
+
return (0, rxjs_1.lastValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(this.acquireLock(name, ttlSeconds))).pipe((0, rxjs_1.map)(locked => {
|
|
68
|
+
console.log(locked);
|
|
69
|
+
if (!locked)
|
|
70
|
+
throw locked;
|
|
71
|
+
}), wait ? (0, rxjs_1.retry)({ count, delay }) : rxjs_1.identity, (0, rxjs_1.switchMap)(() => fn().finally(() => this.releaseLock(name).catch(noop_1.default)))));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.MongoLock = MongoLock;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {MongoClient, MongoError} from "mongodb";
|
|
2
|
+
import moment from "moment";
|
|
3
|
+
import noop from "lodash/noop";
|
|
4
|
+
import {defer, from, identity, lastValueFrom, map, retry, switchMap} from "rxjs";
|
|
5
|
+
|
|
6
|
+
export class MongoLock {
|
|
7
|
+
|
|
8
|
+
constructor(private client: MongoClient) {
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private async getCollection() {
|
|
12
|
+
return this.client.db().collection('locks');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create the MongoDB collection and an expiring index on a field named "expiresAt".
|
|
17
|
+
*
|
|
18
|
+
* db.createCollection('locks');
|
|
19
|
+
* db.locks.createIndex( { "expiresAt": 1 }, { expireAfterSeconds: 0 } )
|
|
20
|
+
**/
|
|
21
|
+
async acquireLock(name: string, ttlSeconds: number) {
|
|
22
|
+
const collection = await this.getCollection();
|
|
23
|
+
|
|
24
|
+
// Entry gets removed automatically due to an expiry index in Mongo
|
|
25
|
+
const expiresAt = moment().add(ttlSeconds, 'seconds').toDate();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await collection.insertOne({
|
|
29
|
+
_id: name as any,
|
|
30
|
+
expiresAt,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return true;
|
|
34
|
+
} catch (ex) {
|
|
35
|
+
// 11000 means duplicate key error, which is expected on an active lock
|
|
36
|
+
if ((ex as MongoError)?.code !== 11000) {
|
|
37
|
+
// Unexpected error, what happened here :o
|
|
38
|
+
throw ex
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// As we got a duplicate key exception, no lock could be acquired
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async releaseLock(name: string) {
|
|
47
|
+
const collection = await this.getCollection();
|
|
48
|
+
return collection.deleteOne({_id: name});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async acquireAndExecute(name: string, ttlSeconds: number, wait: boolean, fn: () => Promise<any>) {
|
|
52
|
+
const delay = 200;
|
|
53
|
+
const count = Math.round((1000 / delay) * ttlSeconds);
|
|
54
|
+
|
|
55
|
+
return lastValueFrom(
|
|
56
|
+
defer(() => from(this.acquireLock(name, ttlSeconds))).pipe(
|
|
57
|
+
map(locked => {
|
|
58
|
+
console.log(locked);
|
|
59
|
+
if (!locked) throw locked;
|
|
60
|
+
}),
|
|
61
|
+
wait ? retry({count, delay}) : identity,
|
|
62
|
+
switchMap(() => fn().finally(() => this.releaseLock(name).catch(noop))),
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|