gcf-common-lib 0.35.0 → 0.36.0

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.
@@ -0,0 +1,78 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { GcfCommon, TGSEvent, TPayload, TContext, TPSEvent } from '../src';
4
+
5
+ describe('GcfCommon.getMetadataOrAttribute', () => {
6
+ it('extracts metadata from GCS finalize event', () => {
7
+ const event: TGSEvent = {
8
+ bucket: 'b',
9
+ contentType: 'text/plain',
10
+ crc32c: 'x',
11
+ etag: 'e',
12
+ generation: '1',
13
+ id: 'id',
14
+ kind: 'storage#object',
15
+ md5Hash: 'm',
16
+ mediaLink: 'ml',
17
+ metadata: { action: '1', env: 'test' },
18
+ metageneration: '1',
19
+ name: 'file.txt',
20
+ selfLink: 'sl',
21
+ size: '10',
22
+ storageClass: 'sc',
23
+ timeCreated: new Date().toISOString(),
24
+ timeStorageClassUpdated: new Date().toISOString(),
25
+ updated: new Date().toISOString(),
26
+ };
27
+
28
+ const context: TContext = {
29
+ eventId: '1',
30
+ timestamp: `${Date.now()}`,
31
+ eventType: 'google.storage.object.finalize',
32
+ resource: {
33
+ name: 'file.txt',
34
+ service: 'storage.googleapis.com',
35
+ type: 'storage#object',
36
+ },
37
+ };
38
+
39
+ const payload: TPayload<TGSEvent> = { event, context };
40
+ const res = GcfCommon.getMetadataOrAttribute(payload);
41
+ assert.deepEqual(res, { a: '1', env: 'test' });
42
+ });
43
+
44
+ it('extracts attributes from Pub/Sub event', () => {
45
+ const event: TPSEvent = {
46
+ '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
47
+ json: { ok: 1 },
48
+ attributes: { action: 'y', request_id: 'r1' },
49
+ };
50
+ const context: TContext = {
51
+ eventId: '2',
52
+ timestamp: `${Date.now()}`,
53
+ eventType: 'google.pubsub.topic.publish',
54
+ resource: {
55
+ name: 'topic',
56
+ service: 'pubsub.googleapis.com',
57
+ type: 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
58
+ },
59
+ };
60
+ const payload: TPayload<TPSEvent> = { event, context };
61
+ const res = GcfCommon.getMetadataOrAttribute(payload);
62
+ assert.deepEqual(res, { x: 'y', request_id: 'r1' });
63
+ });
64
+
65
+ it('extracts attributes from HTTP-like request body', () => {
66
+ const payload: TPayload = {
67
+ request: { body: { message: { attributes: { foo: 'bar', queue: '' }, data: 'ZGF0YQ==' } } } as any,
68
+ };
69
+ const res = GcfCommon.getMetadataOrAttribute(payload);
70
+ assert.deepEqual(res, { foo: 'bar', queue: '' });
71
+ });
72
+
73
+ it('returns empty object if no context and no request', () => {
74
+ const payload: TPayload = {};
75
+ const res = GcfCommon.getMetadataOrAttribute(payload);
76
+ assert.deepEqual(res, {});
77
+ });
78
+ });
@@ -0,0 +1,12 @@
1
+ const { describe, it } = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+
4
+ describe('sample', () => {
5
+ it('adds numbers', () => {
6
+ assert.equal(1 + 1, 2);
7
+ });
8
+ it('async works', async () => {
9
+ const v = await Promise.resolve('ok');
10
+ assert.equal(v, 'ok');
11
+ });
12
+ });
package/test/test.js CHANGED
@@ -1,25 +1,30 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const node_test_1 = require("node:test");
4
- const src_1 = require("../src");
5
- (0, node_test_1.describe)('test', () => {
6
- const event = {
7
- '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
8
- json: { foo: 'bar' },
9
- attributes: { topic: 'test', env: 'test', options: '{}' },
10
- };
11
- const context = {
12
- eventId: '1',
13
- timestamp: `${Date.now()}`,
14
- eventType: 'google.pubsub.topic.publish',
15
- resource: {
16
- name: 'test',
17
- service: 'pubsub.googleapis.com',
18
- type: 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
19
- },
20
- };
21
- (0, node_test_1.it)('test', async () => {
22
- const res = await src_1.GcfCommon.process({ event, context }, async (p) => p.event?.data);
23
- console.log(res);
24
- });
25
- });
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_test_1 = require("node:test");
4
+ const src_1 = require("../src");
5
+ (0, node_test_1.describe)('test', () => {
6
+ const INTEGRATION_PUBSUB = process.env.INTEGRATION_PUBSUB === '1';
7
+ const event = {
8
+ '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
9
+ json: { foo: 'bar' },
10
+ attributes: {
11
+ ...(INTEGRATION_PUBSUB ? { topic: 'test' } : {}),
12
+ env: 'test',
13
+ options: '{}',
14
+ },
15
+ };
16
+ const context = {
17
+ eventId: '1',
18
+ timestamp: `${Date.now()}`,
19
+ eventType: 'google.pubsub.topic.publish',
20
+ resource: {
21
+ name: 'test',
22
+ service: 'pubsub.googleapis.com',
23
+ type: 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
24
+ },
25
+ };
26
+ (0, node_test_1.it)('test', async () => {
27
+ const res = await src_1.GcfCommon.process({ event, context }, async (p) => p.event?.data);
28
+ console.log(res);
29
+ });
30
+ });
package/test/test.ts CHANGED
@@ -1,26 +1,32 @@
1
- import { describe, it } from 'node:test';
2
- import { GcfCommon, TContext, TEvent } from '../src';
3
-
4
- describe('test', () => {
5
- const event: TEvent = {
6
- '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
7
- json: { foo: 'bar' },
8
- attributes: { topic: 'test', env: 'test', options: '{}' },
9
- };
10
-
11
- const context: TContext = {
12
- eventId: '1',
13
- timestamp: `${Date.now()}`,
14
- eventType: 'google.pubsub.topic.publish',
15
- resource: {
16
- name: 'test',
17
- service: 'pubsub.googleapis.com',
18
- type: 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
19
- },
20
- };
21
-
22
- it('test', async () => {
23
- const res = await GcfCommon.process({ event, context }, async p => p.event?.data);
24
- console.log(res);
25
- });
26
- });
1
+ import { describe, it } from 'node:test';
2
+ import { GcfCommon, TContext, TEvent } from '../src';
3
+
4
+ describe('test', () => {
5
+ const INTEGRATION_PUBSUB = process.env.INTEGRATION_PUBSUB === '1';
6
+
7
+ const event: TEvent = {
8
+ '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
9
+ json: { foo: 'bar' },
10
+ attributes: {
11
+ ...(INTEGRATION_PUBSUB ? { topic: 'test' } : {}),
12
+ env: 'test',
13
+ options: '{}',
14
+ },
15
+ };
16
+
17
+ const context: TContext = {
18
+ eventId: '1',
19
+ timestamp: `${Date.now()}`,
20
+ eventType: 'google.pubsub.topic.publish',
21
+ resource: {
22
+ name: 'test',
23
+ service: 'pubsub.googleapis.com',
24
+ type: 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
25
+ },
26
+ };
27
+
28
+ it('test', async () => {
29
+ const res = await GcfCommon.process({ event, context }, async p => p.event?.data);
30
+ console.log(res);
31
+ });
32
+ });
@@ -0,0 +1,8 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+
4
+ describe('ts tests harness noop', () => {
5
+ it('runs', () => {
6
+ assert.equal(1, 1);
7
+ });
8
+ });
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = require("node:test");
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const src_1 = require("../src");
9
+ (0, node_test_1.describe)('utils', () => {
10
+ (0, node_test_1.it)('sec and ms calculations', () => {
11
+ strict_1.default.equal((0, src_1.sec)({ s: 30 }), 30);
12
+ strict_1.default.equal((0, src_1.sec)({ m: 1 }), 60);
13
+ strict_1.default.equal((0, src_1.sec)({ m: 1, s: 30 }), 90);
14
+ strict_1.default.equal((0, src_1.sec)({ h: 1 }), 60 * 60);
15
+ strict_1.default.equal((0, src_1.sec)({ d: 1 }), 24 * 60 * 60);
16
+ strict_1.default.equal((0, src_1.sec)({ w: 1 }), 7 * 24 * 60 * 60);
17
+ strict_1.default.equal((0, src_1.ms)({ s: 1 }), 1e3);
18
+ strict_1.default.equal((0, src_1.ms)({ m: 1, s: 30 }), 90 * 1e3);
19
+ });
20
+ (0, node_test_1.it)('A1 conversions (index/colNum <-> A1)', () => {
21
+ strict_1.default.equal((0, src_1.indexToA1)(0), 'A');
22
+ strict_1.default.equal((0, src_1.indexToA1)(25), 'Z');
23
+ strict_1.default.equal((0, src_1.indexToA1)(26), 'AA');
24
+ strict_1.default.equal((0, src_1.A1ToIndex)('A'), 0);
25
+ strict_1.default.equal((0, src_1.A1ToIndex)('Z'), 25);
26
+ strict_1.default.equal((0, src_1.A1ToIndex)('AA'), 26);
27
+ strict_1.default.equal((0, src_1.colNumToA1)(1), 'A');
28
+ strict_1.default.equal((0, src_1.colNumToA1)(26), 'Z');
29
+ strict_1.default.equal((0, src_1.colNumToA1)(27), 'AA');
30
+ strict_1.default.equal((0, src_1.A1ToColNum)('A'), 1);
31
+ strict_1.default.equal((0, src_1.A1ToColNum)('Z'), 26);
32
+ strict_1.default.equal((0, src_1.A1ToColNum)('AA'), 27);
33
+ });
34
+ (0, node_test_1.it)('safeJsonParse handles valid, invalid, and no-fallback cases', () => {
35
+ const parsed = (0, src_1.safeJsonParse)('{"a":1}', {});
36
+ strict_1.default.deepEqual(parsed, { a: 1 });
37
+ const fallback = { ok: true };
38
+ const invalid = (0, src_1.safeJsonParse)('not-json', fallback);
39
+ strict_1.default.equal(invalid, fallback);
40
+ const noFallback = (0, src_1.safeJsonParse)('not-json');
41
+ strict_1.default.equal(noFallback, undefined);
42
+ });
43
+ });
@@ -0,0 +1,47 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ms, sec, indexToA1, A1ToIndex, colNumToA1, A1ToColNum, safeJsonParse } from '../src';
4
+
5
+ describe('utils', () => {
6
+ it('sec and ms calculations', () => {
7
+ assert.equal(sec({ s: 30 }), 30);
8
+ assert.equal(sec({ m: 1 }), 60);
9
+ assert.equal(sec({ m: 1, s: 30 }), 90);
10
+ assert.equal(sec({ h: 1 }), 60 * 60);
11
+ assert.equal(sec({ d: 1 }), 24 * 60 * 60);
12
+ assert.equal(sec({ w: 1 }), 7 * 24 * 60 * 60);
13
+
14
+ assert.equal(ms({ s: 1 }), 1e3);
15
+ assert.equal(ms({ m: 1, s: 30 }), 90 * 1e3);
16
+ });
17
+
18
+ it('A1 conversions (index/colNum <-> A1)', () => {
19
+ assert.equal(indexToA1(0), 'A');
20
+ assert.equal(indexToA1(25), 'Z');
21
+ assert.equal(indexToA1(26), 'AA');
22
+
23
+ assert.equal(A1ToIndex('A'), 0);
24
+ assert.equal(A1ToIndex('Z'), 25);
25
+ assert.equal(A1ToIndex('AA'), 26);
26
+
27
+ assert.equal(colNumToA1(1), 'A');
28
+ assert.equal(colNumToA1(26), 'Z');
29
+ assert.equal(colNumToA1(27), 'AA');
30
+
31
+ assert.equal(A1ToColNum('A'), 1);
32
+ assert.equal(A1ToColNum('Z'), 26);
33
+ assert.equal(A1ToColNum('AA'), 27);
34
+ });
35
+
36
+ it('safeJsonParse handles valid, invalid, and no-fallback cases', () => {
37
+ const parsed = safeJsonParse('{"a":1}', {} as any);
38
+ assert.deepEqual(parsed, { a: 1 });
39
+
40
+ const fallback = { ok: true } as const;
41
+ const invalid = safeJsonParse('not-json', fallback);
42
+ assert.equal(invalid, fallback);
43
+
44
+ const noFallback = safeJsonParse('not-json');
45
+ assert.equal(noFallback, undefined);
46
+ });
47
+ });
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
- {
2
- "$schema": "http://json.schemastore.org/tsconfig",
3
- "extends": "@tsconfig/node20/tsconfig.json",
4
- "compilerOptions": {
5
- "newLine": "crlf",
6
- }
7
- }
1
+ {
2
+ "$schema": "http://json.schemastore.org/tsconfig",
3
+ "extends": "@tsconfig/node20/tsconfig.json",
4
+ "compilerOptions": {
5
+ "newLine": "crlf",
6
+ }
7
+ }
@@ -1,44 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AmqpHelper = void 0;
7
- const amqplib_1 = require("amqplib");
8
- const bluebird_1 = __importDefault(require("bluebird"));
9
- exports.AmqpHelper = {
10
- async withAmqpConn(fn, url) {
11
- function withDisposer() {
12
- return bluebird_1.default.method(async () => {
13
- const amqpConn = await (0, amqplib_1.connect)(url);
14
- amqpConn.on('close', () => console.info('Amqp connection closed!'));
15
- return amqpConn;
16
- })().disposer((channelModel, promise) => channelModel.close());
17
- }
18
- return bluebird_1.default.using(withDisposer(), channelModel => fn(channelModel));
19
- },
20
- async withAmqpCh(fn, url, useConfirmChannel = false, prefetch = 1) {
21
- return exports.AmqpHelper.withAmqpConn(async (channelModel) => {
22
- function withDisposer() {
23
- return bluebird_1.default.method(async () => {
24
- const ch = useConfirmChannel ? await channelModel.createConfirmChannel() : await channelModel.createChannel();
25
- await ch.prefetch(prefetch);
26
- return ch;
27
- })().disposer((ch, promise) => ch.close());
28
- }
29
- return bluebird_1.default.using(withDisposer(), ch => fn(ch));
30
- }, url);
31
- },
32
- async publishAmqp(ch, exchange, routingKey, json, options) {
33
- const payload = Buffer.from(JSON.stringify(json));
34
- const keepSending = exchange
35
- ? ch.publish(exchange, routingKey, payload, options)
36
- : ch.sendToQueue(routingKey, payload, options);
37
- if (!keepSending)
38
- await new Promise(resolve => ch.once('drain', () => resolve()));
39
- },
40
- async sendToQueueConfAmqp(ch, queue, json, options) {
41
- const payload = Buffer.from(JSON.stringify(json));
42
- await new Promise((resolve, reject) => ch.sendToQueue(queue, payload, options, (err, ok) => (err ? reject(err) : resolve(ok))));
43
- },
44
- };