@toa.io/extensions.stash 0.20.0-dev.18

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 ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@toa.io/extensions.stash",
3
+ "version": "0.20.0-dev.18",
4
+ "description": "Toa Stash",
5
+ "author": "temich <tema.gurtovoy@gmail.com>",
6
+ "homepage": "https://github.com/toa-io/toa#readme",
7
+ "main": "transpiled/index.js",
8
+ "types": "transpiled/index.d.ts",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/toa-io/toa.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/toa-io/toa/issues"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "dependencies": {
20
+ "ioredis": "5.3.2",
21
+ "msgpackr": "1.9.5",
22
+ "redlock": "5.0.0-beta.2"
23
+ },
24
+ "scripts": {
25
+ "prepublishOnly": "npm run transpile",
26
+ "transpile": "tsc"
27
+ },
28
+ "jest": {
29
+ "preset": "ts-jest",
30
+ "testEnvironment": "node"
31
+ }
32
+ }
package/readme.md ADDED
@@ -0,0 +1,61 @@
1
+ # Toa Stash
2
+
3
+ Shared cache and distributed lock manager on top of [ioredis](https://github.com/redis/ioredis)
4
+ and [redlock](https://github.com/mike-marcacci/node-redlock).
5
+
6
+ ## Shared cache
7
+
8
+ `stash` aspect exposes [`ioredis` methods](https://redis.github.io/ioredis/classes/Cluster.html).
9
+
10
+ ```javascript
11
+ async function computation (input, context) {
12
+ await context.stash.set('key', 'value')
13
+ await context.stash.get('key')
14
+ }
15
+ ```
16
+
17
+ Keys are component-scoped, meaning that the underlying Redis keys are `namespace:name:key`.
18
+
19
+ ### Storing objects
20
+
21
+ `async store (key: string, value: object, ...args: Array<string | number>)`
22
+
23
+ `async fetch (key: string): object`
24
+
25
+ `...args` are the [arguments of `set`](https://redis.github.io/ioredis/classes/Cluster.html#set)
26
+ starting from third.
27
+
28
+ Values are encoded using [msgpack](https://msgpack.org).
29
+
30
+ ## Distributed lock manager
31
+
32
+ `async lock(id: string | string[], routine: async? () => void)`
33
+
34
+ Executes `routine` once a lock is succesively acquired. Lock ID is component-scoped.
35
+
36
+ ```javascript
37
+ async function computation (input, context) {
38
+ await context.stash.lock('lock id', () => console.log('Lock acquired'))
39
+ }
40
+ ```
41
+
42
+ ## Manifest
43
+
44
+ To enable extension for a component, add `null` definition to its manifest:
45
+
46
+ ```yaml
47
+ stash: ~
48
+ ```
49
+
50
+ ## Deployment
51
+
52
+ `stash` context annotation is a [Pointer](/libraries/pointer)-like.
53
+
54
+ ```yaml
55
+ stash: # shortcut is available
56
+ default: redis://redis.example.com
57
+ dummies.dummy: redis://dummies.redis.example.com
58
+ ```
59
+
60
+ Neither Password Authentication nor TLS are implemented.
61
+ [#367](https://github.com/toa-io/toa/issues/367)
@@ -0,0 +1,3 @@
1
+ export const Redlock = jest.fn(() => ({
2
+ using: jest.fn(async () => undefined)
3
+ }))
@@ -0,0 +1,103 @@
1
+ import Redlock from 'redlock'
2
+ import { encode } from 'msgpackr'
3
+ import { generate } from 'randomstring'
4
+ import { Aspect } from './Aspect'
5
+ import * as mock from './Connection.fixtures'
6
+ import type { Connection } from './Connection'
7
+ import type { Cluster } from 'ioredis'
8
+
9
+ jest.mock('redlock')
10
+
11
+ let aspect: Aspect
12
+ let connection: jest.MockedObject<Connection>
13
+ let redis: jest.MockedObject<Cluster>
14
+ let redlock: jest.MockedObject<Redlock>
15
+
16
+ const key = generate()
17
+
18
+ beforeEach(() => {
19
+ jest.clearAllMocks()
20
+
21
+ connection = new mock.Connection() as unknown as jest.MockedObject<Connection>
22
+ redis = connection.redis as unknown as jest.MockedObject<Cluster>
23
+ aspect = new Aspect(connection)
24
+ const m = (Redlock as unknown as jest.Mock<Redlock>).mock
25
+ redlock = (Redlock as unknown as jest.Mock<Redlock>).mock.instances[0] as unknown as jest.MockedObject<Redlock>
26
+ })
27
+
28
+ it('should depend on Connection', async () => {
29
+ expect(connection.link).toHaveBeenCalledWith(aspect)
30
+ })
31
+
32
+ it('should set', async () => {
33
+ const value = generate()
34
+
35
+ await aspect.invoke('set', key, value)
36
+
37
+ expect(redis.set).toHaveBeenCalledWith(key, value)
38
+ })
39
+
40
+ it('should get', async () => {
41
+ const value = await aspect.invoke('get', key)
42
+
43
+ expect(redis.get).toHaveBeenCalledWith(key)
44
+ expect(value).toStrictEqual(await redis.get.mock.results[0].value)
45
+ })
46
+
47
+ describe('buffers', () => {
48
+ it.each([
49
+ ['object', { [generate()]: generate() }],
50
+ ['array', [generate(), generate()]]
51
+ ])('should encode %s', async (_, value) => {
52
+ await aspect.invoke('store', key, value)
53
+
54
+ const buffer = encode(value)
55
+
56
+ expect(redis.set).toHaveBeenCalledWith(key, buffer)
57
+ })
58
+
59
+ it('should pass arguments', async () => {
60
+ const value = [1, 2]
61
+ const buffer = encode(value)
62
+
63
+ await aspect.invoke('store', key, value, 'EX', 1000)
64
+
65
+ expect(redis.set).toHaveBeenCalledWith(key, buffer, 'EX', 1000)
66
+ })
67
+
68
+ it.each([
69
+ ['object', { [generate()]: generate() }],
70
+ ['array', [generate(), generate()]]
71
+ ])('should decode %s', async (_, value) => {
72
+ const buffer = encode(value)
73
+
74
+ redis.getBuffer.mockImplementationOnce(async () => buffer)
75
+
76
+ const output = await aspect.invoke('fetch', key)
77
+
78
+ expect(redis.getBuffer).toHaveBeenCalledWith(key)
79
+ expect(output).toStrictEqual(value)
80
+ })
81
+ })
82
+
83
+ it('should create DLM', async () => {
84
+ expect(Redlock).toHaveBeenCalledWith([redis])
85
+ })
86
+
87
+ it('should acquire lock', async () => {
88
+ const keys = [generate(), generate()]
89
+ const callback = jest.fn()
90
+
91
+ await aspect.invoke('lock', keys, callback)
92
+
93
+ expect(redlock.using).toHaveBeenCalledWith(keys, 5000, callback)
94
+ })
95
+
96
+ it('should handle non-array key', async () => {
97
+ const key = generate()
98
+ const callback = jest.fn()
99
+
100
+ await aspect.invoke('lock', key, callback)
101
+
102
+ expect(redlock.using).toHaveBeenCalledWith([key], 5000, callback)
103
+ })
@@ -0,0 +1,58 @@
1
+ import Redlock from 'redlock'
2
+ import { encode, decode } from 'msgpackr'
3
+ import { Connector, type extensions } from '@toa.io/core'
4
+ import type { Connection } from './Connection'
5
+ import type { Redis } from 'ioredis'
6
+
7
+ export class Aspect extends Connector implements extensions.Aspect {
8
+ public readonly name = 'stash'
9
+ private readonly redis: Redis
10
+ private readonly redlock: Redlock
11
+
12
+ public constructor (connection: Connection) {
13
+ super()
14
+
15
+ this.redis = connection.redis
16
+ this.redlock = new Redlock([this.redis])
17
+
18
+ this.depends(connection)
19
+ }
20
+
21
+ public async invoke (method: 'store', key: string, value: object): Promise<void>
22
+ public async invoke (method: 'fetch', key: string): Promise<object>
23
+ public async invoke (method: 'lock', key: string | string[], routine: Routine): Promise<void>
24
+ public async invoke (method: string, ...args: unknown[]): Promise<any>
25
+ public async invoke (method: string, ...args: unknown[]): Promise<any> {
26
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
27
+ // @ts-expect-error
28
+ if (typeof this.redis[method] === 'function') return this.redis[method](...args)
29
+
30
+ if (method === 'store') await this.store(args[0] as string, args[1] as object, ...args.slice(2))
31
+
32
+ if (method === 'fetch') return await this.fetch(args[0] as string)
33
+
34
+ if (method === 'lock') await this.lock(args[0] as string, args[1] as () => Promise<void>)
35
+ }
36
+
37
+ private async store (key: string, value: object, ...args: unknown[]): Promise<void> {
38
+ const buffer = encode(value)
39
+
40
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
41
+ // @ts-expect-error
42
+ await this.redis.set(key, buffer, ...args)
43
+ }
44
+
45
+ private async fetch (key: string): Promise<object | null> {
46
+ const buffer = await this.redis.getBuffer(key)
47
+
48
+ return buffer === null ? null : decode(buffer)
49
+ }
50
+
51
+ private async lock (key: string | string[], routine: Routine): Promise<void> {
52
+ if (typeof key === 'string') key = [key]
53
+
54
+ await this.redlock.using(key, 5000, routine)
55
+ }
56
+ }
57
+
58
+ type Routine = () => Promise<void>
@@ -0,0 +1,18 @@
1
+ import { generate } from 'randomstring'
2
+ import type * as redis from 'ioredis'
3
+
4
+ export const Redis = jest.fn(() => ({
5
+ connect: jest.fn(async () => undefined),
6
+ disconnect: jest.fn(),
7
+ get: jest.fn(async () => generate()),
8
+ getBuffer: jest.fn(async () => Buffer.from(generate())),
9
+ set: jest.fn(async () => undefined),
10
+ on: jest.fn()
11
+ })) as unknown as jest.Mock<typeof redis.Cluster>
12
+
13
+ export const Connection = jest.fn(() => ({
14
+ redis: new Redis(),
15
+ link: jest.fn()
16
+ }))
17
+
18
+ export const ioredis = { Redis }
@@ -0,0 +1,51 @@
1
+ import { generate } from 'randomstring'
2
+ import { Connection } from './Connection'
3
+ import { Connector, type Locator } from '@toa.io/core'
4
+ import { Redis } from './Connection.fixtures'
5
+ import type * as redis from 'ioredis'
6
+
7
+ jest.mock('ioredis', () => ({
8
+ Redis: jest.fn((nodes: string[], options: redis.ClusterOptions) => new Redis(nodes, options))
9
+ }))
10
+
11
+ let connection: Connection
12
+ let cluster: jest.MockedObject<redis.Cluster>
13
+
14
+ const url = generate()
15
+
16
+ const locator = {
17
+ namespace: generate(),
18
+ name: generate()
19
+ } as unknown as Locator
20
+
21
+ beforeEach(() => {
22
+ jest.clearAllMocks()
23
+
24
+ connection = new Connection(url, locator)
25
+ cluster = Redis.mock.results[0].value
26
+ })
27
+
28
+ it('should be instance of Connector', async () => {
29
+ expect(connection).toBeInstanceOf(Connector)
30
+ })
31
+
32
+ it('should connect', async () => {
33
+ const keyPrefix = `${locator.namespace}:${locator.name}:`
34
+ const options: redis.ClusterOptions = { keyPrefix, enableReadyCheck: true }
35
+
36
+ expect(Redis).toHaveBeenCalledWith(url, options)
37
+
38
+ // await connection.connect()
39
+ //
40
+ // expect(cluster.connect).toHaveBeenCalled()
41
+ })
42
+
43
+ it('should disconnect', async () => {
44
+ await connection.disconnect()
45
+
46
+ expect(cluster.disconnect).toHaveBeenCalled()
47
+ })
48
+
49
+ it('should expose cluster', async () => {
50
+ expect(connection.redis).toStrictEqual(cluster)
51
+ })
@@ -0,0 +1,19 @@
1
+ import { Redis, type ClusterOptions } from 'ioredis'
2
+ import { Connector, type Locator } from '@toa.io/core'
3
+
4
+ export class Connection extends Connector {
5
+ public readonly redis: Redis
6
+
7
+ public constructor (url: string, locator: Locator) {
8
+ super()
9
+
10
+ const keyPrefix = `${locator.namespace}:${locator.name}:`
11
+ const options: ClusterOptions = { keyPrefix, enableReadyCheck: true }
12
+
13
+ this.redis = new Redis(url, options)
14
+ }
15
+
16
+ protected override async close (): Promise<void> {
17
+ this.redis.disconnect()
18
+ }
19
+ }
@@ -0,0 +1,46 @@
1
+ import { uris } from '@toa.io/pointer'
2
+ import { Aspect } from './Aspect'
3
+ import { Connection } from './Connection'
4
+ import type { Locator, extensions } from '@toa.io/core'
5
+ import type { context } from '@toa.io/norm'
6
+ import type * as operations from '@toa.io/operations'
7
+
8
+ export class Factory implements extensions.Factory {
9
+ public aspect (locator: Locator): extensions.Aspect {
10
+ const url = this.url()
11
+ const connection = new Connection(url, locator)
12
+
13
+ return new Aspect(connection)
14
+ }
15
+
16
+ private url (): string {
17
+ if (process.env.TOA_DEV === '1') return 'redis://localhost'
18
+
19
+ const url = process.env[URL_VARIABLE]
20
+
21
+ if (url === undefined) throw new Error(`${URL_VARIABLE} is not set`)
22
+
23
+ return url
24
+ }
25
+ }
26
+
27
+ export function deployment (instances: context.Dependency[], annotation: Annotation): Dependency {
28
+ const pointer = uris.construct(annotation)
29
+ const variables: Variables = {}
30
+
31
+ for (const instance of instances) {
32
+ const resolution = uris.resolve(instance.locator, pointer)
33
+ const variable: Variable = { name: URL_VARIABLE, value: resolution.url.href }
34
+
35
+ variables[instance.locator.label] = [variable]
36
+ }
37
+
38
+ return { variables }
39
+ }
40
+
41
+ const URL_VARIABLE = 'TOA_STASH'
42
+
43
+ type Dependency = operations.deployment.Dependency
44
+ type Variables = operations.deployment.Variables
45
+ type Variable = operations.deployment.Variable
46
+ type Annotation = uris.URIs | string
@@ -0,0 +1 @@
1
+ export * from './extension'
@@ -0,0 +1,17 @@
1
+ import { Connector, type extensions } from '@toa.io/core';
2
+ import type { Connection } from './Connection';
3
+ export declare class Aspect extends Connector implements extensions.Aspect {
4
+ readonly name = "stash";
5
+ private readonly redis;
6
+ private readonly redlock;
7
+ constructor(connection: Connection);
8
+ invoke(method: 'store', key: string, value: object): Promise<void>;
9
+ invoke(method: 'fetch', key: string): Promise<object>;
10
+ invoke(method: 'lock', key: string | string[], routine: Routine): Promise<void>;
11
+ invoke(method: string, ...args: unknown[]): Promise<any>;
12
+ private store;
13
+ private fetch;
14
+ private lock;
15
+ }
16
+ type Routine = () => Promise<void>;
17
+ export {};
@@ -0,0 +1,4 @@
1
+ /// <reference types="jest" />
2
+ export declare const Redlock: jest.Mock<{
3
+ using: jest.Mock<Promise<undefined>, []>;
4
+ }, []>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Redlock = void 0;
4
+ exports.Redlock = jest.fn(() => ({
5
+ using: jest.fn(async () => undefined)
6
+ }));
7
+ //# sourceMappingURL=Aspect.fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Aspect.fixtures.js","sourceRoot":"","sources":["../source/Aspect.fixtures.ts"],"names":[],"mappings":";;;AAAa,QAAA,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IACpC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;CACtC,CAAC,CAAC,CAAA"}
@@ -0,0 +1,49 @@
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.Aspect = void 0;
7
+ const redlock_1 = __importDefault(require("redlock"));
8
+ const msgpackr_1 = require("msgpackr");
9
+ const core_1 = require("@toa.io/core");
10
+ class Aspect extends core_1.Connector {
11
+ name = 'stash';
12
+ redis;
13
+ redlock;
14
+ constructor(connection) {
15
+ super();
16
+ this.redis = connection.redis;
17
+ this.redlock = new redlock_1.default([this.redis]);
18
+ this.depends(connection);
19
+ }
20
+ async invoke(method, ...args) {
21
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
+ // @ts-expect-error
23
+ if (typeof this.redis[method] === 'function')
24
+ return this.redis[method](...args);
25
+ if (method === 'store')
26
+ await this.store(args[0], args[1], ...args.slice(2));
27
+ if (method === 'fetch')
28
+ return await this.fetch(args[0]);
29
+ if (method === 'lock')
30
+ await this.lock(args[0], args[1]);
31
+ }
32
+ async store(key, value, ...args) {
33
+ const buffer = (0, msgpackr_1.encode)(value);
34
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
35
+ // @ts-expect-error
36
+ await this.redis.set(key, buffer, ...args);
37
+ }
38
+ async fetch(key) {
39
+ const buffer = await this.redis.getBuffer(key);
40
+ return buffer === null ? null : (0, msgpackr_1.decode)(buffer);
41
+ }
42
+ async lock(key, routine) {
43
+ if (typeof key === 'string')
44
+ key = [key];
45
+ await this.redlock.using(key, 5000, routine);
46
+ }
47
+ }
48
+ exports.Aspect = Aspect;
49
+ //# sourceMappingURL=Aspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA6B;AAC7B,uCAAyC;AACzC,uCAAyD;AAIzD,MAAa,MAAO,SAAQ,gBAAS;IACnB,IAAI,GAAG,OAAO,CAAA;IACb,KAAK,CAAO;IACZ,OAAO,CAAS;IAEjC,YAAoB,UAAsB;QACxC,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAA;QAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,iBAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAExC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC1B,CAAC;IAMM,KAAK,CAAC,MAAM,CAAE,MAAc,EAAE,GAAG,IAAe;QACrD,6DAA6D;QAC7D,mBAAmB;QACnB,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;QAEhF,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAW,EAAE,IAAI,CAAC,CAAC,CAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAEhG,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAW,CAAC,CAAA;QAElE,IAAI,MAAM,KAAK,MAAM;YAAE,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAW,EAAE,IAAI,CAAC,CAAC,CAAwB,CAAC,CAAA;IAC3F,CAAC;IAEO,KAAK,CAAC,KAAK,CAAE,GAAW,EAAE,KAAa,EAAE,GAAG,IAAe;QACjE,MAAM,MAAM,GAAG,IAAA,iBAAM,EAAC,KAAK,CAAC,CAAA;QAE5B,6DAA6D;QAC7D,mBAAmB;QACnB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;IAC5C,CAAC;IAEO,KAAK,CAAC,KAAK,CAAE,GAAW;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAE9C,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAA,iBAAM,EAAC,MAAM,CAAC,CAAA;IAChD,CAAC;IAEO,KAAK,CAAC,IAAI,CAAE,GAAsB,EAAE,OAAgB;QAC1D,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;QAExC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;CACF;AAjDD,wBAiDC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const redlock_1 = __importDefault(require("redlock"));
30
+ const msgpackr_1 = require("msgpackr");
31
+ const randomstring_1 = require("randomstring");
32
+ const Aspect_1 = require("./Aspect");
33
+ const mock = __importStar(require("./Connection.fixtures"));
34
+ jest.mock('redlock');
35
+ let aspect;
36
+ let connection;
37
+ let redis;
38
+ let redlock;
39
+ const key = (0, randomstring_1.generate)();
40
+ beforeEach(() => {
41
+ jest.clearAllMocks();
42
+ connection = new mock.Connection();
43
+ redis = connection.redis;
44
+ aspect = new Aspect_1.Aspect(connection);
45
+ const m = redlock_1.default.mock;
46
+ redlock = redlock_1.default.mock.instances[0];
47
+ });
48
+ it('should depend on Connection', async () => {
49
+ expect(connection.link).toHaveBeenCalledWith(aspect);
50
+ });
51
+ it('should set', async () => {
52
+ const value = (0, randomstring_1.generate)();
53
+ await aspect.invoke('set', key, value);
54
+ expect(redis.set).toHaveBeenCalledWith(key, value);
55
+ });
56
+ it('should get', async () => {
57
+ const value = await aspect.invoke('get', key);
58
+ expect(redis.get).toHaveBeenCalledWith(key);
59
+ expect(value).toStrictEqual(await redis.get.mock.results[0].value);
60
+ });
61
+ describe('buffers', () => {
62
+ it.each([
63
+ ['object', { [(0, randomstring_1.generate)()]: (0, randomstring_1.generate)() }],
64
+ ['array', [(0, randomstring_1.generate)(), (0, randomstring_1.generate)()]]
65
+ ])('should encode %s', async (_, value) => {
66
+ await aspect.invoke('store', key, value);
67
+ const buffer = (0, msgpackr_1.encode)(value);
68
+ expect(redis.set).toHaveBeenCalledWith(key, buffer);
69
+ });
70
+ it('should pass arguments', async () => {
71
+ const value = [1, 2];
72
+ const buffer = (0, msgpackr_1.encode)(value);
73
+ await aspect.invoke('store', key, value, 'EX', 1000);
74
+ expect(redis.set).toHaveBeenCalledWith(key, buffer, 'EX', 1000);
75
+ });
76
+ it.each([
77
+ ['object', { [(0, randomstring_1.generate)()]: (0, randomstring_1.generate)() }],
78
+ ['array', [(0, randomstring_1.generate)(), (0, randomstring_1.generate)()]]
79
+ ])('should decode %s', async (_, value) => {
80
+ const buffer = (0, msgpackr_1.encode)(value);
81
+ redis.getBuffer.mockImplementationOnce(async () => buffer);
82
+ const output = await aspect.invoke('fetch', key);
83
+ expect(redis.getBuffer).toHaveBeenCalledWith(key);
84
+ expect(output).toStrictEqual(value);
85
+ });
86
+ });
87
+ it('should create DLM', async () => {
88
+ expect(redlock_1.default).toHaveBeenCalledWith([redis]);
89
+ });
90
+ it('should acquire lock', async () => {
91
+ const keys = [(0, randomstring_1.generate)(), (0, randomstring_1.generate)()];
92
+ const callback = jest.fn();
93
+ await aspect.invoke('lock', keys, callback);
94
+ expect(redlock.using).toHaveBeenCalledWith(keys, 5000, callback);
95
+ });
96
+ it('should handle non-array key', async () => {
97
+ const key = (0, randomstring_1.generate)();
98
+ const callback = jest.fn();
99
+ await aspect.invoke('lock', key, callback);
100
+ expect(redlock.using).toHaveBeenCalledWith([key], 5000, callback);
101
+ });
102
+ //# sourceMappingURL=Aspect.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Aspect.test.js","sourceRoot":"","sources":["../source/Aspect.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { Redis } from 'ioredis';
2
+ import { Connector, type Locator } from '@toa.io/core';
3
+ export declare class Connection extends Connector {
4
+ readonly redis: Redis;
5
+ constructor(url: string, locator: Locator);
6
+ protected close(): Promise<void>;
7
+ }
@@ -0,0 +1,10 @@
1
+ /// <reference types="jest" />
2
+ import type * as redis from 'ioredis';
3
+ export declare const Redis: jest.Mock<typeof redis.Cluster, any>;
4
+ export declare const Connection: jest.Mock<{
5
+ redis: typeof redis.Cluster;
6
+ link: jest.Mock<any, any>;
7
+ }, []>;
8
+ export declare const ioredis: {
9
+ Redis: jest.Mock<typeof redis.Cluster, any>;
10
+ };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ioredis = exports.Connection = exports.Redis = void 0;
4
+ const randomstring_1 = require("randomstring");
5
+ exports.Redis = jest.fn(() => ({
6
+ connect: jest.fn(async () => undefined),
7
+ disconnect: jest.fn(),
8
+ get: jest.fn(async () => (0, randomstring_1.generate)()),
9
+ getBuffer: jest.fn(async () => Buffer.from((0, randomstring_1.generate)())),
10
+ set: jest.fn(async () => undefined),
11
+ on: jest.fn()
12
+ }));
13
+ exports.Connection = jest.fn(() => ({
14
+ redis: new exports.Redis(),
15
+ link: jest.fn()
16
+ }));
17
+ exports.ioredis = { Redis: exports.Redis };
18
+ //# sourceMappingURL=Connection.fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Connection.fixtures.js","sourceRoot":"","sources":["../source/Connection.fixtures.ts"],"names":[],"mappings":";;;AAAA,+CAAuC;AAG1B,QAAA,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAClC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;IACvC,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE;IACrB,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAA,uBAAQ,GAAE,CAAC;IACpC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAA,uBAAQ,GAAE,CAAC,CAAC;IACvD,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;IACnC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;CACd,CAAC,CAA+C,CAAA;AAEpC,QAAA,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IACvC,KAAK,EAAE,IAAI,aAAK,EAAE;IAClB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;CAChB,CAAC,CAAC,CAAA;AAEU,QAAA,OAAO,GAAG,EAAE,KAAK,EAAL,aAAK,EAAE,CAAA"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Connection = void 0;
4
+ const ioredis_1 = require("ioredis");
5
+ const core_1 = require("@toa.io/core");
6
+ class Connection extends core_1.Connector {
7
+ redis;
8
+ constructor(url, locator) {
9
+ super();
10
+ const keyPrefix = `${locator.namespace}:${locator.name}:`;
11
+ const options = { keyPrefix, enableReadyCheck: true };
12
+ this.redis = new ioredis_1.Redis(url, options);
13
+ }
14
+ async close() {
15
+ this.redis.disconnect();
16
+ }
17
+ }
18
+ exports.Connection = Connection;
19
+ //# sourceMappingURL=Connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Connection.js","sourceRoot":"","sources":["../source/Connection.ts"],"names":[],"mappings":";;;AAAA,qCAAoD;AACpD,uCAAsD;AAEtD,MAAa,UAAW,SAAQ,gBAAS;IACvB,KAAK,CAAO;IAE5B,YAAoB,GAAW,EAAE,OAAgB;QAC/C,KAAK,EAAE,CAAA;QAEP,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,GAAG,CAAA;QACzD,MAAM,OAAO,GAAmB,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAA;QAErE,IAAI,CAAC,KAAK,GAAG,IAAI,eAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACtC,CAAC;IAEkB,KAAK,CAAC,KAAK;QAC5B,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAA;IACzB,CAAC;CACF;AAfD,gCAeC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const randomstring_1 = require("randomstring");
4
+ const Connection_1 = require("./Connection");
5
+ const core_1 = require("@toa.io/core");
6
+ const Connection_fixtures_1 = require("./Connection.fixtures");
7
+ jest.mock('ioredis', () => ({
8
+ Redis: jest.fn((nodes, options) => new Connection_fixtures_1.Redis(nodes, options))
9
+ }));
10
+ let connection;
11
+ let cluster;
12
+ const url = (0, randomstring_1.generate)();
13
+ const locator = {
14
+ namespace: (0, randomstring_1.generate)(),
15
+ name: (0, randomstring_1.generate)()
16
+ };
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ connection = new Connection_1.Connection(url, locator);
20
+ cluster = Connection_fixtures_1.Redis.mock.results[0].value;
21
+ });
22
+ it('should be instance of Connector', async () => {
23
+ expect(connection).toBeInstanceOf(core_1.Connector);
24
+ });
25
+ it('should connect', async () => {
26
+ const keyPrefix = `${locator.namespace}:${locator.name}:`;
27
+ const options = { keyPrefix, enableReadyCheck: true };
28
+ expect(Connection_fixtures_1.Redis).toHaveBeenCalledWith(url, options);
29
+ // await connection.connect()
30
+ //
31
+ // expect(cluster.connect).toHaveBeenCalled()
32
+ });
33
+ it('should disconnect', async () => {
34
+ await connection.disconnect();
35
+ expect(cluster.disconnect).toHaveBeenCalled();
36
+ });
37
+ it('should expose cluster', async () => {
38
+ expect(connection.redis).toStrictEqual(cluster);
39
+ });
40
+ //# sourceMappingURL=Connection.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Connection.test.js","sourceRoot":"","sources":["../source/Connection.test.ts"],"names":[],"mappings":";;AAAA,+CAAuC;AACvC,6CAAyC;AACzC,uCAAsD;AACtD,+DAA6C;AAG7C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,KAAe,EAAE,OAA6B,EAAE,EAAE,CAAC,IAAI,2BAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;CAC9F,CAAC,CAAC,CAAA;AAEH,IAAI,UAAsB,CAAA;AAC1B,IAAI,OAAyC,CAAA;AAE7C,MAAM,GAAG,GAAG,IAAA,uBAAQ,GAAE,CAAA;AAEtB,MAAM,OAAO,GAAG;IACd,SAAS,EAAE,IAAA,uBAAQ,GAAE;IACrB,IAAI,EAAE,IAAA,uBAAQ,GAAE;CACK,CAAA;AAEvB,UAAU,CAAC,GAAG,EAAE;IACd,IAAI,CAAC,aAAa,EAAE,CAAA;IAEpB,UAAU,GAAG,IAAI,uBAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACzC,OAAO,GAAG,2BAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AACvC,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;IAC/C,MAAM,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,gBAAS,CAAC,CAAA;AAC9C,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;IAC9B,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,GAAG,CAAA;IACzD,MAAM,OAAO,GAAyB,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAA;IAE3E,MAAM,CAAC,2BAAK,CAAC,CAAC,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAEhD,6BAA6B;IAC7B,EAAE;IACF,6CAA6C;AAC/C,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;IACjC,MAAM,UAAU,CAAC,UAAU,EAAE,CAAA;IAE7B,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,gBAAgB,EAAE,CAAA;AAC/C,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;IACrC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;AACjD,CAAC,CAAC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { uris } from '@toa.io/pointer';
2
+ import type { Locator, extensions } from '@toa.io/core';
3
+ import type { context } from '@toa.io/norm';
4
+ import type * as operations from '@toa.io/operations';
5
+ export declare class Factory implements extensions.Factory {
6
+ aspect(locator: Locator): extensions.Aspect;
7
+ private url;
8
+ }
9
+ export declare function deployment(instances: context.Dependency[], annotation: Annotation): Dependency;
10
+ type Dependency = operations.deployment.Dependency;
11
+ type Annotation = uris.URIs | string;
12
+ export {};
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deployment = exports.Factory = void 0;
4
+ const pointer_1 = require("@toa.io/pointer");
5
+ const Aspect_1 = require("./Aspect");
6
+ const Connection_1 = require("./Connection");
7
+ class Factory {
8
+ aspect(locator) {
9
+ const url = this.url();
10
+ const connection = new Connection_1.Connection(url, locator);
11
+ return new Aspect_1.Aspect(connection);
12
+ }
13
+ url() {
14
+ if (process.env.TOA_DEV === '1')
15
+ return 'redis://localhost';
16
+ const url = process.env[URL_VARIABLE];
17
+ if (url === undefined)
18
+ throw new Error(`${URL_VARIABLE} is not set`);
19
+ return url;
20
+ }
21
+ }
22
+ exports.Factory = Factory;
23
+ function deployment(instances, annotation) {
24
+ const pointer = pointer_1.uris.construct(annotation);
25
+ const variables = {};
26
+ for (const instance of instances) {
27
+ const resolution = pointer_1.uris.resolve(instance.locator, pointer);
28
+ const variable = { name: URL_VARIABLE, value: resolution.url.href };
29
+ variables[instance.locator.label] = [variable];
30
+ }
31
+ return { variables };
32
+ }
33
+ exports.deployment = deployment;
34
+ const URL_VARIABLE = 'TOA_STASH';
35
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../source/extension.ts"],"names":[],"mappings":";;;AAAA,6CAAsC;AACtC,qCAAiC;AACjC,6CAAyC;AAKzC,MAAa,OAAO;IACX,MAAM,CAAE,OAAgB;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,UAAU,GAAG,IAAI,uBAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAE/C,OAAO,IAAI,eAAM,CAAC,UAAU,CAAC,CAAA;IAC/B,CAAC;IAEO,GAAG;QACT,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,GAAG;YAAE,OAAO,mBAAmB,CAAA;QAE3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QAErC,IAAI,GAAG,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,YAAY,aAAa,CAAC,CAAA;QAEpE,OAAO,GAAG,CAAA;IACZ,CAAC;CACF;AAjBD,0BAiBC;AAED,SAAgB,UAAU,CAAE,SAA+B,EAAE,UAAsB;IACjF,MAAM,OAAO,GAAG,cAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;IAC1C,MAAM,SAAS,GAAc,EAAE,CAAA;IAE/B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;QAChC,MAAM,UAAU,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAC1D,MAAM,QAAQ,GAAa,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,CAAA;QAE7E,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;KAC/C;IAED,OAAO,EAAE,SAAS,EAAE,CAAA;AACtB,CAAC;AAZD,gCAYC;AAED,MAAM,YAAY,GAAG,WAAW,CAAA"}
@@ -0,0 +1 @@
1
+ export * from './extension';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./extension"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B"}
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./transpiled"
5
+ },
6
+ "include": [
7
+ "source"
8
+ ]
9
+ }