diff --git a/README.md b/README.md index e29400c3ee..96d99a6d4b 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,17 @@ Awesome Redis GUI written in Electron, NodeJS and React - `redisinsight/ui` - Contains the frontend code - `redisinsight/api` - Contains the backend code +- `docs` - Contains the documentation - `scripts` - Build scripts and other build-related files - `configs` - Webpack configuration files and other build-related files - `tests` - Contains the e2e +## Plugins documentation + +* [Introduction](docs/plugins/introduction.md) +* [Installation and Usage](docs/plugins/installation.md) +* [Plugin Development](docs/plugins/development.md) + ## Prerequisites Make sure you have installed following packages: diff --git a/docs/plugins/development.md b/docs/plugins/development.md new file mode 100644 index 0000000000..dcb6024e33 --- /dev/null +++ b/docs/plugins/development.md @@ -0,0 +1,182 @@ +# Plugin development + +This document describes the guides to develop your own plugin for the RedisInsight Workbench. + +## How it works + +Plugin visualization in the Workbench is rendered using Iframe to encapsulate plugin scripts and styles, described in +the main plugin script and the stylesheet (if it has been specified in the `package.json`), +iframe includes basic styles as well. + +## Plugin structure + +Each plugin should have a unique name with all its files [loaded](installation.md) to +a separate folder inside the default `plugins` folder. + +> Default plugins are located inside the application. + +### Files +`package.json` should be located in the root folder of your plugins, all other files can be included into a subfolder. + +* **pluginName/package.json** *(required)* - Manifest of the plugin +* **pluginName/{anyName}.js** *(required)* - Core script of the plugin +* **pluginName/{anyName}.css** *(optional)* - File with styles for the plugin visualizations +* **pluginName/{anyFileOrFolder}** *(optional)* - Specify any other file or folder inside the plugin folder +to use by the core module script. *For example*: pluginName/images/image.png. + +## `package.json` structure + +This is the required manifest to use the plugin. `package.json` file should include +the following **required** fields: + + + + + + + + + + + + + + +
namePlugin name. It is recommended to use the folder name as the plugin name in the package.json.
mainRelative path to the core script of the plugin. Example: "./dist/index.js"
visualizations + Array of visualizations (objects) to visualize the results in the Workbench. +

+ Required fields in visualizations: +
    +
  • id - visualization id
  • +
  • name - visualization name to display in the Workbench
  • +
  • activationMethod - name of the exported function to call when +this visualization is selected in the Workbench
  • +
  • + matchCommands - array of commands to use the visualization for. Supports regex string. + Example: ["CLIENT LIST", "FT.*"] +
  • +
+
+ +You can specify the path to a css file in the `styles` field. If specified, +this file will be included inside the iframe plugin. + +Simple example of the `package.json` file with required and optional fields: + +```json +{ + "author": { + "name": "Redis Ltd.", + "email": "support@redis.com", + "url": "https://redis.com/redis-enterprise/redis-insight" + }, + "description": "Show client list as table", + "styles": "./dist/styles.css", + "main": "./dist/index.js", + "name": "client-list", + "version": "0.0.1", + "scripts": {}, + "visualizations": [ + { + "id": "clients-list", + "name": "Table", + "activationMethod": "renderClientsList", + "matchCommands": [ + "CLIENT LIST" + ], + "description": "Example of client list plugin", + "default": true + } + ], + "devDependencies": {}, + "dependencies": {} +} +``` + +## Core script of the plugin + +This is the required script with defined visualization methods. +The core script contains function and its export (functions - for multiple visualizations), +which is run after the relevant visualization is selected in the Workbench. + +The following function receives props of the executed commands: +```typescript +interface Props { + command: string; // executed command + data: string; // result of the executed command + status: 'success' | 'fail'; // response status of the executed command +} + +const renderVisualization = (props: Props) => { + // Do your magic +} + +export default { renderVisualization } +``` + +Each plugin iframe has basic styles of RedisInsight application, including fonts and color schemes. + +It is recommended to use the React & [Elastic UI library](https://elastic.github.io/eui/#/) for +consistency with plugin visualisations and the entire application. + +Find the example of the plugin here. + +* [Client List Plugin README](../../redisinsight/ui/src/packages/clients-list-example/README.md) +* [Client List Plugin dir](../../redisinsight/ui/src/packages/clients-list-example/) + +### Available parameters + +Additional information provided to the plugin iframe is included in the `window.state` +inside of the plugin script. + +```javascript +const { config, modules } = window.state +const { baseUrl } = config + +// modules - the list of modules of the current database +// baseUrl - url for your plugin folder - can be used to include your assets +``` + +### Plugin rendering +To render the plugin visualization, the iframe with basic html is generated which is +then populated with relevant scripts and styles. To render the html data, use existing +DOM Element `#app` or create your own DOM Elements. +Rendered iframe also includes `theme_DARK` or `theme_LIGHT` className on `body` to indicate the application theme used. + +_Javascript Example:_ +```javascript +const renderVisualization = (props) => { + const { command, data } = props; + document.getElementById('app') + .innerHTML = ` +

Executed command:

+

${command}

+

Result of the command

+

${data}

+ ` +} + +export default { renderVisualization } +``` + +_React Example:_ +```javascript +import { render } from 'react-dom' +import App from './App' + +const renderVisualization = (props) => { + const { command, status, data = '' } = props + render( + , + document.getElementById('app') + ) +} + +// This is a required action - export the main function for execution of the visualization +export default { renderVisualization } +``` + + +## Plugins communication +> **_Future updates:_** +Support of communication with the main application via a third-party library - _redisinsight-plugin-sdk_. diff --git a/docs/plugins/installation.md b/docs/plugins/installation.md new file mode 100644 index 0000000000..cff18847f4 --- /dev/null +++ b/docs/plugins/installation.md @@ -0,0 +1,30 @@ +# Plugin installation & Usage + +This document describes the guides to add `plugins` for the Workbench to RedisInsight. + +## Installation guide + +**Note**: While adding new plugins for Workbench, use files only from trusted +authors to avoid automatic execution of malicious code. + +1. Download the plugin for the Workbench. +2. Open the `plugins` folder with the following path + * For MacOs: `/.redisinsight-v2.0/plugins` + * For Windows: `C:\Users\{Username}\.redisinsight-v2.0\plugins` + * For Linux: `/.redisinsight-v2.0/plugins` +3. Add the folder with plugin to the `plugins` folder + +To see the uploaded plugin visualizations in the command results, reload the Workbench +page and run Redis command relevant for this visualization. + + +## Usage + +The plugin may contain different visualizations for any Redis commands. +Below you can find a guide to see command results in the uploaded plugin visualization: + +1. Open RedisInsight +2. Open a database added +3. Open the Workbench +4. Run the Redis command relevant for the plugin visualization +5. Select the plugin visualization to display results in (if this visualization has not been set by default) diff --git a/docs/plugins/introduction.md b/docs/plugins/introduction.md new file mode 100644 index 0000000000..d585af2ab1 --- /dev/null +++ b/docs/plugins/introduction.md @@ -0,0 +1,9 @@ +# Introduction to plugins for the Workbench + +Plugins allow the customization of visualizations for Redis commands executed +in the Workbench inside the RedisInsight. + +## Wiki + +* [Installation and Usage](installation.md) +* [Plugin Development](development.md) diff --git a/electron-builder.json b/electron-builder.json index b7b893e969..9c046951c8 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -6,6 +6,7 @@ "dist/", "node_modules/", "index.html", + "splash.html", "main.prod.js", "main.prod.js.map", "package.json" diff --git a/redisinsight/api/src/exceptions/global-exception.filter.ts b/redisinsight/api/src/exceptions/global-exception.filter.ts new file mode 100644 index 0000000000..d43974372b --- /dev/null +++ b/redisinsight/api/src/exceptions/global-exception.filter.ts @@ -0,0 +1,27 @@ +import { BaseExceptionFilter } from '@nestjs/core'; +import { ArgumentsHost, Logger } from '@nestjs/common'; +import { Request, Response } from 'express'; + +export class GlobalExceptionFilter extends BaseExceptionFilter { + private staticServerLogger = new Logger('StaticServerLogger'); + + catch(exception: Error, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const request = ctx.getRequest(); + + if (/^\/(?:plugins|static)\//i.test(request.url)) { + const response = ctx.getResponse(); + const statusCode = exception['statusCode'] || 500; + const message = `Error when trying to fetch ${request.url}`; + + this.staticServerLogger.error(message, { ...exception } as any); + return response.status(statusCode) + .json({ + statusCode, + message, + }); + } + + return super.catch(exception, host); + } +} diff --git a/redisinsight/api/src/main.ts b/redisinsight/api/src/main.ts index ce34af83e2..13a9fa7893 100644 --- a/redisinsight/api/src/main.ts +++ b/redisinsight/api/src/main.ts @@ -3,6 +3,7 @@ import { SwaggerModule } from '@nestjs/swagger'; import { NestApplicationOptions } from '@nestjs/common'; import * as bodyParser from 'body-parser'; import { WinstonModule } from 'nest-winston'; +import { GlobalExceptionFilter } from 'src/exceptions/global-exception.filter'; import { AppModule } from './app.module'; import SWAGGER_CONFIG from '../config/swagger'; import LOGGER_CONFIG from '../config/logger'; @@ -22,7 +23,7 @@ export default async function bootstrap() { } const app = await NestFactory.create(AppModule, options); - + app.useGlobalFilters(new GlobalExceptionFilter(app.getHttpAdapter())); app.use(bodyParser.json({ limit: '512mb' })); app.use(bodyParser.urlencoded({ limit: '512mb', extended: true })); app.enableCors(); diff --git a/redisinsight/main.dev.ts b/redisinsight/main.dev.ts index 96cc4c6103..108625e1a5 100644 --- a/redisinsight/main.dev.ts +++ b/redisinsight/main.dev.ts @@ -94,7 +94,7 @@ export const getDisplayAppInTrayValue = (): boolean => { /** * Backend part... */ -const port = 5000; +const port = 5001; const launchApiServer = async () => { try { const detectPortConst = await detectPort(port); @@ -134,19 +134,35 @@ const bootstrap = async () => { export const windows = new Set(); -export const createWindow = async () => { +const titleSplash = 'splash'; +export const createSplashScreen = async () => { + const splash = new BrowserWindow({ + width: 500, + height: 200, + transparent: true, + frame: false, + resizable: false, + alwaysOnTop: true, + title: titleSplash, + }); + + splash.loadURL(`file://${__dirname}/splash.html`); + + return splash; +}; + +export const createWindow = async (splash: BrowserWindow | null) => { const RESOURCES_PATH = app.isPackaged ? path.join(process.resourcesPath, 'resources') : path.join(__dirname, '../resources'); - const getAssetPath = (...paths: string[]): string => { - return path.join(RESOURCES_PATH, ...paths); - }; + const getAssetPath = (...paths: string[]): string => path.join(RESOURCES_PATH, ...paths); let x; let y; const currentWindow = BrowserWindow.getFocusedWindow(); - if (currentWindow) { + + if (currentWindow && currentWindow?.getTitle() !== titleSplash) { const [currentWindowX, currentWindowY] = currentWindow.getPosition(); x = currentWindowX + 24; y = currentWindowY + 24; @@ -188,8 +204,9 @@ export const createWindow = async () => { if (process.env.START_MINIMIZED) { newWindow.minimize(); } else { - newWindow.show(); - newWindow.focus(); + newWindow?.show(); + newWindow?.focus(); + splash?.close(); } }); @@ -293,7 +310,11 @@ app.on('continue-activity-error', (event, type, error) => { } }); -app.whenReady().then(bootstrap).then(createWindow).catch(console.log); +app.whenReady() + .then(bootstrap) + .then(createSplashScreen) + .then(createWindow) + .catch(console.log); app.on('activate', () => { // On macOS it's common to re-create a window in the app when the diff --git a/redisinsight/splash.html b/redisinsight/splash.html new file mode 100644 index 0000000000..5763e7345a --- /dev/null +++ b/redisinsight/splash.html @@ -0,0 +1,106 @@ + + + + + + +
+
+ + + RedisInsight 2.0.2-preview © 2021 Redis Ltd. +
+
+ + + diff --git a/redisinsight/ui/src/components/cli/components/cli-search/CliSearchFilter/constants.ts b/redisinsight/ui/src/components/cli/components/cli-search/CliSearchFilter/constants.ts index 46e177974b..25dd61a1e3 100644 --- a/redisinsight/ui/src/components/cli/components/cli-search/CliSearchFilter/constants.ts +++ b/redisinsight/ui/src/components/cli/components/cli-search/CliSearchFilter/constants.ts @@ -69,4 +69,16 @@ export const FILTER_GROUP_TYPE_OPTIONS = [ text: 'JSON', value: CommandGroup.JSON, }, + { + text: 'TimeSeries', + value: CommandGroup.TimeSeries, + }, + { + text: 'Graph', + value: CommandGroup.Graph, + }, + { + text: 'AI', + value: CommandGroup.AI, + }, ] diff --git a/redisinsight/ui/src/constants/commands.ts b/redisinsight/ui/src/constants/commands.ts index 531cf7aee9..b67dfd9c6b 100644 --- a/redisinsight/ui/src/constants/commands.ts +++ b/redisinsight/ui/src/constants/commands.ts @@ -56,6 +56,9 @@ export enum CommandGroup { String = 'string', Search = 'search', JSON = 'json', + TimeSeries = 'timeseries', + Graph = 'graph', + AI = 'ai' } export enum CommandRediSearch { diff --git a/redisinsight/ui/src/packages/clients-list-example/README.md b/redisinsight/ui/src/packages/clients-list-example/README.md new file mode 100644 index 0000000000..40443ad0a0 --- /dev/null +++ b/redisinsight/ui/src/packages/clients-list-example/README.md @@ -0,0 +1,34 @@ +# Example of the plugin for the “Client List” command + +The example has been created using React, TypeScript, and [Elastic UI](https://elastic.github.io/eui/#/). +[Parcel](https://parceljs.org/) is used to build the plugin. + +## Running locally + +The following commands will install dependencies and start the server to run the plugin locally: +``` +yarn +yarn start +``` +These commands will install dependencies and start the server. + +_Note_: Base styles are included to `index.html` from the repository. + +This command will generate the `vendor` folder with styles and fonts of the core app. Add this folder +inside the folder for your plugin and include appropriate styles to the `index.html` file. + +``` +yarn build:statics - for Linux or MacOs +yarn build:statics:win - for Windows +``` + +## Build plugin + +The following commands will build plugins to be used in RedisInsight: +``` +yarn +yarn build +``` + +[Add](../../../../../docs/plugins/installation.md) the package.json file and the +`dist` folder to the folder with your plugin, which should be located in the `plugins` folder. diff --git a/redisinsight/ui/src/packages/clients-list-example/src/main.tsx b/redisinsight/ui/src/packages/clients-list-example/src/main.tsx index ff97fb6f3e..51359287e9 100644 --- a/redisinsight/ui/src/packages/clients-list-example/src/main.tsx +++ b/redisinsight/ui/src/packages/clients-list-example/src/main.tsx @@ -20,4 +20,5 @@ if (process.env.NODE_ENV === 'development') { renderClientsList({ command: '', data: response, status: 'success' }) } +// This is a required action - export the main function for execution of the visualization export default { renderClientsList } diff --git a/redisinsight/ui/src/packages/enablement-area/enablement-area.json b/redisinsight/ui/src/packages/enablement-area/enablement-area.json index 2c00feb4e3..c4582ef82e 100644 --- a/redisinsight/ui/src/packages/enablement-area/enablement-area.json +++ b/redisinsight/ui/src/packages/enablement-area/enablement-area.json @@ -33,6 +33,24 @@ "backTitle": "Document Capabilities", "path": "/static/workbench/guides/document-capabilities/working-with-hashes.html" } + }, + { + "type": "internal-link", + "id": "working-with-json", + "label": "Working with JSON", + "args": { + "backTitle": "Document Capabilities", + "path": "/static/workbench/guides/document-capabilities/working-with-json.html" + } + }, + { + "type": "internal-link", + "id": "learn-more", + "label": "Learn More", + "args": { + "backTitle": "Document Capabilities", + "path": "/static/workbench/guides/document-capabilities/learn-more.html" + } } ] } diff --git a/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/introduction.html b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/introduction.html index 5f58e7011d..e573784324 100644 --- a/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/introduction.html +++ b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/introduction.html @@ -3,8 +3,8 @@
In Redis, you can model documents using:
@@ -24,7 +24,7 @@
  • Supports atomic CRUD operations
  • -

    Full-text search and querying is supported for both Hashes and JSON via a secondary index.

    +

    Full-text search and querying is supported for both Hashes and JSON via a secondary index.

    A number of data types are available for indexing:
      @@ -32,7 +32,7 @@
    • Numeric range index
    • Tag index supporting multiple values
    • Geo index
    • -
    • Vector index
    • +
    • Vector index (upcoming)
    @@ -58,10 +58,10 @@

    PRE-REQUISITES

    Follow these instructions to set up the RedisJSON and RediSearch modules on Redis OSS.

    - For working with Hashes you will need Redis >=6, RediSearch >=2.0. + For working with Hashes you will need Redis >=6, RediSearch >=2.0.

    - For working with JSON you will need Redis >=6, RediSearch >=2.2 and RedisJSON >=2.0. + For working with JSON you will need Redis >=6, RediSearch >=2.2 and RedisJSON >=2.0.

    You could also create a free and ready to use instance on Redis Cloud. diff --git a/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/learn-more.html b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/learn-more.html new file mode 100644 index 0000000000..3f527f58da --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/learn-more.html @@ -0,0 +1,56 @@ + diff --git a/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-hashes.html b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-hashes.html index 60c908492c..e82cb95d3c 100644 --- a/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-hashes.html +++ b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-hashes.html @@ -1,6 +1,6 @@
    -

    ! Click on the button, see the command and the comments in the Workbench editor, and then run it.

    +

    Click on the button, see the command and the comments in the Workbench editor, and then run it.

    1. diff --git a/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-json.html b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-json.html new file mode 100644 index 0000000000..3d23498dbc --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/guides/document-capabilities/working-with-json.html @@ -0,0 +1,169 @@ +
      +
      +

      Click on the button, see the command and the comments in the Workbench editor, and then run it.

      +
        +
      1. + + CRUD operations +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        +
      2. +
      3. + + Secondary Index +
        + +
        +
        + +
        +
        + +
        +
        +
      4. +
      5. + + Search and Querying Basics +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        + +
        +
        +
      6. +
      +
      + +
      diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-hashes/multiple-tags-or-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-hashes/multiple-tags-or-search.txt index 942c0a627a..4644cc619f 100644 --- a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-hashes/multiple-tags-or-search.txt +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-hashes/multiple-tags-or-search.txt @@ -1,4 +1,4 @@ -​​// Perform a search for documents that have one of multiple tags (OR condition) +// Perform a search for documents that have one of multiple tags (OR condition) FT.SEARCH "permits" "@work_type:{construction|design}" diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-and.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-and.txt new file mode 100644 index 0000000000..e72baa6c0e --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-and.txt @@ -0,0 +1,19 @@ +// Perform a combined search on two fields (AND): query for intersection of both search terms, a text search and a tag match + +FT.SEARCH "schools" "@description:girls @status:{closed}" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-geo-filter.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-geo-filter.txt new file mode 100644 index 0000000000..83ec2b9109 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-geo-filter.txt @@ -0,0 +1,19 @@ +// Perform a fuzzy text search and filter on location in a radius distance of 10 miles + +FT.SEARCH "schools" "%%gill%%" GEOFILTER location 51.11 0.45 10 mi + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-or.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-or.txt new file mode 100644 index 0000000000..fa28f448e0 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/combined-search-with-or.txt @@ -0,0 +1,19 @@ +// Perform a combined search on two fields (OR): query for union of both search terms. The brackets are important. + +FT.SEARCH "schools" "(@city:{Oxford})|(@description:girls)" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/create-json-index.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/create-json-index.txt new file mode 100644 index 0000000000..aa4972fce4 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/create-json-index.txt @@ -0,0 +1,24 @@ +// It is possible to index either every hash or every JSON document in the keyspace or configure indexing only for a subset of the same data type documents described by a prefix. + +// RedisJSON supports JSONPath, so we can easily access and index nested properties and array elements. +// Note that you cannot index values that contain JSON objects or JSON arrays. To be indexed, a JSONPath expression must return a single scalar value (string or number). If the JSONPath expression returns an object or an array, it will be ignored. + +// JSON Strings can only be indexed as TEXT, TAG and GEO (using the right syntax). +// JSON numbers can only be indexed as NUMERIC. +// Boolean and NULL values are ignored. + +// Command to create an index on JSON keys that are prefixed with "school:" + +FT.CREATE schools // Index name + on JSON // Indicates the type of data to index + PREFIX 1 "school:" // Tells the index which keys it should index + SCHEMA + $.name AS name TEXT NOSTEM SORTABLE // Will be indexed as a TEXT field. Will permit sorting during query. Stemming is disabled - which is ideal for proper names. + $.description AS description TEXT + $.school_type AS school_type TAG SEPARATOR ";" // For tag fields, a separator indicates how the text contained in the field is to be split into individual tags + $.class AS class TAG // Will be indexed as a tag. Will allow exact-match queries. + $.address.street AS address TEXT NOSTEM // '$.address.street' field will be indexed as TEXT and can be referred as 'street' due to the '... AS fieldname ...' construct. + $.address.city AS city TAG + $.pupils AS pupils NUMERIC SORTABLE // Will be indexed as a numeric field. Will permit sorting during query + $.location AS location GEO // Will be indexed as GEO. Will allow geographic range queries + $.status_log.[-1] as status TAG // Will index the last element of the array as "status" diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-create.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-create.txt new file mode 100644 index 0000000000..5904384541 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-create.txt @@ -0,0 +1,11 @@ +// Let's add four documents as JSON to the index. Note the format of the key names. +// Each document represents a school. + +JSON.SET school:1 . '{"name":"Hall School", "description":"Independent primary school for boys aged 5 to 11","school_type":"single;boys","class":"independent", "address":{"city":"London", "street":"Manor Street"}, "pupils":342, "location":"51.445417, -0.258352", "status_log":["new", "operating"]}' + +JSON.SET school:2 . '{"name":"Garden School", "description":"State school for boys and girls aged 5 to 18","school_type":"mixed;boys;girls","class":"state", "address":{"city":"London", "street":"Gordon Street"}, "pupils":1452, "location":"51.402926, -0.321523", "status_log":["new", "operating"]}' + +JSON.SET school:3 . '{"name":"Gillford School", "description":"Independent school for girls aged 5 to 18","school_type":"single;girls","class":"private", "address":{"city":"Goudhurst", "street":"Goudhurst"}, "pupils":721, "location":"51.112685, 0.451076", "status_log":["new", "operating", "closed"]}' + +JSON.SET school:4 . '{"name":"Old Boys School", "description":"Independent school for boys aged 5 to 18","school_type":"single;boys","class":"independent", "address":{"city":"Oxford", "street":"Trident Street"}, "pupils":1200, "location":"51.781756, -1.123196", "status_log":["new", "operating"]}' + diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-delete.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-delete.txt new file mode 100644 index 0000000000..68970feb23 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-delete.txt @@ -0,0 +1,9 @@ +JSON.GET school:1 $.pupils // Read the pupils field before deletion + +JSON.DEL school:1 $.pupils // Delete only the pupils field from the document + +JSON.GET school:1 // Read the whole document to confirm the construction_value field has been deleted + +JSON.DEL school:1 // Delete the entire document + +JSON.GET school:1 // Confirm the entire document has been deleted diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-read.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-read.txt new file mode 100644 index 0000000000..9af0845a6c --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-read.txt @@ -0,0 +1,5 @@ +JSON.GET school:1 // Read the whole document + +// RedisJSON supports JSONPath, so we can easily access nested properties using $. construct + +JSON.GET school:1 $.description // Read only the field description diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-update.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-update.txt new file mode 100644 index 0000000000..b9cb1e0f19 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/crud-update.txt @@ -0,0 +1,5 @@ +JSON.GET school:1 $.pupils // Read the pupils field before the update + +JSON.SET school:1 $.pupils 430 // Update the pupils field + +JSON.GET school:1 $.pupils // Read the pupils field after the update diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/exact-text-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/exact-text-search.txt new file mode 100644 index 0000000000..64d41fabeb --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/exact-text-search.txt @@ -0,0 +1,19 @@ +// Perform a text search on all text fields: query for documents inside which the word 'girls' occurs + +FT.SEARCH "schools" "girls" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/field-specific-text-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/field-specific-text-search.txt new file mode 100644 index 0000000000..a30dae9d82 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/field-specific-text-search.txt @@ -0,0 +1,20 @@ +// Perform a text search on a specific field: query for documents which have a field 'name' inside which the word 'old' occurs +// To reference a field, use @ construct + +FT.SEARCH "schools" "@name:old" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/fuzzy-text-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/fuzzy-text-search.txt new file mode 100644 index 0000000000..5b3b140140 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/fuzzy-text-search.txt @@ -0,0 +1,19 @@ +// Perform a Fuzzy text search on all text fields: query for documents with words similar to 'gill'. The number of % indicates the allowed Levenshtein distance. So the query would also match on 'girl' because 'gill' and 'girl' have a distance of two. + +FT.SEARCH "schools" "%%gill%%" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/group-and-sort-by-aggregation.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/group-and-sort-by-aggregation.txt new file mode 100644 index 0000000000..80cbfa4f90 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/group-and-sort-by-aggregation.txt @@ -0,0 +1,25 @@ +// Aggregations are a way to process the results of a search query, group, sort and transform them - and extract analytic insights from them. +// Aggregations are represented as the following data processing pipeline: +// Filter -> Group (Reduce) -> Apply -> Sort -> Apply + +// Perform a Group By & Sort By aggregation of your documents: display the number of permits by city and then sort the city alphabetically + +FT.AGGREGATE "schools" "*" + GROUPBY 1 @city REDUCE COUNT 0 AS nb_of_schools + SORTBY 2 @city Asc + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/index-info.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/index-info.txt new file mode 100644 index 0000000000..40ab37462a --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/index-info.txt @@ -0,0 +1,5 @@ +// Command to display information about a particular index. +// In this case display the information about the newly created "schools" index + +FT.INFO "schools" + diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/list-all-indexes.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/list-all-indexes.txt new file mode 100644 index 0000000000..01a7b83a5b --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/list-all-indexes.txt @@ -0,0 +1,3 @@ +// Command to return the list of all existing indexes + +FT._LIST diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/multiple-tags-and-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/multiple-tags-and-search.txt new file mode 100644 index 0000000000..f95fbbb128 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/multiple-tags-and-search.txt @@ -0,0 +1,19 @@ +// Perform a search for documents that all of the tags (AND condition) + +FT.SEARCH "schools" "@school_type:{single} @school_type:{girls}" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/multiple-tags-or-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/multiple-tags-or-search.txt new file mode 100644 index 0000000000..2713ed5863 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/multiple-tags-or-search.txt @@ -0,0 +1,19 @@ +// Perform a search for documents that have one of multiple tags (OR condition) + +FT.SEARCH "schools" "@school_type:{single|girls}" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/numeric-range-query.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/numeric-range-query.txt new file mode 100644 index 0000000000..91dfb9725d --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/numeric-range-query.txt @@ -0,0 +1,21 @@ +// Perform a numeric range query: query for every document that has a 'pupils' value between 0 and 1000 +// For numerical ranges, square brackets are inclusive of the listed values + +FT.SEARCH "schools" "@pupils:[0,1000]" + + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// $.status_log.[-1] as status TAG diff --git a/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/tag-search.txt b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/tag-search.txt new file mode 100644 index 0000000000..bb1abaf392 --- /dev/null +++ b/redisinsight/ui/src/packages/enablement-area/scripts/document-capabilities/working-with-json/tag-search.txt @@ -0,0 +1,19 @@ +// Perform a tag search: query for documents which have the city field set to "London". Note that we use curly braces around the tag. + +FT.SEARCH "schools" "@city:{Oxford}" + +// See the "schools" index schema for reference + +// FT.CREATE schools +// on JSON +// PREFIX 1 "school:" +// SCHEMA +// $.name AS name TEXT NOSTEM SORTABLE +// $.description AS description TEXT +// $.school_type AS school_type TAG SEPARATOR ";" +// $.class AS class TAG +// $.address.street AS address TEXT NOSTEM +// $.address.city AS city TAG +// $.pupils AS pupils NUMERIC SORTABLE +// $.location AS location GEO +// diff --git a/redisinsight/ui/src/packages/redisearch/src/components/TableInfoResult/TableInfoResult.tsx b/redisinsight/ui/src/packages/redisearch/src/components/TableInfoResult/TableInfoResult.tsx index 42a9086bd0..57d956852d 100644 --- a/redisinsight/ui/src/packages/redisearch/src/components/TableInfoResult/TableInfoResult.tsx +++ b/redisinsight/ui/src/packages/redisearch/src/components/TableInfoResult/TableInfoResult.tsx @@ -120,6 +120,7 @@ const TableInfoResult = React.memo((props: Props) => { className={cx('inMemoryTableDefault', 'tableInfo', { tableWithPagination: result?.length > 10, })} + responsive={false} data-testid={`query-table-result-${query}`} /> {Footer()} diff --git a/redisinsight/ui/src/packages/redisearch/src/components/TableResult/TableResult.tsx b/redisinsight/ui/src/packages/redisearch/src/components/TableResult/TableResult.tsx index b497a0adfa..1d115634d0 100644 --- a/redisinsight/ui/src/packages/redisearch/src/components/TableResult/TableResult.tsx +++ b/redisinsight/ui/src/packages/redisearch/src/components/TableResult/TableResult.tsx @@ -117,6 +117,7 @@ const TableResult = React.memo((props: Props) => { tableWithPagination: result?.length > 10, } )} + responsive={false} data-testid={`query-table-result-${query}`} /> )} diff --git a/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx b/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx index e6a4ab0c01..7f216c5cc2 100644 --- a/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx +++ b/redisinsight/ui/src/pages/browser/components/list-details/ListDetails.tsx @@ -2,7 +2,7 @@ import { EuiButtonIcon, EuiText, EuiToolTip } from '@elastic/eui' import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import cx from 'classnames' -import { isNull } from 'lodash' +import { isEqual, isNull } from 'lodash' import { SCAN_COUNT_DEFAULT } from 'uiSrc/constants/api' import { @@ -66,10 +66,16 @@ const ListDetails = (props: Props) => { }, [loadedElements]) const handleEditElement = (index = 0, editing: boolean) => { - const elems = [...elements] - elems[index].editing = editing - - setElements(elems) + const newElemsState = elements.map((item) => { + if (item.index === index) { + return { ...item, editing } + } + return item + }) + + if (!isEqual(elements, newElemsState)) { + setElements(newElemsState) + } } const handleApplyEditElement = (index = 0, element: string) => { diff --git a/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/Group/styles.scss b/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/Group/styles.scss index 1172ecc8c5..466d660140 100644 --- a/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/Group/styles.scss +++ b/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/Group/styles.scss @@ -34,6 +34,10 @@ .euiListGroupItem { button { padding: 3px 8px; + line-height: 24px; + } + .euiListGroupItem__label { + line-height: 20px; } } } diff --git a/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/InternalLink.tsx b/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/InternalLink.tsx index 06f32ca0dd..328c24dad4 100644 --- a/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/InternalLink.tsx +++ b/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/InternalLink.tsx @@ -4,6 +4,7 @@ import cx from 'classnames' import EnablementAreaContext from 'uiSrc/pages/workbench/contexts/enablementAreaContext' import styles from './styles.module.scss' +import './styles.scss' export interface Props { testId: string, diff --git a/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/styles.scss b/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/styles.scss new file mode 100644 index 0000000000..f1b7d39490 --- /dev/null +++ b/redisinsight/ui/src/pages/workbench/components/enablament-area/EnablementArea/components/InternalLink/styles.scss @@ -0,0 +1,3 @@ +.euiListGroupItem--xSmall .euiListGroupItem__label { + line-height: 12px; +} diff --git a/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.tsx b/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.tsx index b70b04bc16..504d61728c 100644 --- a/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.tsx +++ b/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.tsx @@ -79,7 +79,7 @@ const WBView = (props: Props) => { { isCollapsed.current = !isCollapsed.current }} @@ -92,11 +92,12 @@ const WBView = (props: Props) => { data-test-subj="resize-btn-preselects-area" /> {(EuiResizablePanel, EuiResizableButton) => ( @@ -106,6 +107,7 @@ const WBView = (props: Props) => { minSize="140px" paddingSize="none" scrollable={false} + className={styles.queryPanel} initialSize={vertical[verticalPanelIds.firstPanelId] ?? 20} style={{ minHeight: '140px' }} > @@ -129,7 +131,9 @@ const WBView = (props: Props) => { paddingSize="none" scrollable={false} initialSize={vertical[verticalPanelIds.secondPanelId] ?? 80} - className={styles.queryResults} + className={cx(styles.queryResults, styles.queryResultsPanel)} + // Fix scroll on low height - 140px (queryPanel) + style={{ maxHeight: 'calc(100% - 140px)' }} > { - state.data.elements[payload.index] = payload.element + state.data.elements[state.data.elements.length === 1 ? 0 : payload.index] = payload.element }, insertListElements: (state) => { state.loading = true diff --git a/redisinsight/ui/src/utils/commands.ts b/redisinsight/ui/src/utils/commands.ts index 1053257a98..0a9006af89 100644 --- a/redisinsight/ui/src/utils/commands.ts +++ b/redisinsight/ui/src/utils/commands.ts @@ -49,26 +49,30 @@ export const generateArgsNames = ( isEmpty ) +const getExternalCommandFormat = (commandName = '') => + commandName + .replace(/\s+/g, '_') + .replace(/[.]+/g, '') + .toLowerCase() + export const getDocUrlForCommand = ( commandName: string, commandGroup: CommandGroup | string = CommandGroup.Generic ): string => { - let commandPage = '' + let command = getExternalCommandFormat(commandName) switch (commandGroup) { case CommandGroup.Search: - commandPage = commandName - .replace(/\s+/g, '_') - .replace(/[.]+/g, '') - .toLowerCase() - return `https://oss.redis.com/redisearch/Commands/#${commandPage}` + return `https://oss.redis.com/redisearch/Commands/#${command}` case CommandGroup.JSON: - commandPage = commandName - .replace(/\s+/g, '_') - .replace(/[.]+/g, '') - .toLowerCase() - return `https://oss.redis.com/redisjson/commands/#${commandPage}` + return `https://oss.redis.com/redisjson/commands/#${command}` + case CommandGroup.TimeSeries: + return `https://oss.redis.com/redistimeseries/commands/#${command}` + case CommandGroup.Graph: + return `https://oss.redis.com/redisgraph/commands/#${command}` + case CommandGroup.AI: + return `https://oss.redis.com/redisai/commands/#${command}` default: - commandPage = commandName.replace(/\s+/g, '-').toLowerCase() - return `https://redis.io/commands/${commandPage}` + command = commandName.replace(/\s+/g, '-').toLowerCase() + return `https://redis.io/commands/${command}` } } diff --git a/redisinsight/ui/src/utils/tests/commands.spec.ts b/redisinsight/ui/src/utils/tests/commands.spec.ts index 06671e18f4..4ae9286224 100644 --- a/redisinsight/ui/src/utils/tests/commands.spec.ts +++ b/redisinsight/ui/src/utils/tests/commands.spec.ts @@ -192,6 +192,12 @@ const getDocUrlForCommandTests: any[] = [ ['JSON.GET', CommandGroup.JSON, 'https://oss.redis.com/redisjson/commands/#jsonget'], ['FT.CREATE', CommandGroup.Search, 'https://oss.redis.com/redisearch/Commands/#ftcreate'], ['FT.ALTER SCHEMA ADD', CommandGroup.Search, 'https://oss.redis.com/redisearch/Commands/#ftalter_schema_add'], + ['TS.ADD', CommandGroup.TimeSeries, 'https://oss.redis.com/redistimeseries/commands/#tsadd'], + ['TS.CREATE', CommandGroup.TimeSeries, 'https://oss.redis.com/redistimeseries/commands/#tscreate'], + ['GRAPH.EXPLAIN', CommandGroup.Graph, 'https://oss.redis.com/redisgraph/commands/#graphexplain'], + ['GRAPH.QUERY', CommandGroup.Graph, 'https://oss.redis.com/redisgraph/commands/#graphquery'], + ['AI.MODELRUN', CommandGroup.AI, 'https://oss.redis.com/redisai/commands/#aimodelrun'], + ['AI.SCRIPTDEL', CommandGroup.AI, 'https://oss.redis.com/redisai/commands/#aiscriptdel'], ['NON EXIST COMMAND', 'non-exist', 'https://redis.io/commands/non-exist-command'], ] diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 6cba7ae7d5..b8ab6b5e88 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -7,6 +7,8 @@ "test:live": "testcafe --live chrome ", "start:api": "cross-env SERVER_STATIC_CONTENT=true yarn --cwd ../../redisinsight/api start:prod", "build:api": "yarn --cwd ../../redisinsight/api build:prod", + "build:statics": "yarn --cwd ../../ build:statics", + "build:statics:win": "yarn --cwd ../../ build:statics:win", "build:web": "yarn --cwd ../../ build:web", "redis:last": "docker run --name redis-last-version -p 7777:6379 -d redislabs/redismod", "start:app": "cross-env SERVER_STATIC_CONTENT=true yarn start:api",