gcf-common-lib 0.23.11 → 0.23.13

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/.prettierrc ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "printWidth": 120,
3
+ "trailingComma": "all",
4
+ "tabWidth": 2,
5
+ "semi": true,
6
+ "singleQuote": true,
7
+ "arrowParens": "avoid",
8
+ "bracketSameLine": true
9
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gcf-common-lib",
3
3
  "description": "",
4
- "version": "0.23.11",
4
+ "version": "0.23.13",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "branches": [
package/src/index.js CHANGED
@@ -33,10 +33,7 @@ class GcfCommon {
33
33
  */
34
34
  static process(event, context, handler, timeout = 535) {
35
35
  return __awaiter(this, void 0, void 0, function* () {
36
- return Promise.race([
37
- (0, utils_1.timeoutAfter)(timeout),
38
- handler(event, context),
39
- ])
36
+ return Promise.race([(0, utils_1.timeoutAfter)(timeout), handler(event, context)])
40
37
  .then((res) => __awaiter(this, void 0, void 0, function* () {
41
38
  // console.log('res:', res);
42
39
  yield this.publish(event, context, res);
@@ -63,8 +60,7 @@ class GcfCommon {
63
60
  if (!(0, isEmpty_1.default)(topic)) {
64
61
  return exports.pubSub.topic(topic).publishMessage({
65
62
  json: json !== null && json !== void 0 ? json : {},
66
- attributes: Object.assign(Object.assign({}, (0, fromPairs_1.default)(Object.entries(attributes !== null && attributes !== void 0 ? attributes : {})
67
- .map(([k, v]) => [k, '' + v]))), { env: env !== null && env !== void 0 ? env : '', requestId: requestId !== null && requestId !== void 0 ? requestId : '', type: 'response', response: '1' }),
63
+ attributes: Object.assign(Object.assign({}, (0, fromPairs_1.default)(Object.entries(attributes !== null && attributes !== void 0 ? attributes : {}).map(([k, v]) => [k, '' + v]))), { env: env !== null && env !== void 0 ? env : '', requestId: requestId !== null && requestId !== void 0 ? requestId : '', type: 'response', response: '1' }),
68
64
  });
69
65
  }
70
66
  });
package/src/index.ts CHANGED
@@ -1,193 +1,182 @@
1
- import {PubSub} from "@google-cloud/pubsub";
2
- import fromPairs from "lodash/fromPairs";
3
- import isEmpty from "lodash/isEmpty";
4
- import noop from "lodash/noop";
5
- import {timeoutAfter} from "./utils";
6
- import {File, Storage} from "@google-cloud/storage";
7
- import {SecretManagerServiceClient} from "@google-cloud/secret-manager";
1
+ import { PubSub } from '@google-cloud/pubsub';
2
+ import fromPairs from 'lodash/fromPairs';
3
+ import isEmpty from 'lodash/isEmpty';
4
+ import noop from 'lodash/noop';
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 = {
11
- bucket: string;
12
- contentType: string;
13
- crc32c: string;
14
- etag: string;
15
- generation: string;
16
- id: string;
17
- kind: string; // 'storage#object'
18
- md5Hash: string;
19
- mediaLink: string;
20
- metadata: {
21
- // [key: string]: string,
22
- topic: string,
23
- requestId: string,
24
- env: string,
25
- options?: any,
26
- };
27
- metageneration: string;
28
- name: string;
29
- selfLink: string;
30
- size: string;
31
- storageClass: string;
32
- timeCreated: string;
33
- timeStorageClassUpdated: string;
34
- updated: string;
35
- }
11
+ bucket: string;
12
+ contentType: string;
13
+ crc32c: string;
14
+ etag: string;
15
+ generation: string;
16
+ id: string;
17
+ kind: string; // 'storage#object'
18
+ md5Hash: string;
19
+ mediaLink: string;
20
+ metadata: {
21
+ // [key: string]: string,
22
+ topic: string;
23
+ requestId: string;
24
+ env: string;
25
+ options?: any;
26
+ };
27
+ metageneration: string;
28
+ name: string;
29
+ selfLink: string;
30
+ size: string;
31
+ storageClass: string;
32
+ timeCreated: string;
33
+ timeStorageClassUpdated: string;
34
+ updated: string;
35
+ };
36
36
 
37
37
  export type TPSEvent = {
38
- '@type': string; // 'type.googleapis.com/google.pubsub.v1.PubsubMessage'
39
- attributes?: {
40
- // [k: string]: string,
41
- topic: string,
42
- requestId: string,
43
- env: string,
44
- options?: any,
45
- };
46
- data?: string | Dict<any>;
47
- }
38
+ '@type': string; // 'type.googleapis.com/google.pubsub.v1.PubsubMessage'
39
+ attributes?: {
40
+ // [k: string]: string,
41
+ topic: string;
42
+ requestId: string;
43
+ env: string;
44
+ options?: any;
45
+ };
46
+ data?: string | Dict<any>;
47
+ };
48
48
 
49
49
  export type TEvent = TGSEvent | TPSEvent;
50
50
 
51
51
  export type TContext = {
52
- eventId: string;
53
- timestamp: string;
54
- eventType: 'google.storage.object.finalize' | 'google.pubsub.topic.publish';
55
- resource: {
56
- service: 'storage.googleapis.com' | 'pubsub.googleapis.com';
57
- type: 'storage#object' | 'type.googleapis.com/google.pubsub.v1.PubsubMessage';
58
- name: string;
59
- }
60
- }
52
+ eventId: string;
53
+ timestamp: string;
54
+ eventType: 'google.storage.object.finalize' | 'google.pubsub.topic.publish';
55
+ resource: {
56
+ service: 'storage.googleapis.com' | 'pubsub.googleapis.com';
57
+ type: 'storage#object' | 'type.googleapis.com/google.pubsub.v1.PubsubMessage';
58
+ name: string;
59
+ };
60
+ };
61
61
 
62
62
  export type TResponse = {
63
- [prop: string]: any,
64
- error?: {
65
- name: string,
66
- message: string,
67
- stack?: string,
68
- }
69
- }
63
+ [prop: string]: any;
64
+ error?: {
65
+ name: string;
66
+ message: string;
67
+ stack?: string;
68
+ };
69
+ };
70
70
 
71
71
  export const pubSub = new PubSub();
72
72
  export const storage = new Storage();
73
73
  export const secretClient = new SecretManagerServiceClient();
74
74
 
75
75
  export class GcfCommon {
76
-
77
- /**
78
- *
79
- * @param {!TEvent} event Event payload.
80
- * @param {!TContext} context Metadata for the event.
81
- * @param handler
82
- * @param timeout Seconds
83
- */
84
- static async process<T extends TResponse>(
85
- event: TEvent,
86
- context: TContext,
87
- handler: (event: TEvent, context: TContext) => Promise<T>,
88
- timeout = 535,
89
- ) {
90
- return Promise.race([
91
- timeoutAfter(timeout),
92
- handler(event, context),
93
- ])
94
- .then(async (res: T | undefined) => {
95
- // console.log('res:', res);
96
- await this.publish(event, context, res);
97
- })
98
- .catch(async (err: Error) => {
99
- const fname = process?.env?.K_SERVICE ?? 'UNKNOWN';
100
- const response: TResponse = {
101
- error: {
102
- name: err.name,
103
- message: `GCF [${fname}]: ${err.message}`,
104
- stack: err.stack,
105
- },
106
- };
107
- await this.publish(event, context, response, {error: '1'}).catch(noop);
108
- throw err;
109
- });
110
- }
111
-
112
- static async publish(event: TEvent, context: TContext, json?: TResponse, attributes?: Dict<any>) {
113
- const {topic, requestId, env} = await this.getTopic(event, context);
114
- console.log('publish:', topic, env, json, attributes);
115
-
116
- if (!isEmpty(topic)) {
117
- return pubSub.topic(topic as string).publishMessage({
118
- json: json ?? {},
119
- attributes: {
120
- ...fromPairs(
121
- Object.entries(attributes ?? {})
122
- .map(([k, v]) => [k, '' + v])
123
- ),
124
- env: env ?? '',
125
- requestId: requestId ?? '',
126
- type: 'response',
127
- response: '1',
128
- },
129
- });
130
- }
76
+ /**
77
+ *
78
+ * @param {!TEvent} event Event payload.
79
+ * @param {!TContext} context Metadata for the event.
80
+ * @param handler
81
+ * @param timeout Seconds
82
+ */
83
+ static async process<T extends TResponse>(
84
+ event: TEvent,
85
+ context: TContext,
86
+ handler: (event: TEvent, context: TContext) => Promise<T>,
87
+ timeout = 535,
88
+ ) {
89
+ return Promise.race([timeoutAfter(timeout), handler(event, context)])
90
+ .then(async (res: T | undefined) => {
91
+ // console.log('res:', res);
92
+ await this.publish(event, context, res);
93
+ })
94
+ .catch(async (err: Error) => {
95
+ const fname = process?.env?.K_SERVICE ?? 'UNKNOWN';
96
+ const response: TResponse = {
97
+ error: {
98
+ name: err.name,
99
+ message: `GCF [${fname}]: ${err.message}`,
100
+ stack: err.stack,
101
+ },
102
+ };
103
+ await this.publish(event, context, response, { error: '1' }).catch(noop);
104
+ throw err;
105
+ });
106
+ }
107
+
108
+ static async publish(event: TEvent, context: TContext, json?: TResponse, attributes?: Dict<any>) {
109
+ const { topic, requestId, env } = await this.getTopic(event, context);
110
+ console.log('publish:', topic, env, json, attributes);
111
+
112
+ if (!isEmpty(topic)) {
113
+ return pubSub.topic(topic as string).publishMessage({
114
+ json: json ?? {},
115
+ attributes: {
116
+ ...fromPairs(Object.entries(attributes ?? {}).map(([k, v]) => [k, '' + v])),
117
+ env: env ?? '',
118
+ requestId: requestId ?? '',
119
+ type: 'response',
120
+ response: '1',
121
+ },
122
+ });
131
123
  }
132
-
133
- static async getTopic(event: TEvent, context: TContext) {
134
-
135
- /** t_{GUID}__{YYYY-MM-DD} */
136
- let topic: string | undefined;
137
- let requestId: string | undefined;
138
- let env: string | undefined;
139
-
140
- switch (context?.eventType) {
141
- case 'google.storage.object.finalize': {
142
- const gsEvent = event as TGSEvent;
143
- ({topic, requestId, env} = gsEvent?.metadata ?? {});
144
- if (!topic && context?.resource?.type === 'storage#object') {
145
- const [meta] = await storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
146
- ({topic, requestId, env} = meta?.metadata ?? {});
147
- }
148
- break;
149
- }
150
- case 'google.pubsub.topic.publish' : {
151
- const psEvent = event as TPSEvent;
152
- ({topic, requestId, env} = psEvent?.attributes ?? {} as any);
153
- break;
154
- }
124
+ }
125
+
126
+ static async getTopic(event: TEvent, context: TContext) {
127
+ /** t_{GUID}__{YYYY-MM-DD} */
128
+ let topic: string | undefined;
129
+ let requestId: string | undefined;
130
+ let env: string | undefined;
131
+
132
+ switch (context?.eventType) {
133
+ case 'google.storage.object.finalize': {
134
+ const gsEvent = event as TGSEvent;
135
+ ({ topic, requestId, env } = gsEvent?.metadata ?? {});
136
+ if (!topic && context?.resource?.type === 'storage#object') {
137
+ const [meta] = await storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
138
+ ({ topic, requestId, env } = meta?.metadata ?? {});
155
139
  }
156
-
157
- return {topic, requestId, env};
140
+ break;
141
+ }
142
+ case 'google.pubsub.topic.publish': {
143
+ const psEvent = event as TPSEvent;
144
+ ({ topic, requestId, env } = psEvent?.attributes ?? ({} as any));
145
+ break;
146
+ }
158
147
  }
159
148
 
160
- static async getOptions(event: TEvent, context: TContext): Promise<Dict<any>> {
161
- switch (context?.eventType) {
162
- case 'google.storage.object.finalize': {
163
- const gsEvent = event as TGSEvent;
164
- if (gsEvent?.metadata?.options) {
165
- return JSON.parse(gsEvent?.metadata?.options ?? '{}');
166
- } else {
167
- const file: File = storage.bucket(gsEvent.bucket).file(gsEvent.name);
168
- const [meta] = await file.getMetadata();
169
- return JSON.parse(meta?.metadata?.options ?? '{}');
170
- }
171
- }
172
- case 'google.pubsub.topic.publish' : {
173
- const psEvent = event as TPSEvent;
174
- return JSON.parse(psEvent?.attributes?.options ?? '{}');
175
- }
149
+ return { topic, requestId, env };
150
+ }
151
+
152
+ static async getOptions(event: TEvent, context: TContext): Promise<Dict<any>> {
153
+ switch (context?.eventType) {
154
+ case 'google.storage.object.finalize': {
155
+ const gsEvent = event as TGSEvent;
156
+ if (gsEvent?.metadata?.options) {
157
+ return JSON.parse(gsEvent?.metadata?.options ?? '{}');
158
+ } else {
159
+ const file: File = storage.bucket(gsEvent.bucket).file(gsEvent.name);
160
+ const [meta] = await file.getMetadata();
161
+ return JSON.parse(meta?.metadata?.options ?? '{}');
176
162
  }
163
+ }
164
+ case 'google.pubsub.topic.publish': {
165
+ const psEvent = event as TPSEvent;
166
+ return JSON.parse(psEvent?.attributes?.options ?? '{}');
167
+ }
177
168
  }
178
-
179
- static async getSecret(name: string, version?: string) {
180
- const projectId = await secretClient.getProjectId();
181
- const secretName = `projects/${projectId}/secrets/${name}`;
182
- const secretVersion = `${secretName}/versions/${version ?? 'latest'}`;
183
- const [response] = await secretClient.accessSecretVersion({name: secretVersion});
184
- return response?.payload?.data?.toString();
185
- }
186
-
187
- static async getSecrets(names: string[], versions?: string[]) {
188
- return Promise.all(
189
- names.map(async (name, idx) =>
190
- this.getSecret(name, versions?.[idx]).catch())
191
- );
192
- }
169
+ }
170
+
171
+ static async getSecret(name: string, version?: string) {
172
+ const projectId = await secretClient.getProjectId();
173
+ const secretName = `projects/${projectId}/secrets/${name}`;
174
+ const secretVersion = `${secretName}/versions/${version ?? 'latest'}`;
175
+ const [response] = await secretClient.accessSecretVersion({ name: secretVersion });
176
+ return response?.payload?.data?.toString();
177
+ }
178
+
179
+ static async getSecrets(names: string[], versions?: string[]) {
180
+ return Promise.all(names.map(async (name, idx) => this.getSecret(name, versions?.[idx]).catch()));
181
+ }
193
182
  }
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.MongoHelper = void 0;
13
+ const mongodb_1 = require("mongodb");
13
14
  class MongoHelper {
14
15
  static collectionExists(client, name) {
15
16
  return __awaiter(this, void 0, void 0, function* () {
@@ -32,5 +33,18 @@ class MongoHelper {
32
33
  return coll;
33
34
  });
34
35
  }
36
+ withMongoClient(fn, url, mongoClientOptions) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ const mongoClient = new mongodb_1.MongoClient(url, mongoClientOptions);
39
+ return yield mongoClient
40
+ .connect()
41
+ .then((client) => __awaiter(this, void 0, void 0, function* () {
42
+ return yield fn(client);
43
+ }))
44
+ .finally(() => __awaiter(this, void 0, void 0, function* () {
45
+ yield mongoClient.close();
46
+ }));
47
+ });
48
+ }
35
49
  }
36
50
  exports.MongoHelper = MongoHelper;
@@ -1,30 +1,42 @@
1
- import {Collection, CreateCollectionOptions, MongoClient} from "mongodb";
2
- import {Document} from "bson";
1
+ import { Collection, CreateCollectionOptions, MongoClient, MongoClientOptions } from 'mongodb';
2
+ import { Document } from 'bson';
3
3
 
4
4
  export class MongoHelper {
5
- static async collectionExists(client: MongoClient, name: string) {
6
- const db = client.db();
7
- const collections = await db.listCollections({name}, {nameOnly: true}).toArray();
8
- return collections.length > 0;
9
- }
10
-
11
- static async collectionSafeGet<TSchema extends Document = Document>(
12
- client: MongoClient,
13
- name: string,
14
- options?: CreateCollectionOptions,
15
- afterCreate?: (col: Collection<TSchema>) => Promise<void>,
16
- ) {
17
- const db = client.db();
18
- let coll: Collection<TSchema>;
5
+ static async collectionExists(client: MongoClient, name: string) {
6
+ const db = client.db();
7
+ const collections = await db.listCollections({ name }, { nameOnly: true }).toArray();
8
+ return collections.length > 0;
9
+ }
19
10
 
20
- if (await this.collectionExists(client, name)) {
21
- coll = db.collection<TSchema>(name);
22
- } else {
23
- coll = await db.createCollection<TSchema>(name, options);
24
- afterCreate && (await afterCreate(coll));
25
- }
11
+ static async collectionSafeGet<TSchema extends Document = Document>(
12
+ client: MongoClient,
13
+ name: string,
14
+ options?: CreateCollectionOptions,
15
+ afterCreate?: (col: Collection<TSchema>) => Promise<void>,
16
+ ) {
17
+ const db = client.db();
18
+ let coll: Collection<TSchema>;
26
19
 
27
- return coll;
20
+ if (await this.collectionExists(client, name)) {
21
+ coll = db.collection<TSchema>(name);
22
+ } else {
23
+ coll = await db.createCollection<TSchema>(name, options);
24
+ afterCreate && (await afterCreate(coll));
28
25
  }
29
26
 
30
- }
27
+ return coll;
28
+ }
29
+
30
+ async withMongoClient(fn: (db: MongoClient) => Promise<void>, url: string, mongoClientOptions?: MongoClientOptions) {
31
+ const mongoClient = new MongoClient(url, mongoClientOptions);
32
+
33
+ return await mongoClient
34
+ .connect()
35
+ .then(async client => {
36
+ return await fn(client);
37
+ })
38
+ .finally(async () => {
39
+ await mongoClient.close();
40
+ });
41
+ }
42
+ }
package/src/mongo-lock.js CHANGED
@@ -24,10 +24,9 @@ class MongoLock {
24
24
  getCollection() {
25
25
  return __awaiter(this, void 0, void 0, function* () {
26
26
  const locksColl = yield mongo_helper_1.MongoHelper.collectionSafeGet(this.client, 'locks');
27
- yield locksColl.indexExists('expiresAt')
28
- .then((exists) => __awaiter(this, void 0, void 0, function* () {
27
+ yield locksColl.indexExists('expiresAt').then((exists) => __awaiter(this, void 0, void 0, function* () {
29
28
  if (!exists)
30
- yield locksColl.createIndex({ "expiresAt": 1 }, { expireAfterSeconds: 0 });
29
+ yield locksColl.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
31
30
  }));
32
31
  return locksColl;
33
32
  });
@@ -72,10 +71,22 @@ class MongoLock {
72
71
  const delay = 200;
73
72
  const count = Math.round((1000 / delay) * ttlSeconds);
74
73
  return (0, rxjs_1.lastValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(this.acquireLock(name, ttlSeconds))).pipe((0, rxjs_1.map)(locked => {
75
- console.log(locked);
74
+ // console.log(locked);
76
75
  if (!locked)
77
76
  throw locked;
78
- }), wait ? (0, rxjs_1.retry)({ count, delay }) : rxjs_1.identity, (0, rxjs_1.switchMap)(() => fn().finally(() => this.releaseLock(name).catch(noop_1.default)))));
77
+ }), wait ? (0, rxjs_1.retry)({ count, delay }) : rxjs_1.identity, (0, rxjs_1.switchMap)(() => __awaiter(this, void 0, void 0, function* () { return fn().finally(() => this.releaseLock(name).catch(noop_1.default)); }))));
78
+ });
79
+ }
80
+ checkLock(name, waitSeconds = 0) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ const delay = 200;
83
+ const count = Math.round((1000 / delay) * waitSeconds);
84
+ const collection = yield this.getCollection();
85
+ return (0, rxjs_1.lastValueFrom)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(collection.findOne({ _id: name }))).pipe((0, rxjs_1.map)(lock => {
86
+ // console.log(lock);
87
+ if (lock)
88
+ throw lock;
89
+ }), waitSeconds > 0 ? (0, rxjs_1.retry)({ count, delay }) : rxjs_1.identity));
79
90
  });
80
91
  }
81
92
  }
package/src/mongo-lock.ts CHANGED
@@ -1,72 +1,85 @@
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
- import {MongoHelper} from "./mongo-helper";
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
+ import { MongoHelper } from './mongo-helper';
6
6
 
7
7
  export class MongoLock {
8
+ constructor(private client: MongoClient) {}
8
9
 
9
- constructor(private client: MongoClient) {
10
- }
11
-
12
- async getCollection() {
13
- const locksColl = await MongoHelper.collectionSafeGet(this.client, 'locks' );
14
- await locksColl.indexExists('expiresAt')
15
- .then(async exists => {
16
- if (!exists) await locksColl.createIndex({"expiresAt": 1}, {expireAfterSeconds: 0});
17
- });
18
- return locksColl;
19
- }
10
+ async getCollection() {
11
+ const locksColl = await MongoHelper.collectionSafeGet(this.client, 'locks');
12
+ await locksColl.indexExists('expiresAt').then(async exists => {
13
+ if (!exists) await locksColl.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
14
+ });
15
+ return locksColl;
16
+ }
20
17
 
21
- /**
22
- * Create the MongoDB collection and an expiring index on a field named "expiresAt".
23
- *
24
- * db.createCollection('locks');
25
- * db.locks.createIndex( { "expiresAt": 1 }, { expireAfterSeconds: 0 } )
26
- **/
27
- async acquireLock(name: string, ttlSeconds: number) {
28
- const collection = await this.getCollection();
18
+ /**
19
+ * Create the MongoDB collection and an expiring index on a field named "expiresAt".
20
+ *
21
+ * db.createCollection('locks');
22
+ * db.locks.createIndex( { "expiresAt": 1 }, { expireAfterSeconds: 0 } )
23
+ **/
24
+ async acquireLock(name: string, ttlSeconds: number) {
25
+ const collection = await this.getCollection();
29
26
 
30
- // Entry gets removed automatically due to an expiry index in Mongo
31
- const expiresAt = moment().add(ttlSeconds, 'seconds').toDate();
27
+ // Entry gets removed automatically due to an expiry index in Mongo
28
+ const expiresAt = moment().add(ttlSeconds, 'seconds').toDate();
32
29
 
33
- try {
34
- await collection.insertOne({
35
- _id: name as any,
36
- expiresAt,
37
- });
30
+ try {
31
+ await collection.insertOne({
32
+ _id: name as any,
33
+ expiresAt,
34
+ });
38
35
 
39
- return true;
40
- } catch (ex) {
41
- // 11000 means duplicate key error, which is expected on an active lock
42
- if ((ex as MongoError)?.code !== 11000) {
43
- // Unexpected error, what happened here :o
44
- throw ex
45
- }
36
+ return true;
37
+ } catch (ex) {
38
+ // 11000 means duplicate key error, which is expected on an active lock
39
+ if ((ex as MongoError)?.code !== 11000) {
40
+ // Unexpected error, what happened here :o
41
+ throw ex;
42
+ }
46
43
 
47
- // As we got a duplicate key exception, no lock could be acquired
48
- return false;
49
- }
44
+ // As we got a duplicate key exception, no lock could be acquired
45
+ return false;
50
46
  }
47
+ }
51
48
 
52
- async releaseLock(name: string) {
53
- const collection = await this.getCollection();
54
- return collection.deleteOne({_id: name as any});
55
- }
49
+ async releaseLock(name: string) {
50
+ const collection = await this.getCollection();
51
+ return collection.deleteOne({ _id: name as any });
52
+ }
56
53
 
57
- async acquireAndExecute(name: string, ttlSeconds: number, wait: boolean, fn: () => Promise<any>) {
58
- const delay = 200;
59
- const count = Math.round((1000 / delay) * ttlSeconds);
54
+ async acquireAndExecute<T = any>(name: string, ttlSeconds: number, wait: boolean, fn: () => Promise<T>) {
55
+ const delay = 200;
56
+ const count = Math.round((1000 / delay) * ttlSeconds);
60
57
 
61
- return lastValueFrom(
62
- defer(() => from(this.acquireLock(name, ttlSeconds))).pipe(
63
- map(locked => {
64
- console.log(locked);
65
- if (!locked) throw locked;
66
- }),
67
- wait ? retry({count, delay}) : identity,
68
- switchMap(() => fn().finally(() => this.releaseLock(name).catch(noop))),
69
- ),
70
- );
71
- }
72
- }
58
+ return lastValueFrom(
59
+ defer(() => from(this.acquireLock(name, ttlSeconds))).pipe(
60
+ map(locked => {
61
+ // console.log(locked);
62
+ if (!locked) throw locked;
63
+ }),
64
+ wait ? retry({ count, delay }) : identity,
65
+ switchMap(async () => fn().finally(() => this.releaseLock(name).catch(noop))),
66
+ ),
67
+ );
68
+ }
69
+
70
+ async checkLock(name: string, waitSeconds: number = 0) {
71
+ const delay = 200;
72
+ const count = Math.round((1000 / delay) * waitSeconds);
73
+ const collection = await this.getCollection();
74
+
75
+ return lastValueFrom(
76
+ defer(() => from(collection.findOne({ _id: name as any }))).pipe(
77
+ map(lock => {
78
+ // console.log(lock);
79
+ if (lock) throw lock;
80
+ }),
81
+ waitSeconds > 0 ? retry({ count, delay }) : identity,
82
+ ),
83
+ );
84
+ }
85
+ }
package/src/utils.js CHANGED
@@ -46,13 +46,17 @@ function colNumToA1(columnNumber) {
46
46
  charIdxArr.push(chars.length - 1);
47
47
  columnNumber = Math.floor(columnNumber / chars.length) - 1;
48
48
  }
49
- else { // If remainder is non-zero
49
+ else {
50
+ // If remainder is non-zero
50
51
  charIdxArr.push(rem - 1);
51
52
  columnNumber = Math.floor(columnNumber / chars.length);
52
53
  }
53
54
  }
54
55
  // Reverse the string and print result
55
- return charIdxArr.reverse().map((n) => chars[n]).join('');
56
+ return charIdxArr
57
+ .reverse()
58
+ .map(n => chars[n])
59
+ .join('');
56
60
  }
57
61
  exports.colNumToA1 = colNumToA1;
58
62
  function A1ToColNum(value) {
package/src/utils.ts CHANGED
@@ -3,55 +3,59 @@
3
3
  * @param seconds Google function timeout limit (max: 9 min)
4
4
  */
5
5
  export async function timeoutAfter(seconds: number = 540) {
6
- return new Promise<undefined>((resolve, reject) => {
7
- setTimeout(() => {
8
- reject(new Error(`${seconds} seconds timeout exceeded`));
9
- }, seconds * 1000);
10
- });
6
+ return new Promise<undefined>((resolve, reject) => {
7
+ setTimeout(() => {
8
+ reject(new Error(`${seconds} seconds timeout exceeded`));
9
+ }, seconds * 1000);
10
+ });
11
11
  }
12
12
 
13
13
  //
14
14
 
15
15
  export function indexToA1(idx: number) {
16
- return colNumToA1(idx + 1);
16
+ return colNumToA1(idx + 1);
17
17
  }
18
18
 
19
19
  export function A1ToIndex(value: string) {
20
- return A1ToColNum(value) - 1;
20
+ return A1ToColNum(value) - 1;
21
21
  }
22
22
 
23
23
  export function colNumToA1(columnNumber: number) {
24
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
25
-
26
- // To store result (Excel column name)
27
- const charIdxArr: number[] = [];
28
-
29
- while (columnNumber > 0) {
30
- // Find remainder
31
- const rem = columnNumber % chars.length;
32
-
33
- // If remainder is 0, then a
34
- // 'Z' must be there in output
35
- if (rem === 0) {
36
- charIdxArr.push(chars.length - 1);
37
- columnNumber = Math.floor(columnNumber / chars.length) - 1;
38
- } else { // If remainder is non-zero
39
- charIdxArr.push(rem - 1);
40
- columnNumber = Math.floor(columnNumber / chars.length);
41
- }
24
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
25
+
26
+ // To store result (Excel column name)
27
+ const charIdxArr: number[] = [];
28
+
29
+ while (columnNumber > 0) {
30
+ // Find remainder
31
+ const rem = columnNumber % chars.length;
32
+
33
+ // If remainder is 0, then a
34
+ // 'Z' must be there in output
35
+ if (rem === 0) {
36
+ charIdxArr.push(chars.length - 1);
37
+ columnNumber = Math.floor(columnNumber / chars.length) - 1;
38
+ } else {
39
+ // If remainder is non-zero
40
+ charIdxArr.push(rem - 1);
41
+ columnNumber = Math.floor(columnNumber / chars.length);
42
42
  }
43
+ }
43
44
 
44
- // Reverse the string and print result
45
- return charIdxArr.reverse().map((n) => chars[n]).join('');
45
+ // Reverse the string and print result
46
+ return charIdxArr
47
+ .reverse()
48
+ .map(n => chars[n])
49
+ .join('');
46
50
  }
47
51
 
48
52
  export function A1ToColNum(value: string) {
49
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
50
- let result = 0;
51
- // tslint:disable-next-line:prefer-for-of
52
- for (let i = 0; i < value.length; i++) {
53
- result *= chars.length;
54
- result += chars.indexOf(value[i]) + 1;
55
- }
56
- return result;
53
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
54
+ let result = 0;
55
+ // tslint:disable-next-line:prefer-for-of
56
+ for (let i = 0; i < value.length; i++) {
57
+ result *= chars.length;
58
+ result += chars.indexOf(value[i]) + 1;
59
+ }
60
+ return result;
57
61
  }