From 3547b2029397c3cad4fd41d8eb5bafc43fe05ad4 Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Mon, 31 Jan 2022 12:52:19 -0500 Subject: [PATCH] Graph (#1887) * init * implement graph commands * add graph to packages table * fix ts.infoDebug * fix redisearch tests * Update INFO_DEBUG.ts * fix INFO.spec.ts * test QUERY and SLOWLOG Co-authored-by: Avital-Fine --- README.md | 3 +- index.ts | 3 + package-lock.json | 73 ++++++++++++++----- packages/bloom/package.json | 2 +- packages/bloom/tsconfig.json | 4 + packages/client/package.json | 4 +- packages/graph/.nycrc.json | 4 + packages/graph/.release-it.json | 10 +++ packages/graph/README.md | 1 + .../graph/lib/commands/CONFIG_GET.spec.ts | 22 ++++++ packages/graph/lib/commands/CONFIG_GET.ts | 12 +++ .../graph/lib/commands/CONFIG_SET.spec.ts | 19 +++++ packages/graph/lib/commands/CONFIG_SET.ts | 10 +++ packages/graph/lib/commands/DELETE.spec.ts | 21 ++++++ packages/graph/lib/commands/DELETE.ts | 7 ++ packages/graph/lib/commands/EXPLAIN.spec.ts | 18 +++++ packages/graph/lib/commands/EXPLAIN.ts | 9 +++ packages/graph/lib/commands/LIST.spec.ts | 19 +++++ packages/graph/lib/commands/LIST.ts | 7 ++ packages/graph/lib/commands/PROFILE.spec.ts | 18 +++++ packages/graph/lib/commands/PROFILE.ts | 9 +++ packages/graph/lib/commands/QUERY.spec.ts | 22 ++++++ packages/graph/lib/commands/QUERY.ts | 43 +++++++++++ packages/graph/lib/commands/QUERY_RO.spec.ts | 22 ++++++ packages/graph/lib/commands/QUERY_RO.ts | 21 ++++++ packages/graph/lib/commands/SLOWLOG.spec.ts | 18 +++++ packages/graph/lib/commands/SLOWLOG.ts | 30 ++++++++ packages/graph/lib/commands/index.ts | 49 +++++++++++++ packages/graph/lib/index.ts | 1 + packages/graph/lib/test-utils.ts | 21 ++++++ packages/graph/package.json | 29 ++++++++ packages/graph/tsconfig.json | 20 +++++ packages/json/lib/test-utils.ts | 2 +- packages/json/package.json | 2 +- packages/search/lib/commands/CREATE.spec.ts | 2 +- .../search/lib/commands/DROPINDEX.spec.ts | 2 +- packages/search/lib/commands/INFO.spec.ts | 16 +++- packages/search/lib/commands/SYNDUMP.spec.ts | 5 +- .../search/lib/commands/SYNUPDATE.spec.ts | 5 +- packages/search/lib/test-utils.ts | 2 +- packages/search/package.json | 2 +- packages/test-utils/package.json | 2 +- .../lib/commands/INFO_DEBUG.spec.ts | 1 + .../time-series/lib/commands/INFO_DEBUG.ts | 6 +- packages/time-series/lib/test-utils.ts | 2 +- packages/time-series/package.json | 2 +- 46 files changed, 563 insertions(+), 39 deletions(-) create mode 100644 packages/graph/.nycrc.json create mode 100644 packages/graph/.release-it.json create mode 100644 packages/graph/README.md create mode 100644 packages/graph/lib/commands/CONFIG_GET.spec.ts create mode 100644 packages/graph/lib/commands/CONFIG_GET.ts create mode 100644 packages/graph/lib/commands/CONFIG_SET.spec.ts create mode 100644 packages/graph/lib/commands/CONFIG_SET.ts create mode 100644 packages/graph/lib/commands/DELETE.spec.ts create mode 100644 packages/graph/lib/commands/DELETE.ts create mode 100644 packages/graph/lib/commands/EXPLAIN.spec.ts create mode 100644 packages/graph/lib/commands/EXPLAIN.ts create mode 100644 packages/graph/lib/commands/LIST.spec.ts create mode 100644 packages/graph/lib/commands/LIST.ts create mode 100644 packages/graph/lib/commands/PROFILE.spec.ts create mode 100644 packages/graph/lib/commands/PROFILE.ts create mode 100644 packages/graph/lib/commands/QUERY.spec.ts create mode 100644 packages/graph/lib/commands/QUERY.ts create mode 100644 packages/graph/lib/commands/QUERY_RO.spec.ts create mode 100644 packages/graph/lib/commands/QUERY_RO.ts create mode 100644 packages/graph/lib/commands/SLOWLOG.spec.ts create mode 100644 packages/graph/lib/commands/SLOWLOG.ts create mode 100644 packages/graph/lib/commands/index.ts create mode 100644 packages/graph/lib/index.ts create mode 100644 packages/graph/lib/test-utils.ts create mode 100644 packages/graph/package.json create mode 100644 packages/graph/tsconfig.json diff --git a/README.md b/README.md index 0399595d8c..974407fce4 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,12 @@ node-redis is a modern, high performance [Redis](https://redis.io) client for No ## Packages -| Name | Description | +| Name | Description | |---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [redis](./) | [![Downloads](https://img.shields.io/npm/dm/redis.svg)](https://www.npmjs.com/package/redis) [![Version](https://img.shields.io/npm/v/redis.svg)](https://www.npmjs.com/package/redis) | | [@node-redis/client](./packages/client) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Version](https://img.shields.io/npm/v/@node-redis/client.svg)](https://www.npmjs.com/package/@node-redis/client) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/client/) | | [@node-redis/bloom](./packages/bloom) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Version](https://img.shields.io/npm/v/@node-redis/bloom.svg)](https://www.npmjs.com/package/@node-redis/bloom) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/bloom/) [Redis Bloom](https://oss.redis.com/redisbloom/) commands | +| [@node-redis/graph](./packages/graph) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/graph.svg)](https://www.npmjs.com/package/@node-redis/graph) [![Version](https://img.shields.io/npm/v/@node-redis/graph.svg)](https://www.npmjs.com/package/@node-redis/graph) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/graph/) [Redis Graph](https://oss.redis.com/redisgraph/) commands | | [@node-redis/json](./packages/json) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Version](https://img.shields.io/npm/v/@node-redis/json.svg)](https://www.npmjs.com/package/@node-redis/json) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/json/) [Redis JSON](https://oss.redis.com/redisjson/) commands | | [@node-redis/search](./packages/search) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Version](https://img.shields.io/npm/v/@node-redis/search.svg)](https://www.npmjs.com/package/@node-redis/search) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/search/) [Redis Search](https://oss.redis.com/redisearch/) commands | | [@node-redis/time-series](./packages/time-series) | [![Downloads](https://img.shields.io/npm/dm/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Version](https://img.shields.io/npm/v/@node-redis/time-series.svg)](https://www.npmjs.com/package/@node-redis/time-series) [![Docs](https://img.shields.io/badge/-documentation-dc382c)](https://redis.js.org/documentation/time-series/) [Redis Time-Series](https://oss.redis.com/redistimeseries/) commands | diff --git a/index.ts b/index.ts index 7f1eb386a4..63f21c582e 100644 --- a/index.ts +++ b/index.ts @@ -1,18 +1,21 @@ import { createClient as _createClient, createCluster as _createCluster, RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType } from '@node-redis/client'; import { RedisScripts } from '@node-redis/client/dist/lib/commands'; import RedisBloomModules from '@node-redis/bloom'; +import RedisGraph from '@node-redis/graph'; import RedisJSON from '@node-redis/json'; import RediSearch from '@node-redis/search'; import RedisTimeSeries from '@node-redis/time-series'; export * from '@node-redis/client'; export * from '@node-redis/bloom'; +export * from '@node-redis/graph'; export * from '@node-redis/json'; export * from '@node-redis/search'; export * from '@node-redis/time-series'; const modules = { ...RedisBloomModules, + graph: RedisGraph, json: RedisJSON, ft: RediSearch, ts: RedisTimeSeries diff --git a/package-lock.json b/package-lock.json index 117e169175..8638fcd107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -570,6 +570,10 @@ "resolved": "packages/client", "link": true }, + "node_modules/@node-redis/graph": { + "resolved": "packages/graph", + "link": true + }, "node_modules/@node-redis/json": { "resolved": "packages/json", "link": true @@ -876,9 +880,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", - "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", + "integrity": "sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw==", "dev": true }, "node_modules/@types/parse-json": { @@ -6434,7 +6438,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6459,7 +6463,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", @@ -6478,6 +6482,25 @@ "node": ">=12" } }, + "packages/graph": { + "name": "@node-redis/graph", + "version": "1.0.0-rc", + "license": "MIT", + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.12", + "nyc": "^15.1.0", + "release-it": "^14.12.4", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.11", + "typescript": "^4.5.5" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + } + }, "packages/json": { "name": "@node-redis/json", "version": "1.0.2", @@ -6485,7 +6508,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6504,7 +6527,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6521,7 +6544,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -6541,7 +6564,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6971,7 +6994,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -6985,7 +7008,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/redis-parser": "^3.0.0", "@types/sinon": "^10.0.8", "@types/yallist": "^4.0.1", @@ -7005,12 +7028,26 @@ "yallist": "4.0.0" } }, + "@node-redis/graph": { + "version": "file:packages/graph", + "requires": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.12", + "nyc": "^15.1.0", + "release-it": "^14.12.4", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.11", + "typescript": "^4.5.5" + } + }, "@node-redis/json": { "version": "file:packages/json", "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7024,7 +7061,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7038,7 +7075,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -7053,7 +7090,7 @@ "requires": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.12", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", @@ -7326,9 +7363,9 @@ "dev": true }, "@types/node": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", - "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", + "integrity": "sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw==", "dev": true }, "@types/parse-json": { diff --git a/packages/bloom/package.json b/packages/bloom/package.json index ef012f893d..e16b512ec5 100644 --- a/packages/bloom/package.json +++ b/packages/bloom/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", diff --git a/packages/bloom/tsconfig.json b/packages/bloom/tsconfig.json index 19ef1005e3..da6b76e89b 100644 --- a/packages/bloom/tsconfig.json +++ b/packages/bloom/tsconfig.json @@ -6,6 +6,10 @@ "include": [ "./lib/**/*.ts" ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], "typedocOptions": { "entryPoints": [ "./lib" diff --git a/packages/client/package.json b/packages/client/package.json index 036bcc858c..d450e41256 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -22,9 +22,9 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "@types/redis-parser": "^3.0.0", - "@types/sinon": "^10.0.8", + "@types/sinon": "^10.0.9", "@types/yallist": "^4.0.1", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", diff --git a/packages/graph/.nycrc.json b/packages/graph/.nycrc.json new file mode 100644 index 0000000000..b4e671e178 --- /dev/null +++ b/packages/graph/.nycrc.json @@ -0,0 +1,4 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": ["**/*.spec.ts", "lib/test-utils.ts"] +} diff --git a/packages/graph/.release-it.json b/packages/graph/.release-it.json new file mode 100644 index 0000000000..530d8f355d --- /dev/null +++ b/packages/graph/.release-it.json @@ -0,0 +1,10 @@ +{ + "git": { + "tagName": "graph@${version}", + "commitMessage": "Release ${tagName}", + "tagAnnotation": "Release ${tagName}" + }, + "npm": { + "publishArgs": ["--access", "public"] + } +} diff --git a/packages/graph/README.md b/packages/graph/README.md new file mode 100644 index 0000000000..2313f474d5 --- /dev/null +++ b/packages/graph/README.md @@ -0,0 +1 @@ +# @node-redis/graph diff --git a/packages/graph/lib/commands/CONFIG_GET.spec.ts b/packages/graph/lib/commands/CONFIG_GET.spec.ts new file mode 100644 index 0000000000..6e1fa74e21 --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_GET.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_GET'; + +describe('CONFIG GET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT'), + ['GRAPH.CONFIG', 'GET', 'TIMEOUT'] + ); + }); + + testUtils.testWithClient('client.graph.configGet', async client => { + assert.deepEqual( + await client.graph.configGet('TIMEOUT'), + [ + 'TIMEOUT', + 0 + ] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/CONFIG_GET.ts b/packages/graph/lib/commands/CONFIG_GET.ts new file mode 100644 index 0000000000..ce80a1148e --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_GET.ts @@ -0,0 +1,12 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(configKey: string): Array { + return ['GRAPH.CONFIG', 'GET', configKey]; +} + +type ConfigItem = [ + configKey: string, + value: number +]; + +export declare function transformReply(): ConfigItem | Array; diff --git a/packages/graph/lib/commands/CONFIG_SET.spec.ts b/packages/graph/lib/commands/CONFIG_SET.spec.ts new file mode 100644 index 0000000000..51dce0a8cd --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_SET.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CONFIG_SET'; + +describe('CONFIG SET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('TIMEOUT', 0), + ['GRAPH.CONFIG', 'SET', 'TIMEOUT', '0'] + ); + }); + + testUtils.testWithClient('client.graph.configSet', async client => { + assert.equal( + await client.graph.configSet('TIMEOUT', 0), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/CONFIG_SET.ts b/packages/graph/lib/commands/CONFIG_SET.ts new file mode 100644 index 0000000000..ac81449ad1 --- /dev/null +++ b/packages/graph/lib/commands/CONFIG_SET.ts @@ -0,0 +1,10 @@ +export function transformArguments(configKey: string, value: number): Array { + return [ + 'GRAPH.CONFIG', + 'SET', + configKey, + value.toString() + ]; +} + +export declare function transformReply(): 'OK'; diff --git a/packages/graph/lib/commands/DELETE.spec.ts b/packages/graph/lib/commands/DELETE.spec.ts new file mode 100644 index 0000000000..e51ac2bfab --- /dev/null +++ b/packages/graph/lib/commands/DELETE.spec.ts @@ -0,0 +1,21 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './DELETE'; + +describe('', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['GRAPH.DELETE', 'key'] + ); + }); + + testUtils.testWithClient('client.graph.delete', async client => { + await client.graph.query('key', 'RETURN 1'); + + assert.equal( + typeof await client.graph.delete('key'), + 'string' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/DELETE.ts b/packages/graph/lib/commands/DELETE.ts new file mode 100644 index 0000000000..240708143c --- /dev/null +++ b/packages/graph/lib/commands/DELETE.ts @@ -0,0 +1,7 @@ +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['GRAPH.DELETE', key]; +} + +export declare function transformReply(): string; diff --git a/packages/graph/lib/commands/EXPLAIN.spec.ts b/packages/graph/lib/commands/EXPLAIN.spec.ts new file mode 100644 index 0000000000..2919a21163 --- /dev/null +++ b/packages/graph/lib/commands/EXPLAIN.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './EXPLAIN'; + +describe('EXPLAIN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'RETURN 0'), + ['GRAPH.EXPLAIN', 'key', 'RETURN 0'] + ); + }); + + testUtils.testWithClient('client.graph.explain', async client => { + const reply = await client.graph.explain('key', 'RETURN 0'); + assert.ok(Array.isArray(reply)); + assert.ok(!reply.find(x => typeof x !== 'string')); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/EXPLAIN.ts b/packages/graph/lib/commands/EXPLAIN.ts new file mode 100644 index 0000000000..419ff62b11 --- /dev/null +++ b/packages/graph/lib/commands/EXPLAIN.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, query: string): Array { + return ['GRAPH.EXPLAIN', key, query]; +} + +export declare function transfromReply(): Array; diff --git a/packages/graph/lib/commands/LIST.spec.ts b/packages/graph/lib/commands/LIST.spec.ts new file mode 100644 index 0000000000..d4fab0358b --- /dev/null +++ b/packages/graph/lib/commands/LIST.spec.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './LIST'; + +describe('LIST', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['GRAPH.LIST'] + ); + }); + + testUtils.testWithClient('client.graph.list', async client => { + assert.deepEqual( + await client.graph.list(), + [] + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/LIST.ts b/packages/graph/lib/commands/LIST.ts new file mode 100644 index 0000000000..1939d43d88 --- /dev/null +++ b/packages/graph/lib/commands/LIST.ts @@ -0,0 +1,7 @@ +export const IS_READ_ONLY = true; + +export function transformArguments(): Array { + return ['GRAPH.LIST']; +} + +export declare function transformReply(): Array; diff --git a/packages/graph/lib/commands/PROFILE.spec.ts b/packages/graph/lib/commands/PROFILE.spec.ts new file mode 100644 index 0000000000..80857eb0ab --- /dev/null +++ b/packages/graph/lib/commands/PROFILE.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './PROFILE'; + +describe('PROFILE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'RETURN 0'), + ['GRAPH.PROFILE', 'key', 'RETURN 0'] + ); + }); + + testUtils.testWithClient('client.graph.profile', async client => { + const reply = await client.graph.profile('key', 'RETURN 0'); + assert.ok(Array.isArray(reply)); + assert.ok(!reply.find(x => typeof x !== 'string')); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/PROFILE.ts b/packages/graph/lib/commands/PROFILE.ts new file mode 100644 index 0000000000..473c526e67 --- /dev/null +++ b/packages/graph/lib/commands/PROFILE.ts @@ -0,0 +1,9 @@ +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, query: string): Array { + return ['GRAPH.PROFILE', key, query]; +} + +export declare function transfromReply(): Array; diff --git a/packages/graph/lib/commands/QUERY.spec.ts b/packages/graph/lib/commands/QUERY.spec.ts new file mode 100644 index 0000000000..44492d75d2 --- /dev/null +++ b/packages/graph/lib/commands/QUERY.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './QUERY'; + +describe('QUERY', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '*', 100), + ['GRAPH.QUERY', 'key', '*', '100'] + ); + }); + + testUtils.testWithClient('client.graph.query', async client => { + await client.graph.query('key', + "CREATE (r:human {name:'roi', age:34}), (a:human {name:'amit', age:32}), (r)-[:knows]->(a)" + ); + const reply = await client.graph.query('key', + "MATCH (r:human)-[:knows]->(a:human) RETURN r.age, r.name" + ); + assert.equal(reply.data.length, 1); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/QUERY.ts b/packages/graph/lib/commands/QUERY.ts new file mode 100644 index 0000000000..06436e5e74 --- /dev/null +++ b/packages/graph/lib/commands/QUERY.ts @@ -0,0 +1,43 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands/index'; +import { pushQueryArguments } from '.'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + graph: RedisCommandArgument, + query: RedisCommandArgument, + timeout?: number +): RedisCommandArguments { + return pushQueryArguments( + ['GRAPH.QUERY'], + graph, + query, + timeout + ); +} + +type Headers = Array; + +type Data = Array>; + +type Metadata = Array; + +type QueryRawReply = [ + headers: Headers, + data: Data, + metadata: Metadata +]; + +interface QueryReply { + headers: Headers, + data: Data, + metadata: Metadata +}; + +export function transformReply(reply: QueryRawReply): QueryReply { + return { + headers: reply[0], + data: reply[1], + metadata: reply[2] + }; +} diff --git a/packages/graph/lib/commands/QUERY_RO.spec.ts b/packages/graph/lib/commands/QUERY_RO.spec.ts new file mode 100644 index 0000000000..78814603ac --- /dev/null +++ b/packages/graph/lib/commands/QUERY_RO.spec.ts @@ -0,0 +1,22 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './QUERY_RO'; + +describe('QUERY_RO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', '*', 100), + ['GRAPH.RO_QUERY', 'key', '*', '100'] + ); + }); + + testUtils.testWithClient('client.graph.queryRo', async client => { + await client.graph.query('key', + "CREATE (r:human {name:'roi', age:34}), (a:human {name:'amit', age:32}), (r)-[:knows]->(a)" + ); + const reply = await client.graph.queryRo('key', + "MATCH (r:human)-[:knows]->(a:human) RETURN r.age, r.name" + ); + assert.equal(reply.data.length, 1); + }, GLOBAL.SERVERS.OPEN); +}); \ No newline at end of file diff --git a/packages/graph/lib/commands/QUERY_RO.ts b/packages/graph/lib/commands/QUERY_RO.ts new file mode 100644 index 0000000000..9da471adcc --- /dev/null +++ b/packages/graph/lib/commands/QUERY_RO.ts @@ -0,0 +1,21 @@ +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; +import { pushQueryArguments } from '.'; + +export { FIRST_KEY_INDEX } from './QUERY'; + +export const IS_READ_ONLY = true; + +export function transformArguments( + graph: RedisCommandArgument, + query: RedisCommandArgument, + timeout?: number +): RedisCommandArguments { + return pushQueryArguments( + ['GRAPH.RO_QUERY'], + graph, + query, + timeout + ); +} + +export { transformReply } from './QUERY'; diff --git a/packages/graph/lib/commands/SLOWLOG.spec.ts b/packages/graph/lib/commands/SLOWLOG.spec.ts new file mode 100644 index 0000000000..e3083b994d --- /dev/null +++ b/packages/graph/lib/commands/SLOWLOG.spec.ts @@ -0,0 +1,18 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './SLOWLOG'; + +describe('SLOWLOG', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['GRAPH.SLOWLOG', 'key'] + ); + }); + + testUtils.testWithClient('client.graph.slowLog', async client => { + await client.graph.query('key', 'RETURN 1'); + const reply = await client.graph.slowLog('key'); + assert.equal(reply.length, 1); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/graph/lib/commands/SLOWLOG.ts b/packages/graph/lib/commands/SLOWLOG.ts new file mode 100644 index 0000000000..6ae87af89b --- /dev/null +++ b/packages/graph/lib/commands/SLOWLOG.ts @@ -0,0 +1,30 @@ +export const IS_READ_ONLY = true; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string) { + return ['GRAPH.SLOWLOG', key]; +} + +type SlowLogRawReply = Array<[ + timestamp: string, + command: string, + query: string, + took: string +]>; + +type SlowLogReply = Array<{ + timestamp: Date; + command: string; + query: string; + took: number; +}>; + +export function transformReply(logs: SlowLogRawReply): SlowLogReply { + return logs.map(([timestamp, command, query, took]) => ({ + timestamp: new Date(Number(timestamp) * 1000), + command, + query, + took: Number(took) + })); +} diff --git a/packages/graph/lib/commands/index.ts b/packages/graph/lib/commands/index.ts new file mode 100644 index 0000000000..0b6180e175 --- /dev/null +++ b/packages/graph/lib/commands/index.ts @@ -0,0 +1,49 @@ +import * as CONFIG_GET from './CONFIG_GET'; +import * as CONFIG_SET from './CONFIG_SET';; +import * as DELETE from './DELETE'; +import * as EXPLAIN from './EXPLAIN'; +import * as LIST from './LIST'; +import * as PROFILE from './PROFILE'; +import * as QUERY_RO from './QUERY_RO'; +import * as QUERY from './QUERY'; +import * as SLOWLOG from './SLOWLOG'; +import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; + +export default { + CONFIG_GET, + configGet: CONFIG_GET, + CONFIG_SET, + configSet: CONFIG_SET, + DELETE, + delete: DELETE, + EXPLAIN, + explain: EXPLAIN, + LIST, + list: LIST, + PROFILE, + profile: PROFILE, + QUERY_RO, + queryRo: QUERY_RO, + QUERY, + query: QUERY, + SLOWLOG, + slowLog: SLOWLOG +}; + +export function pushQueryArguments( + args: RedisCommandArguments, + graph: RedisCommandArgument, + query: RedisCommandArgument, + timeout?: number +): RedisCommandArguments { + args.push( + graph, + query + ); + + if (timeout !== undefined) { + args.push(timeout.toString()); + } + + return args; +} \ No newline at end of file diff --git a/packages/graph/lib/index.ts b/packages/graph/lib/index.ts new file mode 100644 index 0000000000..bc0e103e8c --- /dev/null +++ b/packages/graph/lib/index.ts @@ -0,0 +1 @@ +export { default } from './commands'; diff --git a/packages/graph/lib/test-utils.ts b/packages/graph/lib/test-utils.ts new file mode 100644 index 0000000000..ad6dc0fd19 --- /dev/null +++ b/packages/graph/lib/test-utils.ts @@ -0,0 +1,21 @@ +import TestUtils from '@node-redis/test-utils'; +import RedisGraph from '.'; + +export default new TestUtils({ + dockerImageName: 'redislabs/redisgraph', + dockerImageVersionArgument: 'redisgraph-version', + defaultDockerVersion: '2.8.7' +}); + +export const GLOBAL = { + SERVERS: { + OPEN: { + serverArguments: ['--loadmodule /usr/lib/redis/modules/redisgraph.so'], + clientOptions: { + modules: { + graph: RedisGraph + } + } + } + } +}; diff --git a/packages/graph/package.json b/packages/graph/package.json new file mode 100644 index 0000000000..3b95952aeb --- /dev/null +++ b/packages/graph/package.json @@ -0,0 +1,29 @@ +{ + "name": "@node-redis/graph", + "version": "1.0.0-rc", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'", + "build": "tsc", + "documentation": "typedoc" + }, + "peerDependencies": { + "@node-redis/client": "^1.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@node-redis/test-utils": "*", + "@types/node": "^17.0.13", + "nyc": "^15.1.0", + "release-it": "^14.12.4", + "source-map-support": "^0.5.21", + "ts-node": "^10.4.0", + "typedoc": "^0.22.11", + "typescript": "^4.5.5" + } +} diff --git a/packages/graph/tsconfig.json b/packages/graph/tsconfig.json new file mode 100644 index 0000000000..9d17cb6337 --- /dev/null +++ b/packages/graph/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./lib/**/*.ts" + ], + "exclude": [ + "./lib/test-utils.ts", + "./lib/**/*.spec.ts" + ], + "typedocOptions": { + "entryPoints": [ + "./lib" + ], + "entryPointStrategy": "expand", + "out": "../../documentation/graph" + } +} diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index eeee3c77c9..c41870567e 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default new TestUtils({ dockerImageName: 'redislabs/rejson', dockerImageVersionArgument: 'rejson-version', - defaultDockerVersion: '2.0.2' + defaultDockerVersion: '2.0.6' }); export const GLOBAL = { diff --git a/packages/json/package.json b/packages/json/package.json index db9c237303..361928215a 100644 --- a/packages/json/package.json +++ b/packages/json/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", diff --git a/packages/search/lib/commands/CREATE.spec.ts b/packages/search/lib/commands/CREATE.spec.ts index 7d21e82d66..35aec47a67 100644 --- a/packages/search/lib/commands/CREATE.spec.ts +++ b/packages/search/lib/commands/CREATE.spec.ts @@ -340,7 +340,7 @@ describe('CREATE', () => { testUtils.testWithClient('client.ft.create', async client => { assert.equal( await client.ft.create('index', { - field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + field: SchemaFieldTypes.TEXT }), 'OK' ); diff --git a/packages/search/lib/commands/DROPINDEX.spec.ts b/packages/search/lib/commands/DROPINDEX.spec.ts index b1cb7c9313..6a60a5d851 100644 --- a/packages/search/lib/commands/DROPINDEX.spec.ts +++ b/packages/search/lib/commands/DROPINDEX.spec.ts @@ -22,7 +22,7 @@ describe('DROPINDEX', () => { testUtils.testWithClient('client.ft.dropIndex', async client => { await client.ft.create('index', { - field: SchemaFieldTypes.TEXT // TODO: shouldn't be mandatory + field: SchemaFieldTypes.TEXT }); assert.equal( diff --git a/packages/search/lib/commands/INFO.spec.ts b/packages/search/lib/commands/INFO.spec.ts index 805d4c820c..2ee3048c01 100644 --- a/packages/search/lib/commands/INFO.spec.ts +++ b/packages/search/lib/commands/INFO.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import { SchemaFieldTypes } from '.'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './INFO'; @@ -11,8 +12,8 @@ describe('INFO', () => { }); testUtils.testWithClient('client.ft.info', async client => { - await client.ft.create('index', {}, { - ON: 'HASH' // TODO: shouldn't be mandatory + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT }); assert.deepEqual( @@ -25,7 +26,16 @@ describe('INFO', () => { keyType: 'HASH', prefixes: [''] }, - attributes: [], + attributes: [[ + 'identifier', + 'field', + 'attribute', + 'field', + 'type', + 'TEXT', + 'WEIGHT', + '1' + ]], numDocs: '0', maxDocId: '0', numTerms: '0', diff --git a/packages/search/lib/commands/SYNDUMP.spec.ts b/packages/search/lib/commands/SYNDUMP.spec.ts index 4b0cb0c8b3..472db54bcf 100644 --- a/packages/search/lib/commands/SYNDUMP.spec.ts +++ b/packages/search/lib/commands/SYNDUMP.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SYNDUMP'; +import { SchemaFieldTypes } from '.'; describe('SYNDUMP', () => { it('transformArguments', () => { @@ -11,8 +12,8 @@ describe('SYNDUMP', () => { }); testUtils.testWithClient('client.ft.synDump', async client => { - await client.ft.create('index', {}, { - ON: 'HASH' // TODO: shouldn't be mandatory + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT }); assert.deepEqual( diff --git a/packages/search/lib/commands/SYNUPDATE.spec.ts b/packages/search/lib/commands/SYNUPDATE.spec.ts index bf7fed8493..19ac9b85e5 100644 --- a/packages/search/lib/commands/SYNUPDATE.spec.ts +++ b/packages/search/lib/commands/SYNUPDATE.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './SYNUPDATE'; +import { SchemaFieldTypes } from '.'; describe('SYNUPDATE', () => { describe('transformArguments', () => { @@ -27,8 +28,8 @@ describe('SYNUPDATE', () => { }); testUtils.testWithClient('client.ft.synUpdate', async client => { - await client.ft.create('index', {}, { - ON: 'HASH' // TODO: shouldn't be mandatory + await client.ft.create('index', { + field: SchemaFieldTypes.TEXT }); assert.equal( diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 4af05e1062..fd11951bb5 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -4,7 +4,7 @@ import RediSearch from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redisearch', dockerImageVersionArgument: 'redisearch-version', - defaultDockerVersion: '2.2.1' + defaultDockerVersion: '2.2.7' }); export const GLOBAL = { diff --git a/packages/search/package.json b/packages/search/package.json index 9ab292f9b8..1bf7e1de99 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index d827cef26e..263b4cccff 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -12,7 +12,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "@types/yargs": "^17.0.8", "mocha": "^9.2.0", "nyc": "^15.1.0", diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index d6c7f2c5f8..00101d980a 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -44,6 +44,7 @@ describe('INFO_DEBUG', () => { key: 'key2', timeBucket: 5 }], + keySelfName: 'key', chunks: [{ startTimestamp: 1, endTimestamp: 1, diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index 6680a2044b..ad9522d2a6 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -16,6 +16,8 @@ export function transformArguments(key: string): Array { type InfoDebugRawReply = [ ...infoArgs: InfoRawReply, _: string, + keySelfName: string, + _: string, chunks: Array<[ _: string, startTimestamp: number, @@ -31,6 +33,7 @@ type InfoDebugRawReply = [ ] interface InfoDebugReply extends InfoReply { + keySelfName: string; chunks: Array<{ startTimestamp: number; endTimestamp: number; @@ -42,7 +45,8 @@ interface InfoDebugReply extends InfoReply { export function transformReply(rawReply: InfoDebugRawReply): InfoDebugReply { const reply = transformInfoReply(rawReply as unknown as InfoRawReply); - (reply as InfoDebugReply).chunks = rawReply[25].map(chunk => ({ + (reply as InfoDebugReply).keySelfName = rawReply[25]; + (reply as InfoDebugReply).chunks = rawReply[27].map(chunk => ({ startTimestamp: chunk[1], endTimestamp: chunk[3], samples: chunk[5], diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index 7beb04e298..eebb1b416d 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default new TestUtils({ dockerImageName: 'redislabs/redistimeseries', dockerImageVersionArgument: 'timeseries-version', - defaultDockerVersion: '1.6.0' + defaultDockerVersion: '1.6.8' }); export const GLOBAL = { diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 8242de1c13..9edafb85db 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", "@node-redis/test-utils": "*", - "@types/node": "^17.0.10", + "@types/node": "^17.0.13", "nyc": "^15.1.0", "release-it": "^14.12.4", "source-map-support": "^0.5.21",