UNPKG

@telepilotco/n8n-nodes-kv-storage

Version:

Key-Value Storage Node for n8n supporting different scopes

335 lines 13.8 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.KvStorageService = exports.EventType = exports.Scope = void 0; require("reflect-metadata"); const typedi_1 = require("typedi"); const debug = require('debug')('kv-storage'); var Scope; (function (Scope) { Scope["ALL"] = "ALL"; Scope["EXECUTION"] = "EXECUTION"; Scope["WORKFLOW"] = "WORKFLOW"; Scope["INSTANCE"] = "INSTANCE"; })(Scope = exports.Scope || (exports.Scope = {})); var EventType; (function (EventType) { EventType["ANY"] = "ANY"; EventType["ADDED"] = "added"; EventType["UPDATED"] = "updated"; EventType["DELETED"] = "deleted"; })(EventType = exports.EventType || (exports.EventType = {})); let KvStorageService = class KvStorageService { constructor() { this.map = {}; this.mapExpiration = {}; this.workflowListenersMap = {}; this.instanceListeners = []; this.executionListeners = []; this.allListeners = []; debug('constructor'); setInterval(() => { debug('setInterval'); this.deleteExpiredEntries(); }, 1 * 1000); } deleteExpiredEntries() { const expirationKeys = Object.keys(this.mapExpiration); debug('deleteExpiredEntries.length=' + expirationKeys.length); const expiredKeys = expirationKeys.filter((k) => { return this.mapExpiration[k] <= Date.now(); }); expiredKeys.map((k) => { debug('deleteing expired key=' + k); this.deleteKeyWithScopedKey(k); }); } listAllKeyValuesInAllScopes() { debug('listAllKeyValuesInAllScopes: '); const mapKeys = Object.keys(this.map); const regExp = /scope:(\w+)-(.+):(.*)/; const matchedEntries = {}; mapKeys .filter((scopedKey) => scopedKey.match(regExp)) .map((scopedKey) => { const m = scopedKey.match(regExp); const scope = m[1]; const specifier = m[2]; const key = m[3]; const entryKey = `${scope}-${specifier}`; if (Object.keys(matchedEntries).includes(entryKey)) { matchedEntries[entryKey].entries[key] = this.map[scopedKey]; } else { const e = { scope, specifier, entries: { [key]: this.map[scopedKey] }, }; if (Object.keys(this.mapExpiration).includes(scopedKey)) { e.expiresAt = this.mapExpiration[scopedKey]; } matchedEntries[entryKey] = e; } }); return matchedEntries; } listAllKeysInScope(scope, specifier = '') { debug('getAllKeysInScope: scope=' + scope + ';specifier=' + specifier); const mapKeys = Object.keys(this.map); const regExp = new RegExp(`scope\\:${scope}-${specifier}\\:.*`, 'g'); const matchedKeys = mapKeys .filter((scopedKey) => scopedKey.match(regExp)) .map((scopedKey) => this.getKey(scopedKey)); return { keys: matchedKeys, scope, specifier }; } listAllKeyValuesInScope(scope, specifier = '') { debug('getAllKeyValuesInScope: scope=' + scope + ';specifier=' + specifier); const regExp = new RegExp(`scope\\:${scope}-${specifier}\\:.*`, 'g'); const mapKeys = Object.keys(this.map); const matchedKeys = mapKeys.filter((k) => k.match(regExp)); const matchedEntries = {}; matchedKeys.map((sK) => { const k = this.getKey(sK); matchedEntries[k] = this.map[sK]; }); return { entries: matchedEntries, scope, specifier }; } deleteKeyWithScopedKey(scopedKey) { const scopeKeyOf = this.getScope(scopedKey); const scope = Scope[scopeKeyOf]; const getSpecifier = this.getSpecifier(scopedKey); const key = this.getKey(scopedKey); return this.deleteKey(key, scope, getSpecifier); } deleteKey(key, scope, specifier = '') { debug('deleteKey: key=' + key + ';scope=' + scope + ';specifier=' + specifier); const scopedKey = this.composeScopeKey(key, scope, specifier); const mapKeys = Object.keys(this.map); let res = false; let val_ = []; const eventType = EventType.DELETED; const timestamp = Date.now(); if (mapKeys.includes(scopedKey)) { res = true; val_ = this.map[scopedKey]; delete this.map[scopedKey]; delete this.mapExpiration[scopedKey]; } const event = { eventType, scope, specifier, key, val: val_, timestamp, }; this.sendEvent(event, scope, specifier); return { res }; } getValue(key, scope, specifier = '') { debug('getValue: key=' + key + ';scope=' + scope + ';specifier=' + specifier); const scopedKey = this.composeScopeKey(key, scope, specifier); return { val: this.map[scopedKey] }; } incrementValue(key, scope, specifier = '', ttl = -1) { debug('incrementValue: key=' + key + ';scope=' + scope + ';specifier=' + specifier); const scopedKey = this.composeScopeKey(key, scope, specifier); let expiresAt = -1; if (ttl > -1) { expiresAt = Date.now() + ttl * 1000; this.mapExpiration[scopedKey] = expiresAt; debug('expiresAt=' + expiresAt); } const timestamp = Date.now(); let eventType = EventType.ADDED; if (Object.keys(this.map).includes(scopedKey)) { eventType = EventType.UPDATED; } let oldVal = Number(this.map[scopedKey]); if (!oldVal || oldVal === undefined) { oldVal = 0; } this.map[scopedKey] = [oldVal + 1]; const event = { eventType, scope, specifier, key, val: this.map[scopedKey], timestamp, expiresAt, }; if (Object.keys(this.map).includes(scopedKey)) { event.oldVal = oldVal; event.eventType = eventType; } this.sendEvent(event, scope, specifier); return { val: this.map[scopedKey] }; } setValue(key, val, scope, specifier = '', ttl = -1) { debug('setValue: key=' + key + ';val=' + val + ';scope=' + scope + ';specifier=' + specifier); const scopedKey = this.composeScopeKey(key, scope, specifier); let expiresAt = -1; if (ttl > -1) { expiresAt = Date.now() + ttl * 1000; this.mapExpiration[scopedKey] = expiresAt; debug('expiresAt=' + expiresAt); } const timestamp = Date.now(); let eventType = EventType.ADDED; if (Object.keys(this.map).includes(scopedKey)) { eventType = EventType.UPDATED; } const oldVal = this.map[scopedKey]; this.map[scopedKey] = [val]; const event = { eventType, scope, specifier, key, val, timestamp, expiresAt, }; if (Object.keys(this.map).includes(scopedKey)) { event.oldVal = oldVal; event.eventType = eventType; } this.sendEvent(event, scope, specifier); return { val: this.map[scopedKey] }; } sendEvent(event, scope, specifier) { if (this.allListeners.length > 0) { this.allListeners.map((callback) => callback(event)); } if (scope === Scope.INSTANCE && this.instanceListeners.length > 0) { this.instanceListeners.map((callback) => callback(event)); } if (scope === Scope.EXECUTION && this.executionListeners.length > 0) { this.executionListeners.map((callback) => callback(event)); } if (scope === Scope.WORKFLOW) { Object.keys(this.workflowListenersMap).map((k) => { if (specifier === k) { this.workflowListenersMap[Number(k)].map((callback) => callback(event)); } }); } } composeScopeKey(key, scope, specifier = '') { debug('composeScopeKey: key=' + key + ';scope=' + scope + ';specifier=' + specifier); if (scope === Scope.EXECUTION) { return `scope:${scope}-${specifier}:${key}`; } else if (scope === Scope.WORKFLOW) { return `scope:${scope}-${specifier}:${key}`; } else if (scope === Scope.INSTANCE) { return `scope:${scope}-${specifier}:${key}`; } const scopedKey = `scope:${scope}:${key}`; debug('scopedKey=' + scopedKey); return scopedKey; } getKey(scopedKey) { const match = scopedKey.match(/scope:\w+-.*:(.*)/); return match !== null ? match[1] : 'EMPTY'; } getScope(scopedKey) { const match = scopedKey.match(/scope:(\w+)-.*:.*/); return match !== null ? match[1] : 'EMPTY'; } getSpecifier(scopedKey) { const match = scopedKey.match(/scope:\w+-(.*):.*/); return match !== null ? match[1] : 'EMPTY'; } addListener(scope, specifier, callback) { debug('addListener: scope=' + scope + ';specifier=' + specifier); if (scope === Scope.WORKFLOW) { const workflowListenersMapKeys = Object.keys(this.workflowListenersMap); debug('workflowListenersMapKeys: ' + workflowListenersMapKeys); const keys = specifier .split(',') .map((s) => s.trim()) .filter((s) => s.length > 0); keys.map((key) => { if (!workflowListenersMapKeys.includes(key)) { debug('initialized with callback'); this.workflowListenersMap[Number(key)] = [callback]; } else { debug('pushed callback'); this.workflowListenersMap[Number(key)].push(callback); } }); } else if (scope === Scope.EXECUTION) { debug('pushed callback'); this.executionListeners.push(callback); debug('this.executionListeners.length=' + this.executionListeners.length); } else if (scope === Scope.INSTANCE) { debug('pushed callback'); this.instanceListeners.push(callback); debug('this.instanceListeners.length=' + this.instanceListeners.length); } else if (scope === Scope.ALL) { debug('pushed callback'); this.allListeners.push(callback); debug('this.allListeners.length=' + this.allListeners.length); } } removeListener(scope, specifier, callback) { debug('removeListener: scope=' + scope + ';specifier=' + specifier); if (scope === Scope.WORKFLOW) { const workflowListenersMapKeys = Object.keys(this.workflowListenersMap); const keys = specifier .split(',') .map((s) => s.trim()) .filter((s) => s.length > 0); keys.map((key) => { if (workflowListenersMapKeys.includes(key)) { debug('this.workflowListenersMap[' + key + '].length=' + this.workflowListenersMap[Number(key)].length); this.workflowListenersMap[Number(key)] = this.workflowListenersMap[Number(key)].filter((cb) => cb !== callback); debug('this.workflowListenersMap[' + key + '].length=' + this.workflowListenersMap[Number(key)].length); } }); } else if (scope === Scope.EXECUTION) { debug('this.executionListeners.length=' + this.executionListeners.length); this.executionListeners = this.executionListeners.filter((cb) => cb !== callback); debug('this.executionListeners.length=' + this.executionListeners.length); } else if (scope === Scope.INSTANCE) { debug('this.instanceListeners.length=' + this.instanceListeners.length); this.instanceListeners = this.instanceListeners.filter((cb) => cb !== callback); debug('this.instanceListeners.length=' + this.instanceListeners.length); } else if (scope === Scope.ALL) { debug('this.allListeners.length=' + this.allListeners.length); this.allListeners = this.allListeners.filter((cb) => cb !== callback); debug('this.allListeners.length=' + this.allListeners.length); } } }; KvStorageService = __decorate([ (0, typedi_1.Service)(), __metadata("design:paramtypes", []) ], KvStorageService); exports.KvStorageService = KvStorageService; //# sourceMappingURL=KvStorageService.js.map