gcf-common-lib 0.23.18 → 0.23.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gcf-common-lib",
3
3
  "description": "",
4
- "version": "0.23.18",
4
+ "version": "0.23.20",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "branches": [
@@ -21,6 +21,8 @@
21
21
  "@google-cloud/pubsub": "^3.3.0",
22
22
  "@google-cloud/secret-manager": "^4.2.1",
23
23
  "@google-cloud/storage": "^6.9.4",
24
+ "@types/amqplib": "^0.10.1",
25
+ "amqplib": "^0.10.3",
24
26
  "lodash": "^4.17.21",
25
27
  "moment": "^2.29.4",
26
28
  "mongodb": "^4.14.0",
package/src/index.js CHANGED
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  return (mod && mod.__esModule) ? mod : { "default": mod };
39
39
  };
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.GcfCommon = exports.secretClient = exports.storage = exports.pubSub = exports._mongodb = exports._rxjs = exports._secret_manager = exports._pubsub = exports._storage = void 0;
41
+ exports.GcfCommon = exports.secretClient = exports.storage = exports.pubSub = exports.MongoDb = exports.RxJs = exports.SecretManager = exports.PubSub = exports.Storage = void 0;
42
42
  const pubsub_1 = require("@google-cloud/pubsub");
43
43
  const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
44
44
  const noop_1 = __importDefault(require("lodash/noop"));
@@ -46,11 +46,11 @@ const utils_1 = require("./utils");
46
46
  const storage_1 = require("@google-cloud/storage");
47
47
  const secret_manager_1 = require("@google-cloud/secret-manager");
48
48
  const lodash_1 = require("lodash");
49
- exports._storage = __importStar(require("@google-cloud/storage"));
50
- exports._pubsub = __importStar(require("@google-cloud/pubsub"));
51
- exports._secret_manager = __importStar(require("@google-cloud/secret-manager"));
52
- exports._rxjs = __importStar(require("rxjs"));
53
- exports._mongodb = __importStar(require("mongodb"));
49
+ exports.Storage = __importStar(require("@google-cloud/storage"));
50
+ exports.PubSub = __importStar(require("@google-cloud/pubsub"));
51
+ exports.SecretManager = __importStar(require("@google-cloud/secret-manager"));
52
+ exports.RxJs = __importStar(require("rxjs"));
53
+ exports.MongoDb = __importStar(require("mongodb"));
54
54
  __exportStar(require("./types"), exports);
55
55
  __exportStar(require("./utils"), exports);
56
56
  exports.pubSub = new pubsub_1.PubSub();
@@ -88,14 +88,50 @@ class GcfCommon {
88
88
  }
89
89
  static publish(event, context, json, attributes) {
90
90
  return __awaiter(this, void 0, void 0, function* () {
91
- const { topic, appId, env, requestId } = yield this.getTopic(event, context);
92
- if (!(0, isEmpty_1.default)(topic)) {
91
+ const { topic, queue, appId, env, requestId } = yield this.getTopicAndQueue(event, context);
92
+ if (topic && !(0, isEmpty_1.default)(topic)) {
93
93
  console.log('publish:', topic, appId, env, json, attributes);
94
94
  return exports.pubSub.topic(topic).publishMessage({
95
95
  json: json !== null && json !== void 0 ? json : {},
96
96
  attributes: Object.assign(Object.assign({}, (0, lodash_1.mapValues)(attributes !== null && attributes !== void 0 ? attributes : {}, (v) => '' + v)), { requestId: requestId !== null && requestId !== void 0 ? requestId : '', appId: appId !== null && appId !== void 0 ? appId : '', env: env !== null && env !== void 0 ? env : '', type: 'response', response: '1' }),
97
97
  });
98
98
  }
99
+ if (queue && !(0, isEmpty_1.default)(queue)) {
100
+ console.log('send:', queue, appId, env, json, attributes);
101
+ return yield (0, utils_1.withAmqpCh)((ch) => __awaiter(this, void 0, void 0, function* () {
102
+ const payload = Buffer.from(JSON.stringify(json !== null && json !== void 0 ? json : {}));
103
+ yield ch.assertQueue(queue, this.amqpOptions.assertOptions);
104
+ ch.sendToQueue(queue, payload, this.amqpOptions.publishOptions);
105
+ }), this.amqpOptions.url);
106
+ }
107
+ });
108
+ }
109
+ static getTopicAndQueue(event, context) {
110
+ var _a, _b;
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ let { topic, queue, appId, env, requestId } = this.getMetadata(event, context);
113
+ if (!topic && !queue && ((_a = context === null || context === void 0 ? void 0 : context.resource) === null || _a === void 0 ? void 0 : _a.type) === 'storage#object') {
114
+ if ((context === null || context === void 0 ? void 0 : context.eventType) === 'google.storage.object.finalize') {
115
+ const gsEvent = event;
116
+ const [meta] = yield exports.storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
117
+ ({ topic, queue, appId, env, requestId } = (_b = meta === null || meta === void 0 ? void 0 : meta.metadata) !== null && _b !== void 0 ? _b : {});
118
+ }
119
+ }
120
+ return { topic, queue, appId, env, requestId };
121
+ });
122
+ }
123
+ static getQueue(event, context) {
124
+ var _a, _b;
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ let { queue, appId, env, requestId } = this.getMetadata(event, context);
127
+ if (!queue && ((_a = context === null || context === void 0 ? void 0 : context.resource) === null || _a === void 0 ? void 0 : _a.type) === 'storage#object') {
128
+ if ((context === null || context === void 0 ? void 0 : context.eventType) === 'google.storage.object.finalize') {
129
+ const gsEvent = event;
130
+ const [meta] = yield exports.storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
131
+ ({ queue, appId, env, requestId } = (_b = meta === null || meta === void 0 ? void 0 : meta.metadata) !== null && _b !== void 0 ? _b : {});
132
+ }
133
+ }
134
+ return { queue, appId, env, requestId };
99
135
  });
100
136
  }
101
137
  static getTopic(event, context) {
@@ -159,3 +195,7 @@ class GcfCommon {
159
195
  }
160
196
  }
161
197
  exports.GcfCommon = GcfCommon;
198
+ GcfCommon.amqpOptions = {
199
+ assertOptions: { durable: true },
200
+ publishOptions: { persistent: true },
201
+ };
package/src/index.ts CHANGED
@@ -1,18 +1,19 @@
1
1
  import { PubSub } from '@google-cloud/pubsub';
2
2
  import isEmpty from 'lodash/isEmpty';
3
3
  import noop from 'lodash/noop';
4
- import { timeoutAfter } from './utils';
4
+ import { timeoutAfter, withAmqpCh } from './utils';
5
5
  import { Storage } from '@google-cloud/storage';
6
6
  import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
7
7
  import { TContext, TEvent, TGSEvent, TMetadata, TPSEvent, TResponse } from './types';
8
8
  import { mapValues } from 'lodash';
9
+ import { Options } from 'amqplib/properties';
9
10
  import Dict = NodeJS.Dict;
10
11
 
11
- export * as _storage from '@google-cloud/storage';
12
- export * as _pubsub from '@google-cloud/pubsub';
13
- export * as _secret_manager from '@google-cloud/secret-manager';
14
- export * as _rxjs from 'rxjs';
15
- export * as _mongodb from 'mongodb';
12
+ export * as Storage from '@google-cloud/storage';
13
+ export * as PubSub from '@google-cloud/pubsub';
14
+ export * as SecretManager from '@google-cloud/secret-manager';
15
+ export * as RxJs from 'rxjs';
16
+ export * as MongoDb from 'mongodb';
16
17
  export * from './types';
17
18
  export * from './utils';
18
19
 
@@ -21,6 +22,15 @@ export const storage = new Storage();
21
22
  export const secretClient = new SecretManagerServiceClient();
22
23
 
23
24
  export class GcfCommon {
25
+ static amqpOptions: {
26
+ url?: string;
27
+ assertOptions?: Options.AssertQueue;
28
+ publishOptions?: Options.Publish;
29
+ } = {
30
+ assertOptions: { durable: true },
31
+ publishOptions: { persistent: true },
32
+ };
33
+
24
34
  /**
25
35
  *
26
36
  * @param {!TEvent} event Event payload.
@@ -54,11 +64,11 @@ export class GcfCommon {
54
64
  }
55
65
 
56
66
  static async publish(event: TEvent, context: TContext, json?: TResponse, attributes?: Dict<any>) {
57
- const { topic, appId, env, requestId } = await this.getTopic(event, context);
67
+ const { topic, queue, appId, env, requestId } = await this.getTopicAndQueue(event, context);
58
68
 
59
- if (!isEmpty(topic)) {
69
+ if (topic && !isEmpty(topic)) {
60
70
  console.log('publish:', topic, appId, env, json, attributes);
61
- return pubSub.topic(topic as string).publishMessage({
71
+ return pubSub.topic(topic).publishMessage({
62
72
  json: json ?? {},
63
73
  attributes: {
64
74
  ...mapValues(attributes ?? {}, (v) => '' + v),
@@ -70,6 +80,44 @@ export class GcfCommon {
70
80
  },
71
81
  });
72
82
  }
83
+
84
+ if (queue && !isEmpty(queue)) {
85
+ console.log('send:', queue, appId, env, json, attributes);
86
+ return await withAmqpCh(async ch => {
87
+ const payload = Buffer.from(JSON.stringify(json ?? {}));
88
+ await ch.assertQueue(queue, this.amqpOptions.assertOptions);
89
+ ch.sendToQueue(queue, payload, this.amqpOptions.publishOptions);
90
+ }, this.amqpOptions.url as string);
91
+ }
92
+ }
93
+
94
+
95
+ static async getTopicAndQueue(event: TEvent, context: TContext) {
96
+ let { topic, queue, appId, env, requestId } = this.getMetadata(event, context);
97
+
98
+ if (!topic && !queue && context?.resource?.type === 'storage#object') {
99
+ if (context?.eventType === 'google.storage.object.finalize') {
100
+ const gsEvent = event as TGSEvent;
101
+ const [meta] = await storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
102
+ ({ topic, queue, appId, env, requestId } = meta?.metadata ?? {});
103
+ }
104
+ }
105
+
106
+ return { topic, queue, appId, env, requestId };
107
+ }
108
+
109
+ static async getQueue(event: TEvent, context: TContext) {
110
+ let { queue, appId, env, requestId } = this.getMetadata(event, context);
111
+
112
+ if (!queue && context?.resource?.type === 'storage#object') {
113
+ if (context?.eventType === 'google.storage.object.finalize') {
114
+ const gsEvent = event as TGSEvent;
115
+ const [meta] = await storage.bucket(gsEvent.bucket).file(gsEvent.name).getMetadata();
116
+ ({ queue, appId, env, requestId } = meta?.metadata ?? {});
117
+ }
118
+ }
119
+
120
+ return { queue, appId, env, requestId };
73
121
  }
74
122
 
75
123
  static async getTopic(event: TEvent, context: TContext) {
package/src/types.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import Dict = NodeJS.Dict;
2
2
 
3
3
  export type TMetadata = {
4
- topic?: string; // response topic [t_{GUID}__{YYYY-MM-DD}]
4
+ topic?: string; // response PubSub topic [t_{GUID}__{YYYY-MM-DD}]
5
+ queue?: string; // response amqp queue
5
6
  requestId?: string; // for rpc response [GUID]
6
7
  env?: string; // app environment
7
8
  appId?: string; // app id
package/src/utils.js CHANGED
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.A1ToColNum = exports.colNumToA1 = exports.A1ToIndex = exports.indexToA1 = exports.delay = exports.timeoutAfter = void 0;
12
+ exports.withAmqpCh = exports.withAmqpConn = exports.A1ToColNum = exports.colNumToA1 = exports.A1ToIndex = exports.indexToA1 = exports.delay = exports.timeoutAfter = void 0;
13
+ const amqplib_1 = require("amqplib");
13
14
  /**
14
15
  *
15
16
  * @param seconds Google function v1 timeout limit (max: 9 min)
@@ -76,3 +77,23 @@ function A1ToColNum(value) {
76
77
  return result;
77
78
  }
78
79
  exports.A1ToColNum = A1ToColNum;
80
+ function withAmqpConn(fn, url) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ const amqpConn = yield (0, amqplib_1.connect)(url);
83
+ amqpConn.on('close', () => console.info('Amqp connection closed!'));
84
+ return yield fn(amqpConn).finally(() => __awaiter(this, void 0, void 0, function* () { return yield amqpConn.close(); }));
85
+ });
86
+ }
87
+ exports.withAmqpConn = withAmqpConn;
88
+ function withAmqpCh(fn, url) {
89
+ return __awaiter(this, void 0, void 0, function* () {
90
+ return yield withAmqpConn((conn) => __awaiter(this, void 0, void 0, function* () {
91
+ const ch = yield conn.createChannel();
92
+ return yield fn(ch).finally(() => __awaiter(this, void 0, void 0, function* () {
93
+ yield delay(1000);
94
+ yield ch.close();
95
+ }));
96
+ }), url);
97
+ });
98
+ }
99
+ exports.withAmqpCh = withAmqpCh;
package/src/utils.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Channel, connect, Connection } from 'amqplib';
2
+
1
3
  /**
2
4
  *
3
5
  * @param seconds Google function v1 timeout limit (max: 9 min)
@@ -63,3 +65,19 @@ export function A1ToColNum(value: string) {
63
65
  }
64
66
  return result;
65
67
  }
68
+
69
+ export async function withAmqpConn(fn: (conn: Connection) => Promise<any>, url: string) {
70
+ const amqpConn = await connect(url);
71
+ amqpConn.on('close', () => console.info('Amqp connection closed!'));
72
+ return await fn(amqpConn).finally(async () => await amqpConn.close());
73
+ }
74
+
75
+ export async function withAmqpCh(fn: (ch: Channel) => Promise<any>, url: string) {
76
+ return await withAmqpConn(async conn => {
77
+ const ch = await conn.createChannel();
78
+ return await fn(ch).finally(async () => {
79
+ await delay(1000);
80
+ await ch.close();
81
+ });
82
+ }, url);
83
+ }