mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
feat(Redis Node): Update node-redis (no-changelog) (#8269)
Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
parent
3734c89cf6
commit
ab52aaf7e9
|
@ -148,7 +148,7 @@
|
|||
"openai": "4.20.0",
|
||||
"pdf-parse": "1.1.1",
|
||||
"pg": "8.11.3",
|
||||
"redis": "4.6.11",
|
||||
"redis": "4.6.12",
|
||||
"sqlite3": "5.1.6",
|
||||
"temp": "0.9.4",
|
||||
"typeorm": "0.3.17",
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import util from 'util';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeCredentialTestResult,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import set from 'lodash/set';
|
||||
import redis from 'redis';
|
||||
|
||||
import {
|
||||
setupRedisClient,
|
||||
redisConnectionTest,
|
||||
convertInfoToObject,
|
||||
getValue,
|
||||
setValue,
|
||||
} from './utils';
|
||||
|
||||
export class Redis implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -100,6 +100,23 @@ export class Redis implements INodeType {
|
|||
default: 'info',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// delete
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['delete'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'Name of the key to delete from Redis',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// get
|
||||
// ----------------------------------
|
||||
|
@ -117,19 +134,6 @@ export class Redis implements INodeType {
|
|||
description:
|
||||
'Name of the property to write received data to. Supports dot-notation. Example: "data.person[0].name".',
|
||||
},
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['delete'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'Name of the key to delete from Redis',
|
||||
},
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
|
@ -498,345 +502,135 @@ export class Redis implements INodeType {
|
|||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async redisConnectionTest(
|
||||
this: ICredentialTestFunctions,
|
||||
credential: ICredentialsDecrypted,
|
||||
): Promise<INodeCredentialTestResult> {
|
||||
const credentials = credential.data as ICredentialDataDecryptedObject;
|
||||
const redisOptions: redis.ClientOpts = {
|
||||
host: credentials.host as string,
|
||||
port: credentials.port as number,
|
||||
db: credentials.database as number,
|
||||
};
|
||||
|
||||
if (credentials.password) {
|
||||
redisOptions.password = credentials.password as string;
|
||||
}
|
||||
try {
|
||||
const client = redis.createClient(redisOptions);
|
||||
|
||||
await new Promise((resolve, reject): any => {
|
||||
client.on('connect', async () => {
|
||||
client.ping('ping', (error, pong) => {
|
||||
if (error) reject(error);
|
||||
resolve(pong);
|
||||
client.quit();
|
||||
});
|
||||
});
|
||||
client.on('error', async (err) => {
|
||||
client.quit();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
credentialTest: { redisConnectionTest },
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
// Parses the given value in a number if it is one else returns a string
|
||||
function getParsedValue(value: string): string | number {
|
||||
if (value.match(/^[\d\.]+$/) === null) {
|
||||
// Is a string
|
||||
return value;
|
||||
} else {
|
||||
// Is a number
|
||||
return parseFloat(value);
|
||||
}
|
||||
}
|
||||
async execute(this: IExecuteFunctions) {
|
||||
// TODO: For array and object fields it should not have a "value" field it should
|
||||
// have a parameter field for a path. Because it is not possible to set
|
||||
// array, object via parameter directly (should maybe be possible?!?!)
|
||||
// Should maybe have a parameter which is JSON.
|
||||
const credentials = await this.getCredentials('redis');
|
||||
|
||||
// Converts the Redis Info String into an object
|
||||
function convertInfoToObject(stringData: string): IDataObject {
|
||||
const returnData: IDataObject = {};
|
||||
const client = setupRedisClient(credentials);
|
||||
await client.connect();
|
||||
await client.ping();
|
||||
|
||||
let key: string, value: string;
|
||||
for (const line of stringData.split('\n')) {
|
||||
if (['#', ''].includes(line.charAt(0))) {
|
||||
continue;
|
||||
}
|
||||
[key, value] = line.split(':');
|
||||
if (key === undefined || value === undefined) {
|
||||
continue;
|
||||
}
|
||||
value = value.trim();
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
const returnItems: INodeExecutionData[] = [];
|
||||
|
||||
if (value.includes('=')) {
|
||||
returnData[key] = {};
|
||||
let key2: string, value2: string;
|
||||
for (const keyValuePair of value.split(',')) {
|
||||
[key2, value2] = keyValuePair.split('=');
|
||||
(returnData[key] as IDataObject)[key2] = getParsedValue(value2);
|
||||
}
|
||||
} else {
|
||||
returnData[key] = getParsedValue(value);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (operation === 'info') {
|
||||
const result = await client.info();
|
||||
returnItems.push({ json: convertInfoToObject(result) });
|
||||
} else if (
|
||||
['delete', 'get', 'keys', 'set', 'incr', 'publish', 'push', 'pop'].includes(operation)
|
||||
) {
|
||||
const items = this.getInputData();
|
||||
|
||||
return returnData;
|
||||
}
|
||||
let item: INodeExecutionData;
|
||||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||
item = { json: {} };
|
||||
|
||||
async function getValue(client: redis.RedisClient, keyName: string, type?: string) {
|
||||
if (type === undefined || type === 'automatic') {
|
||||
// Request the type first
|
||||
const clientType = util.promisify(client.type).bind(client);
|
||||
type = await clientType(keyName);
|
||||
}
|
||||
if (operation === 'delete') {
|
||||
const keyDelete = this.getNodeParameter('key', itemIndex) as string;
|
||||
|
||||
if (type === 'string') {
|
||||
const clientGet = util.promisify(client.get).bind(client);
|
||||
return clientGet(keyName);
|
||||
} else if (type === 'hash') {
|
||||
const clientHGetAll = util.promisify(client.hgetall).bind(client);
|
||||
return clientHGetAll(keyName);
|
||||
} else if (type === 'list') {
|
||||
const clientLRange = util.promisify(client.lrange).bind(client);
|
||||
return clientLRange(keyName, 0, -1);
|
||||
} else if (type === 'sets') {
|
||||
const clientSMembers = util.promisify(client.smembers).bind(client);
|
||||
return clientSMembers(keyName);
|
||||
}
|
||||
}
|
||||
await client.del(keyDelete);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'get') {
|
||||
const propertyName = this.getNodeParameter('propertyName', itemIndex) as string;
|
||||
const keyGet = this.getNodeParameter('key', itemIndex) as string;
|
||||
const keyType = this.getNodeParameter('keyType', itemIndex) as string;
|
||||
|
||||
const setValue = async (
|
||||
client: redis.RedisClient,
|
||||
keyName: string,
|
||||
value: string | number | object | string[] | number[],
|
||||
expire: boolean,
|
||||
ttl: number,
|
||||
type?: string,
|
||||
valueIsJSON?: boolean,
|
||||
) => {
|
||||
if (type === undefined || type === 'automatic') {
|
||||
// Request the type first
|
||||
if (typeof value === 'string') {
|
||||
type = 'string';
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'list';
|
||||
} else if (typeof value === 'object') {
|
||||
type = 'hash';
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Could not identify the type to set. Please set it manually!',
|
||||
);
|
||||
}
|
||||
}
|
||||
const value = (await getValue(client, keyGet, keyType)) ?? null;
|
||||
|
||||
if (type === 'string') {
|
||||
const clientSet = util.promisify(client.set).bind(client);
|
||||
await clientSet(keyName, value.toString());
|
||||
} else if (type === 'hash') {
|
||||
const clientHset = util.promisify(client.hset).bind(client);
|
||||
if (valueIsJSON) {
|
||||
let values: unknown;
|
||||
if (typeof value === 'string') {
|
||||
const options = this.getNodeParameter('options', itemIndex, {});
|
||||
|
||||
if (options.dotNotation === false) {
|
||||
item.json[propertyName] = value;
|
||||
} else {
|
||||
set(item.json, propertyName, value);
|
||||
}
|
||||
|
||||
returnItems.push(item);
|
||||
} else if (operation === 'keys') {
|
||||
const keyPattern = this.getNodeParameter('keyPattern', itemIndex) as string;
|
||||
const getValues = this.getNodeParameter('getValues', itemIndex, true) as boolean;
|
||||
|
||||
const keys = await client.keys(keyPattern);
|
||||
|
||||
if (!getValues) {
|
||||
returnItems.push({ json: { keys } });
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const keyName of keys) {
|
||||
item.json[keyName] = await getValue(client, keyName);
|
||||
}
|
||||
returnItems.push(item);
|
||||
} else if (operation === 'set') {
|
||||
const keySet = this.getNodeParameter('key', itemIndex) as string;
|
||||
const value = this.getNodeParameter('value', itemIndex) as string;
|
||||
const keyType = this.getNodeParameter('keyType', itemIndex) as string;
|
||||
const valueIsJSON = this.getNodeParameter('valueIsJSON', itemIndex, true) as boolean;
|
||||
const expire = this.getNodeParameter('expire', itemIndex, false) as boolean;
|
||||
const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number;
|
||||
|
||||
await setValue.call(this, client, keySet, value, expire, ttl, keyType, valueIsJSON);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'incr') {
|
||||
const keyIncr = this.getNodeParameter('key', itemIndex) as string;
|
||||
const expire = this.getNodeParameter('expire', itemIndex, false) as boolean;
|
||||
const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number;
|
||||
const incrementVal = await client.incr(keyIncr);
|
||||
if (expire && ttl > 0) {
|
||||
await client.expire(keyIncr, ttl);
|
||||
}
|
||||
returnItems.push({ json: { [keyIncr]: incrementVal } });
|
||||
} else if (operation === 'publish') {
|
||||
const channel = this.getNodeParameter('channel', itemIndex) as string;
|
||||
const messageData = this.getNodeParameter('messageData', itemIndex) as string;
|
||||
await client.publish(channel, messageData);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'push') {
|
||||
const redisList = this.getNodeParameter('list', itemIndex) as string;
|
||||
const messageData = this.getNodeParameter('messageData', itemIndex) as string;
|
||||
const tail = this.getNodeParameter('tail', itemIndex, false) as boolean;
|
||||
await client[tail ? 'rPush' : 'lPush'](redisList, messageData);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'pop') {
|
||||
const redisList = this.getNodeParameter('list', itemIndex) as string;
|
||||
const tail = this.getNodeParameter('tail', itemIndex, false) as boolean;
|
||||
const propertyName = this.getNodeParameter(
|
||||
'propertyName',
|
||||
itemIndex,
|
||||
'propertyName',
|
||||
) as string;
|
||||
|
||||
const value = await client[tail ? 'rPop' : 'lPop'](redisList);
|
||||
|
||||
let outputValue;
|
||||
try {
|
||||
values = JSON.parse(value);
|
||||
outputValue = value && JSON.parse(value);
|
||||
} catch {
|
||||
// This is how we originally worked and prevents a breaking change
|
||||
values = value;
|
||||
outputValue = value;
|
||||
}
|
||||
} else {
|
||||
values = value;
|
||||
}
|
||||
for (const key of Object.keys(values as object)) {
|
||||
// @ts-ignore
|
||||
await clientHset(keyName, key, (values as IDataObject)[key]!.toString());
|
||||
}
|
||||
} else {
|
||||
const values = value.toString().split(' ');
|
||||
//@ts-ignore
|
||||
await clientHset(keyName, values);
|
||||
}
|
||||
} else if (type === 'list') {
|
||||
const clientLset = util.promisify(client.lset).bind(client);
|
||||
for (let index = 0; index < (value as string[]).length; index++) {
|
||||
await clientLset(keyName, index, (value as IDataObject)[index]!.toString());
|
||||
}
|
||||
} else if (type === 'sets') {
|
||||
const clientSadd = util.promisify(client.sadd).bind(client);
|
||||
//@ts-ignore
|
||||
await clientSadd(keyName, value);
|
||||
}
|
||||
|
||||
if (expire) {
|
||||
const clientExpire = util.promisify(client.expire).bind(client);
|
||||
await clientExpire(keyName, ttl);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// TODO: For array and object fields it should not have a "value" field it should
|
||||
// have a parameter field for a path. Because it is not possible to set
|
||||
// array, object via parameter directly (should maybe be possible?!?!)
|
||||
// Should maybe have a parameter which is JSON.
|
||||
const credentials = await this.getCredentials('redis');
|
||||
|
||||
const redisOptions: redis.ClientOpts = {
|
||||
host: credentials.host as string,
|
||||
port: credentials.port as number,
|
||||
db: credentials.database as number,
|
||||
};
|
||||
|
||||
if (credentials.password) {
|
||||
redisOptions.password = credentials.password as string;
|
||||
}
|
||||
|
||||
const client = redis.createClient(redisOptions);
|
||||
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
|
||||
client.on('error', (err: Error) => {
|
||||
client.quit();
|
||||
reject(err);
|
||||
});
|
||||
|
||||
client.on('ready', async (_err: Error | null) => {
|
||||
client.select(credentials.database as number);
|
||||
try {
|
||||
if (operation === 'info') {
|
||||
const clientInfo = util.promisify(client.info).bind(client);
|
||||
const result = await clientInfo();
|
||||
|
||||
resolve([[{ json: convertInfoToObject(result as string) }]]);
|
||||
client.quit();
|
||||
} else if (
|
||||
['delete', 'get', 'keys', 'set', 'incr', 'publish', 'push', 'pop'].includes(operation)
|
||||
) {
|
||||
const items = this.getInputData();
|
||||
const returnItems: INodeExecutionData[] = [];
|
||||
|
||||
let item: INodeExecutionData;
|
||||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||
item = { json: {} };
|
||||
|
||||
if (operation === 'delete') {
|
||||
const keyDelete = this.getNodeParameter('key', itemIndex) as string;
|
||||
|
||||
const clientDel = util.promisify(client.del).bind(client);
|
||||
// @ts-ignore
|
||||
await clientDel(keyDelete);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'get') {
|
||||
const propertyName = this.getNodeParameter('propertyName', itemIndex) as string;
|
||||
const keyGet = this.getNodeParameter('key', itemIndex) as string;
|
||||
const keyType = this.getNodeParameter('keyType', itemIndex) as string;
|
||||
|
||||
const value = (await getValue(client, keyGet, keyType)) || null;
|
||||
|
||||
const options = this.getNodeParameter('options', itemIndex, {});
|
||||
|
||||
if (options.dotNotation === false) {
|
||||
item.json[propertyName] = value;
|
||||
} else {
|
||||
set(item.json, propertyName, value);
|
||||
}
|
||||
|
||||
returnItems.push(item);
|
||||
} else if (operation === 'keys') {
|
||||
const keyPattern = this.getNodeParameter('keyPattern', itemIndex) as string;
|
||||
const getValues = this.getNodeParameter('getValues', itemIndex, true) as boolean;
|
||||
|
||||
const clientKeys = util.promisify(client.keys).bind(client);
|
||||
const keys = await clientKeys(keyPattern);
|
||||
|
||||
if (!getValues) {
|
||||
returnItems.push({ json: { keys } });
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const keyName of keys) {
|
||||
item.json[keyName] = await getValue(client, keyName);
|
||||
}
|
||||
returnItems.push(item);
|
||||
} else if (operation === 'set') {
|
||||
const keySet = this.getNodeParameter('key', itemIndex) as string;
|
||||
const value = this.getNodeParameter('value', itemIndex) as string;
|
||||
const keyType = this.getNodeParameter('keyType', itemIndex) as string;
|
||||
const valueIsJSON = this.getNodeParameter(
|
||||
'valueIsJSON',
|
||||
itemIndex,
|
||||
true,
|
||||
) as boolean;
|
||||
const expire = this.getNodeParameter('expire', itemIndex, false) as boolean;
|
||||
const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number;
|
||||
|
||||
await setValue(client, keySet, value, expire, ttl, keyType, valueIsJSON);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'incr') {
|
||||
const keyIncr = this.getNodeParameter('key', itemIndex) as string;
|
||||
const expire = this.getNodeParameter('expire', itemIndex, false) as boolean;
|
||||
const ttl = this.getNodeParameter('ttl', itemIndex, -1) as number;
|
||||
const clientIncr = util.promisify(client.incr).bind(client);
|
||||
// @ts-ignore
|
||||
const incrementVal = await clientIncr(keyIncr);
|
||||
if (expire && ttl > 0) {
|
||||
const clientExpire = util.promisify(client.expire).bind(client);
|
||||
await clientExpire(keyIncr, ttl);
|
||||
}
|
||||
returnItems.push({ json: { [keyIncr]: incrementVal } });
|
||||
} else if (operation === 'publish') {
|
||||
const channel = this.getNodeParameter('channel', itemIndex) as string;
|
||||
const messageData = this.getNodeParameter('messageData', itemIndex) as string;
|
||||
const clientPublish = util.promisify(client.publish).bind(client);
|
||||
await clientPublish(channel, messageData);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'push') {
|
||||
const redisList = this.getNodeParameter('list', itemIndex) as string;
|
||||
const messageData = this.getNodeParameter('messageData', itemIndex) as string;
|
||||
const tail = this.getNodeParameter('tail', itemIndex, false) as boolean;
|
||||
const action = tail ? client.RPUSH : client.LPUSH;
|
||||
const clientPush = util.promisify(action).bind(client);
|
||||
// @ts-ignore: typescript not understanding generic function signatures
|
||||
await clientPush(redisList, messageData);
|
||||
returnItems.push(items[itemIndex]);
|
||||
} else if (operation === 'pop') {
|
||||
const redisList = this.getNodeParameter('list', itemIndex) as string;
|
||||
const tail = this.getNodeParameter('tail', itemIndex, false) as boolean;
|
||||
const propertyName = this.getNodeParameter(
|
||||
'propertyName',
|
||||
itemIndex,
|
||||
'propertyName',
|
||||
) as string;
|
||||
|
||||
const action = tail ? client.rpop : client.lpop;
|
||||
const clientPop = util.promisify(action).bind(client);
|
||||
const value = await clientPop(redisList);
|
||||
|
||||
let outputValue;
|
||||
try {
|
||||
outputValue = JSON.parse(value);
|
||||
} catch {
|
||||
outputValue = value;
|
||||
}
|
||||
const options = this.getNodeParameter('options', itemIndex, {});
|
||||
if (options.dotNotation === false) {
|
||||
item.json[propertyName] = outputValue;
|
||||
} else {
|
||||
set(item.json, propertyName, outputValue);
|
||||
}
|
||||
returnItems.push(item);
|
||||
}
|
||||
const options = this.getNodeParameter('options', itemIndex, {});
|
||||
if (options.dotNotation === false) {
|
||||
item.json[propertyName] = outputValue;
|
||||
} else {
|
||||
set(item.json, propertyName, outputValue);
|
||||
}
|
||||
|
||||
client.quit();
|
||||
resolve([returnItems]);
|
||||
returnItems.push(item);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
await client.quit();
|
||||
}
|
||||
|
||||
return [returnItems];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-loop-func */
|
||||
import type {
|
||||
ITriggerFunctions,
|
||||
IDataObject,
|
||||
|
@ -7,7 +8,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import redis from 'redis';
|
||||
import { redisConnectionTest, setupRedisClient } from './utils';
|
||||
|
||||
export class RedisTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -26,6 +27,7 @@ export class RedisTrigger implements INodeType {
|
|||
{
|
||||
name: 'redis',
|
||||
required: true,
|
||||
testedBy: 'redisConnectionTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
|
@ -64,36 +66,29 @@ export class RedisTrigger implements INodeType {
|
|||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: { redisConnectionTest },
|
||||
};
|
||||
|
||||
async trigger(this: ITriggerFunctions): Promise<ITriggerResponse> {
|
||||
const credentials = await this.getCredentials('redis');
|
||||
|
||||
const redisOptions: redis.ClientOpts = {
|
||||
host: credentials.host as string,
|
||||
port: credentials.port as number,
|
||||
db: credentials.database as number,
|
||||
};
|
||||
|
||||
if (credentials.password) {
|
||||
redisOptions.password = credentials.password as string;
|
||||
}
|
||||
|
||||
const channels = (this.getNodeParameter('channels') as string).split(',');
|
||||
|
||||
const options = this.getNodeParameter('options') as IDataObject;
|
||||
|
||||
if (!channels) {
|
||||
throw new NodeOperationError(this.getNode(), 'Channels are mandatory!');
|
||||
}
|
||||
|
||||
const client = redis.createClient(redisOptions);
|
||||
const client = setupRedisClient(credentials);
|
||||
|
||||
const manualTriggerFunction = async () => {
|
||||
await new Promise((resolve, reject) => {
|
||||
client.on('connect', () => {
|
||||
for (const channel of channels) {
|
||||
client.psubscribe(channel);
|
||||
}
|
||||
client.on('pmessage', (pattern: string, channel: string, message: string) => {
|
||||
await client.connect();
|
||||
await client.ping();
|
||||
|
||||
try {
|
||||
for (const channel of channels) {
|
||||
await client.pSubscribe(channel, (message) => {
|
||||
if (options.jsonParseBody) {
|
||||
try {
|
||||
message = JSON.parse(message);
|
||||
|
@ -102,19 +97,15 @@ export class RedisTrigger implements INodeType {
|
|||
|
||||
if (options.onlyMessage) {
|
||||
this.emit([this.helpers.returnJsonArray({ message })]);
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit([this.helpers.returnJsonArray({ channel, message })]);
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
client.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new NodeOperationError(this.getNode(), error);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.getMode() === 'trigger') {
|
||||
|
@ -122,7 +113,7 @@ export class RedisTrigger implements INodeType {
|
|||
}
|
||||
|
||||
async function closeFunction() {
|
||||
client.quit();
|
||||
await client.quit();
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
158
packages/nodes-base/nodes/Redis/test/Redis.node.test.ts
Normal file
158
packages/nodes-base/nodes/Redis/test/Redis.node.test.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
import { mock } from 'jest-mock-extended';
|
||||
import type { RedisClientType } from '@redis/client';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import { Redis } from '../Redis.node';
|
||||
|
||||
const mockClient = mock<RedisClientType>();
|
||||
jest.mock('redis', () => ({
|
||||
createClient: () => mockClient,
|
||||
}));
|
||||
|
||||
describe('Redis Node', () => {
|
||||
const mockCredential = {
|
||||
host: 'redis',
|
||||
port: 1234,
|
||||
database: 0,
|
||||
password: 'random',
|
||||
};
|
||||
|
||||
const node = new Redis();
|
||||
const thisArg = mock<IExecuteFunctions>({});
|
||||
thisArg.getCredentials.calledWith('redis').mockResolvedValue(mockCredential);
|
||||
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
|
||||
afterEach(() => {
|
||||
expect(mockClient.connect).toHaveBeenCalled();
|
||||
expect(mockClient.ping).toHaveBeenCalled();
|
||||
expect(mockClient.quit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('info operation', async () => {
|
||||
thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('info');
|
||||
mockClient.info.mockResolvedValue(`
|
||||
# Server
|
||||
redis_version:6.2.14
|
||||
redis_git_sha1:00000000
|
||||
redis_git_dirty:0
|
||||
redis_mode:standalone
|
||||
arch_bits:64
|
||||
tcp_port:6379
|
||||
uptime_in_seconds:429905
|
||||
uptime_in_days:4
|
||||
|
||||
# Clients
|
||||
connected_clients:1
|
||||
cluster_connections:0
|
||||
max_clients:10000
|
||||
|
||||
# Memory
|
||||
used_memory:876648
|
||||
|
||||
# Replication
|
||||
role:master
|
||||
connected_slaves:0
|
||||
master_failover_state:no-failover
|
||||
`);
|
||||
|
||||
const output = await node.execute.call(thisArg);
|
||||
|
||||
expect(mockClient.info).toHaveBeenCalled();
|
||||
expect(output[0][0].json).toEqual({
|
||||
redis_version: 6.2,
|
||||
redis_git_sha1: 0,
|
||||
redis_git_dirty: 0,
|
||||
redis_mode: 'standalone',
|
||||
arch_bits: 64,
|
||||
tcp_port: 6379,
|
||||
uptime_in_seconds: 429905,
|
||||
uptime_in_days: 4,
|
||||
connected_clients: 1,
|
||||
cluster_connections: 0,
|
||||
max_clients: 10000,
|
||||
used_memory: 876648,
|
||||
role: 'master',
|
||||
connected_slaves: 0,
|
||||
master_failover_state: 'no-failover',
|
||||
});
|
||||
});
|
||||
|
||||
it('delete operation', async () => {
|
||||
thisArg.getInputData.mockReturnValue([{ json: { x: 1 } }]);
|
||||
thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('delete');
|
||||
thisArg.getNodeParameter.calledWith('key', 0).mockReturnValue('key1');
|
||||
mockClient.del.calledWith('key1').mockResolvedValue(1);
|
||||
|
||||
const output = await node.execute.call(thisArg);
|
||||
expect(mockClient.del).toHaveBeenCalledWith('key1');
|
||||
expect(output[0][0].json).toEqual({ x: 1 });
|
||||
});
|
||||
|
||||
describe('get operation', () => {
|
||||
beforeEach(() => {
|
||||
thisArg.getInputData.mockReturnValue([{ json: { x: 1 } }]);
|
||||
thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('get');
|
||||
thisArg.getNodeParameter.calledWith('options', 0).mockReturnValue({ dotNotation: true });
|
||||
thisArg.getNodeParameter.calledWith('key', 0).mockReturnValue('key1');
|
||||
thisArg.getNodeParameter.calledWith('propertyName', 0).mockReturnValue('x.y');
|
||||
});
|
||||
|
||||
it('keyType = automatic', async () => {
|
||||
thisArg.getNodeParameter.calledWith('keyType', 0).mockReturnValue('automatic');
|
||||
mockClient.type.calledWith('key1').mockResolvedValue('string');
|
||||
mockClient.get.calledWith('key1').mockResolvedValue('value');
|
||||
|
||||
const output = await node.execute.call(thisArg);
|
||||
expect(mockClient.type).toHaveBeenCalledWith('key1');
|
||||
expect(mockClient.get).toHaveBeenCalledWith('key1');
|
||||
expect(output[0][0].json).toEqual({ x: { y: 'value' } });
|
||||
});
|
||||
|
||||
it('keyType = hash', async () => {
|
||||
thisArg.getNodeParameter.calledWith('keyType', 0).mockReturnValue('hash');
|
||||
mockClient.hGetAll.calledWith('key1').mockResolvedValue({
|
||||
field1: '1',
|
||||
field2: '2',
|
||||
});
|
||||
|
||||
const output = await node.execute.call(thisArg);
|
||||
expect(mockClient.hGetAll).toHaveBeenCalledWith('key1');
|
||||
expect(output[0][0].json).toEqual({
|
||||
x: {
|
||||
y: {
|
||||
field1: '1',
|
||||
field2: '2',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('keys operation', () => {
|
||||
beforeEach(() => {
|
||||
thisArg.getInputData.mockReturnValue([{ json: { x: 1 } }]);
|
||||
thisArg.getNodeParameter.calledWith('operation', 0).mockReturnValue('keys');
|
||||
thisArg.getNodeParameter.calledWith('keyPattern', 0).mockReturnValue('key*');
|
||||
mockClient.keys.calledWith('key*').mockResolvedValue(['key1', 'key2']);
|
||||
});
|
||||
|
||||
it('getValues = false', async () => {
|
||||
thisArg.getNodeParameter.calledWith('getValues', 0).mockReturnValue(false);
|
||||
|
||||
const output = await node.execute.call(thisArg);
|
||||
expect(mockClient.keys).toHaveBeenCalledWith('key*');
|
||||
expect(output[0][0].json).toEqual({ keys: ['key1', 'key2'] });
|
||||
});
|
||||
|
||||
it('getValues = true', async () => {
|
||||
thisArg.getNodeParameter.calledWith('getValues', 0).mockReturnValue(true);
|
||||
mockClient.type.mockResolvedValue('string');
|
||||
mockClient.get.calledWith('key1').mockResolvedValue('value1');
|
||||
mockClient.get.calledWith('key2').mockResolvedValue('value2');
|
||||
|
||||
const output = await node.execute.call(thisArg);
|
||||
expect(mockClient.keys).toHaveBeenCalledWith('key*');
|
||||
expect(output[0][0].json).toEqual({ key1: 'value1', key2: 'value2' });
|
||||
});
|
||||
});
|
||||
});
|
168
packages/nodes-base/nodes/Redis/utils.ts
Normal file
168
packages/nodes-base/nodes/Redis/utils.ts
Normal file
|
@ -0,0 +1,168 @@
|
|||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestFunctions,
|
||||
ICredentialsDecrypted,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { createClient } from 'redis';
|
||||
export type RedisClientType = ReturnType<typeof createClient>;
|
||||
|
||||
export function setupRedisClient(credentials: ICredentialDataDecryptedObject): RedisClientType {
|
||||
const redisOptions = {
|
||||
socket: {
|
||||
host: credentials.host as string,
|
||||
port: credentials.port as number,
|
||||
},
|
||||
database: credentials.database as number,
|
||||
password: (credentials.password as string) || undefined,
|
||||
};
|
||||
|
||||
return createClient(redisOptions);
|
||||
}
|
||||
|
||||
export async function redisConnectionTest(
|
||||
this: ICredentialTestFunctions,
|
||||
credential: ICredentialsDecrypted,
|
||||
): Promise<INodeCredentialTestResult> {
|
||||
const credentials = credential.data as ICredentialDataDecryptedObject;
|
||||
|
||||
try {
|
||||
const client = setupRedisClient(credentials);
|
||||
await client.connect();
|
||||
await client.ping();
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Parses the given value in a number if it is one else returns a string */
|
||||
function getParsedValue(value: string): string | number {
|
||||
if (value.match(/^[\d\.]+$/) === null) {
|
||||
// Is a string
|
||||
return value;
|
||||
} else {
|
||||
// Is a number
|
||||
return parseFloat(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts the Redis Info String into an object */
|
||||
export function convertInfoToObject(stringData: string): IDataObject {
|
||||
const returnData: IDataObject = {};
|
||||
|
||||
let key: string, value: string;
|
||||
for (const line of stringData.split('\n')) {
|
||||
if (['#', ''].includes(line.charAt(0))) {
|
||||
continue;
|
||||
}
|
||||
[key, value] = line.split(':');
|
||||
if (key === undefined || value === undefined) {
|
||||
continue;
|
||||
}
|
||||
value = value.trim();
|
||||
|
||||
if (value.includes('=')) {
|
||||
returnData[key] = {};
|
||||
let key2: string, value2: string;
|
||||
for (const keyValuePair of value.split(',')) {
|
||||
[key2, value2] = keyValuePair.split('=');
|
||||
(returnData[key] as IDataObject)[key2] = getParsedValue(value2);
|
||||
}
|
||||
} else {
|
||||
returnData[key] = getParsedValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getValue(client: RedisClientType, keyName: string, type?: string) {
|
||||
if (type === undefined || type === 'automatic') {
|
||||
// Request the type first
|
||||
type = await client.type(keyName);
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
return client.get(keyName);
|
||||
} else if (type === 'hash') {
|
||||
return client.hGetAll(keyName);
|
||||
} else if (type === 'list') {
|
||||
return client.lRange(keyName, 0, -1);
|
||||
} else if (type === 'sets') {
|
||||
return client.sMembers(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
export async function setValue(
|
||||
this: IExecuteFunctions,
|
||||
client: RedisClientType,
|
||||
keyName: string,
|
||||
value: string | number | object | string[] | number[],
|
||||
expire: boolean,
|
||||
ttl: number,
|
||||
type?: string,
|
||||
valueIsJSON?: boolean,
|
||||
) {
|
||||
if (type === undefined || type === 'automatic') {
|
||||
// Request the type first
|
||||
if (typeof value === 'string') {
|
||||
type = 'string';
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'list';
|
||||
} else if (typeof value === 'object') {
|
||||
type = 'hash';
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Could not identify the type to set. Please set it manually!',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
await client.set(keyName, value.toString());
|
||||
} else if (type === 'hash') {
|
||||
if (valueIsJSON) {
|
||||
let values: unknown;
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
values = JSON.parse(value);
|
||||
} catch {
|
||||
// This is how we originally worked and prevents a breaking change
|
||||
values = value;
|
||||
}
|
||||
} else {
|
||||
values = value;
|
||||
}
|
||||
for (const key of Object.keys(values as object)) {
|
||||
await client.hSet(keyName, key, (values as IDataObject)[key]!.toString());
|
||||
}
|
||||
} else {
|
||||
const values = value.toString().split(' ');
|
||||
await client.hSet(keyName, values);
|
||||
}
|
||||
} else if (type === 'list') {
|
||||
for (let index = 0; index < (value as string[]).length; index++) {
|
||||
await client.lSet(keyName, index, (value as IDataObject)[index]!.toString());
|
||||
}
|
||||
} else if (type === 'sets') {
|
||||
//@ts-ignore
|
||||
await client.sAdd(keyName, value);
|
||||
}
|
||||
|
||||
if (expire) {
|
||||
await client.expire(keyName, ttl);
|
||||
}
|
||||
return;
|
||||
}
|
|
@ -816,7 +816,6 @@
|
|||
"@types/node-ssh": "^7.0.1",
|
||||
"@types/nodemailer": "^6.4.0",
|
||||
"@types/promise-ftp": "^1.3.4",
|
||||
"@types/redis": "^2.8.11",
|
||||
"@types/request-promise-native": "~1.0.15",
|
||||
"@types/rfc2047": "^2.0.1",
|
||||
"@types/showdown": "^1.9.4",
|
||||
|
@ -877,7 +876,7 @@
|
|||
"pretty-bytes": "5.6.0",
|
||||
"promise-ftp": "1.3.5",
|
||||
"pyodide": "0.23.4",
|
||||
"redis": "3.1.2",
|
||||
"redis": "4.6.12",
|
||||
"rfc2047": "4.0.1",
|
||||
"rhea": "1.0.24",
|
||||
"rss-parser": "3.12.0",
|
||||
|
|
|
@ -231,7 +231,7 @@ importers:
|
|||
version: 1.2.0
|
||||
langchain:
|
||||
specifier: 0.0.198
|
||||
version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17)
|
||||
version: 0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17)
|
||||
lodash:
|
||||
specifier: 4.17.21
|
||||
version: 4.17.21
|
||||
|
@ -257,8 +257,8 @@ importers:
|
|||
specifier: 8.11.3
|
||||
version: 8.11.3
|
||||
redis:
|
||||
specifier: 4.6.11
|
||||
version: 4.6.11
|
||||
specifier: 4.6.12
|
||||
version: 4.6.12
|
||||
sqlite3:
|
||||
specifier: 5.1.6
|
||||
version: 5.1.6
|
||||
|
@ -267,7 +267,7 @@ importers:
|
|||
version: 0.9.4
|
||||
typeorm:
|
||||
specifier: 0.3.17
|
||||
version: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.11)(sqlite3@5.1.6)
|
||||
version: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.6)
|
||||
zod:
|
||||
specifier: 3.22.4
|
||||
version: 3.22.4
|
||||
|
@ -1363,8 +1363,8 @@ importers:
|
|||
specifier: 0.23.4
|
||||
version: 0.23.4(patch_hash=kzcwsjcayy5m6iezu7r4tdimjq)
|
||||
redis:
|
||||
specifier: 3.1.2
|
||||
version: 3.1.2
|
||||
specifier: 4.6.12
|
||||
version: 4.6.12
|
||||
rfc2047:
|
||||
specifier: 4.0.1
|
||||
version: 4.0.1
|
||||
|
@ -1465,9 +1465,6 @@ importers:
|
|||
'@types/promise-ftp':
|
||||
specifier: ^1.3.4
|
||||
version: 1.3.4
|
||||
'@types/redis':
|
||||
specifier: ^2.8.11
|
||||
version: 2.8.32
|
||||
'@types/request-promise-native':
|
||||
specifier: ~1.0.15
|
||||
version: 1.0.18
|
||||
|
@ -7158,16 +7155,16 @@ packages:
|
|||
'@babel/runtime': 7.22.6
|
||||
dev: true
|
||||
|
||||
/@redis/bloom@1.2.0(@redis/client@1.5.12):
|
||||
/@redis/bloom@1.2.0(@redis/client@1.5.13):
|
||||
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
|
||||
peerDependencies:
|
||||
'@redis/client': ^1.0.0
|
||||
dependencies:
|
||||
'@redis/client': 1.5.12
|
||||
'@redis/client': 1.5.13
|
||||
dev: false
|
||||
|
||||
/@redis/client@1.5.12:
|
||||
resolution: {integrity: sha512-/ZjE18HRzMd80eXIIUIPcH81UoZpwulbo8FmbElrjPqH0QC0SeIKu1BOU49bO5trM5g895kAjhvalt5h77q+4A==}
|
||||
/@redis/client@1.5.13:
|
||||
resolution: {integrity: sha512-epkUM9D0Sdmt93/8Ozk43PNjLi36RZzG+d/T1Gdu5AI8jvghonTeLYV69WVWdilvFo+PYxbP0TZ0saMvr6nscQ==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
cluster-key-slot: 1.1.2
|
||||
|
@ -7175,36 +7172,36 @@ packages:
|
|||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/@redis/graph@1.1.1(@redis/client@1.5.12):
|
||||
/@redis/graph@1.1.1(@redis/client@1.5.13):
|
||||
resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==}
|
||||
peerDependencies:
|
||||
'@redis/client': ^1.0.0
|
||||
dependencies:
|
||||
'@redis/client': 1.5.12
|
||||
'@redis/client': 1.5.13
|
||||
dev: false
|
||||
|
||||
/@redis/json@1.0.6(@redis/client@1.5.12):
|
||||
/@redis/json@1.0.6(@redis/client@1.5.13):
|
||||
resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==}
|
||||
peerDependencies:
|
||||
'@redis/client': ^1.0.0
|
||||
dependencies:
|
||||
'@redis/client': 1.5.12
|
||||
'@redis/client': 1.5.13
|
||||
dev: false
|
||||
|
||||
/@redis/search@1.1.6(@redis/client@1.5.12):
|
||||
/@redis/search@1.1.6(@redis/client@1.5.13):
|
||||
resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==}
|
||||
peerDependencies:
|
||||
'@redis/client': ^1.0.0
|
||||
dependencies:
|
||||
'@redis/client': 1.5.12
|
||||
'@redis/client': 1.5.13
|
||||
dev: false
|
||||
|
||||
/@redis/time-series@1.0.5(@redis/client@1.5.12):
|
||||
/@redis/time-series@1.0.5(@redis/client@1.5.13):
|
||||
resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==}
|
||||
peerDependencies:
|
||||
'@redis/client': ^1.0.0
|
||||
dependencies:
|
||||
'@redis/client': 1.5.12
|
||||
'@redis/client': 1.5.13
|
||||
dev: false
|
||||
|
||||
/@rollup/plugin-alias@5.1.0(rollup@3.29.4):
|
||||
|
@ -10598,12 +10595,6 @@ packages:
|
|||
csstype: 3.1.1
|
||||
dev: true
|
||||
|
||||
/@types/redis@2.8.32:
|
||||
resolution: {integrity: sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==}
|
||||
dependencies:
|
||||
'@types/node': 18.16.16
|
||||
dev: true
|
||||
|
||||
/@types/replacestream@4.0.1:
|
||||
resolution: {integrity: sha512-3ecTmnzB90sgarVpIszCF1cX2cnxwqDovWb31jGrKfxAL0Knui1H7Reaz/zlT9zaE3u0un7L5cNy9fQPy0d2sg==}
|
||||
dev: true
|
||||
|
@ -18941,7 +18932,7 @@ packages:
|
|||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||
dev: false
|
||||
|
||||
/langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.11)(typeorm@0.3.17):
|
||||
/langchain@0.0.198(@aws-sdk/client-bedrock-runtime@3.454.0)(@aws-sdk/credential-provider-node@3.451.0)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@0.2.1)(@huggingface/inference@2.6.4)(@pinecone-database/pinecone@1.1.2)(@qdrant/js-client-rest@1.7.0)(@supabase/supabase-js@2.38.5)(@xata.io/client@0.25.3)(axios@1.6.2)(cohere-ai@6.2.2)(d3-dsv@2.0.0)(epub2@3.0.1)(html-to-text@9.0.5)(lodash@4.17.21)(mammoth@1.6.0)(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(typeorm@0.3.17):
|
||||
resolution: {integrity: sha512-YC0O1g8r61InCWyF5NmiQjdghdq6LKcgMrDZtqLbgDxAe4RoSldonm+5oNXS3yjCISG0j3s5Cty+yB7klqvUpg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
|
@ -19279,8 +19270,8 @@ packages:
|
|||
p-retry: 4.6.2
|
||||
pdf-parse: 1.1.1
|
||||
pg: 8.11.3
|
||||
redis: 4.6.11
|
||||
typeorm: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.11)(sqlite3@5.1.6)
|
||||
redis: 4.6.12
|
||||
typeorm: 0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.6)
|
||||
uuid: 9.0.0
|
||||
yaml: 2.3.4
|
||||
zod: 3.22.4
|
||||
|
@ -23233,25 +23224,15 @@ packages:
|
|||
dependencies:
|
||||
redis-errors: 1.2.0
|
||||
|
||||
/redis@3.1.2:
|
||||
resolution: {integrity: sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==}
|
||||
engines: {node: '>=10'}
|
||||
/redis@4.6.12:
|
||||
resolution: {integrity: sha512-41Xuuko6P4uH4VPe5nE3BqXHB7a9lkFL0J29AlxKaIfD6eWO8VO/5PDF9ad2oS+mswMsfFxaM5DlE3tnXT+P8Q==}
|
||||
dependencies:
|
||||
denque: 1.5.1
|
||||
redis-commands: 1.7.0
|
||||
redis-errors: 1.2.0
|
||||
redis-parser: 3.0.0
|
||||
dev: false
|
||||
|
||||
/redis@4.6.11:
|
||||
resolution: {integrity: sha512-kg1Lt4NZLYkAjPOj/WcyIGWfZfnyfKo1Wg9YKVSlzhFwxpFIl3LYI8BWy1Ab963LLDsTz2+OwdsesHKljB3WMQ==}
|
||||
dependencies:
|
||||
'@redis/bloom': 1.2.0(@redis/client@1.5.12)
|
||||
'@redis/client': 1.5.12
|
||||
'@redis/graph': 1.1.1(@redis/client@1.5.12)
|
||||
'@redis/json': 1.0.6(@redis/client@1.5.12)
|
||||
'@redis/search': 1.1.6(@redis/client@1.5.12)
|
||||
'@redis/time-series': 1.0.5(@redis/client@1.5.12)
|
||||
'@redis/bloom': 1.2.0(@redis/client@1.5.13)
|
||||
'@redis/client': 1.5.13
|
||||
'@redis/graph': 1.1.1(@redis/client@1.5.13)
|
||||
'@redis/json': 1.0.6(@redis/client@1.5.13)
|
||||
'@redis/search': 1.1.6(@redis/client@1.5.13)
|
||||
'@redis/time-series': 1.0.5(@redis/client@1.5.13)
|
||||
dev: false
|
||||
|
||||
/reflect-metadata@0.1.13:
|
||||
|
@ -25740,7 +25721,7 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/typeorm@0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.11)(sqlite3@5.1.6):
|
||||
/typeorm@0.3.17(mssql@9.1.1)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.6):
|
||||
resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==}
|
||||
engines: {node: '>= 12.9.0'}
|
||||
hasBin: true
|
||||
|
@ -25810,7 +25791,7 @@ packages:
|
|||
mkdirp: 2.1.3
|
||||
mssql: 9.1.1
|
||||
pg: 8.11.3
|
||||
redis: 4.6.11
|
||||
redis: 4.6.12
|
||||
reflect-metadata: 0.1.13
|
||||
sha.js: 2.4.11
|
||||
sqlite3: 5.1.6
|
||||
|
|
Loading…
Reference in a new issue