Skip to content

Commit e2d62d1

Browse files
Merge pull request #11 from RedisInsight/feature/updates
2 parents ed44726 + 3198084 commit e2d62d1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1050
-44
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ Awesome Redis GUI written in Electron, NodeJS and React
88

99
- `redisinsight/ui` - Contains the frontend code
1010
- `redisinsight/api` - Contains the backend code
11+
- `docs` - Contains the documentation
1112
- `scripts` - Build scripts and other build-related files
1213
- `configs` - Webpack configuration files and other build-related files
1314
- `tests` - Contains the e2e
1415

16+
## Plugins documentation
17+
18+
* [Introduction](docs/plugins/introduction.md)
19+
* [Installation and Usage](docs/plugins/installation.md)
20+
* [Plugin Development](docs/plugins/development.md)
21+
1522
## Prerequisites
1623

1724
Make sure you have installed following packages:

docs/plugins/development.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Plugin development
2+
3+
This document describes the guides to develop your own plugin for the RedisInsight Workbench.
4+
5+
## How it works
6+
7+
Plugin visualization in the Workbench is rendered using Iframe to encapsulate plugin scripts and styles, described in
8+
the main plugin script and the stylesheet (if it has been specified in the `package.json`),
9+
iframe includes basic styles as well.
10+
11+
## Plugin structure
12+
13+
Each plugin should have a unique name with all its files [loaded](installation.md) to
14+
a separate folder inside the default `plugins` folder.
15+
16+
> Default plugins are located inside the application.
17+
18+
### Files
19+
`package.json` should be located in the root folder of your plugins, all other files can be included into a subfolder.
20+
21+
* **pluginName/package.json** *(required)* - Manifest of the plugin
22+
* **pluginName/{anyName}.js** *(required)* - Core script of the plugin
23+
* **pluginName/{anyName}.css** *(optional)* - File with styles for the plugin visualizations
24+
* **pluginName/{anyFileOrFolder}** *(optional)* - Specify any other file or folder inside the plugin folder
25+
to use by the core module script. *For example*: pluginName/images/image.png.
26+
27+
## `package.json` structure
28+
29+
This is the required manifest to use the plugin. `package.json` file should include
30+
the following **required** fields:
31+
32+
<table>
33+
<tr>
34+
<td><i>name</i></td>
35+
<td>Plugin name. It is recommended to use the folder name as the plugin name in the package.json.</td>
36+
</tr>
37+
<tr>
38+
<td><i>main</i></td>
39+
<td>Relative path to the core script of the plugin. <i>Example: </i> "./dist/index.js"</td>
40+
</tr>
41+
<tr>
42+
<td><i>visualizations</i></td>
43+
<td>
44+
Array of visualizations (objects) to visualize the results in the Workbench.
45+
<br><br>
46+
Required fields in visualizations:
47+
<ul>
48+
<li><strong><i>id</i></strong> - visualization id</li>
49+
<li><strong><i>name</i></strong> - visualization name to display in the Workbench</li>
50+
<li><strong><i>activationMethod</i></strong> - name of the exported function to call when
51+
this visualization is selected in the Workbench</li>
52+
<li>
53+
<strong><i>matchCommands</i></strong> - array of commands to use the visualization for. Supports regex string.
54+
<i>Example: </i> ["CLIENT LIST", "FT.*"]
55+
</li>
56+
</ul>
57+
</td>
58+
</tr>
59+
</table>
60+
61+
You can specify the path to a css file in the `styles` field. If specified,
62+
this file will be included inside the iframe plugin.
63+
64+
Simple example of the `package.json` file with required and optional fields:
65+
66+
```json
67+
{
68+
"author": {
69+
"name": "Redis Ltd.",
70+
"email": "[email protected]",
71+
"url": "https://redis.com/redis-enterprise/redis-insight"
72+
},
73+
"description": "Show client list as table",
74+
"styles": "./dist/styles.css",
75+
"main": "./dist/index.js",
76+
"name": "client-list",
77+
"version": "0.0.1",
78+
"scripts": {},
79+
"visualizations": [
80+
{
81+
"id": "clients-list",
82+
"name": "Table",
83+
"activationMethod": "renderClientsList",
84+
"matchCommands": [
85+
"CLIENT LIST"
86+
],
87+
"description": "Example of client list plugin",
88+
"default": true
89+
}
90+
],
91+
"devDependencies": {},
92+
"dependencies": {}
93+
}
94+
```
95+
96+
## Core script of the plugin
97+
98+
This is the required script with defined visualization methods.
99+
The core script contains function and its export (functions - for multiple visualizations),
100+
which is run after the relevant visualization is selected in the Workbench.
101+
102+
The following function receives props of the executed commands:
103+
```typescript
104+
interface Props {
105+
command: string; // executed command
106+
data: string; // result of the executed command
107+
status: 'success' | 'fail'; // response status of the executed command
108+
}
109+
110+
const renderVisualization = (props: Props) => {
111+
// Do your magic
112+
}
113+
114+
export default { renderVisualization }
115+
```
116+
117+
Each plugin iframe has basic styles of RedisInsight application, including fonts and color schemes.
118+
119+
It is recommended to use the React & [Elastic UI library](https://elastic.github.io/eui/#/) for
120+
consistency with plugin visualisations and the entire application.
121+
122+
Find the example of the plugin here.
123+
124+
* [Client List Plugin README](../../redisinsight/ui/src/packages/clients-list-example/README.md)
125+
* [Client List Plugin dir](../../redisinsight/ui/src/packages/clients-list-example/)
126+
127+
### Available parameters
128+
129+
Additional information provided to the plugin iframe is included in the `window.state`
130+
inside of the plugin script.
131+
132+
```javascript
133+
const { config, modules } = window.state
134+
const { baseUrl } = config
135+
136+
// modules - the list of modules of the current database
137+
// baseUrl - url for your plugin folder - can be used to include your assets
138+
```
139+
140+
### Plugin rendering
141+
To render the plugin visualization, the iframe with basic html is generated which is
142+
then populated with relevant scripts and styles. To render the html data, use existing
143+
DOM Element `#app` or create your own DOM Elements.
144+
Rendered iframe also includes `theme_DARK` or `theme_LIGHT` className on `body` to indicate the application theme used.
145+
146+
_Javascript Example:_
147+
```javascript
148+
const renderVisualization = (props) => {
149+
const { command, data } = props;
150+
document.getElementById('app')
151+
.innerHTML = `
152+
<h3>Executed command:<h3>
153+
<p>${command}</p>
154+
<h4>Result of the command</h4>
155+
<p>${data}</p>
156+
`
157+
}
158+
159+
export default { renderVisualization }
160+
```
161+
162+
_React Example:_
163+
```javascript
164+
import { render } from 'react-dom'
165+
import App from './App'
166+
167+
const renderVisualization = (props) => {
168+
const { command, status, data = '' } = props
169+
render(
170+
<App command={command} response={data} status={status} />,
171+
document.getElementById('app')
172+
)
173+
}
174+
175+
// This is a required action - export the main function for execution of the visualization
176+
export default { renderVisualization }
177+
```
178+
179+
180+
## Plugins communication
181+
> **_Future updates:_**
182+
Support of communication with the main application via a third-party library - _redisinsight-plugin-sdk_.

docs/plugins/installation.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Plugin installation & Usage
2+
3+
This document describes the guides to add `plugins` for the Workbench to RedisInsight.
4+
5+
## Installation guide
6+
7+
**Note**: While adding new plugins for Workbench, use files only from trusted
8+
authors to avoid automatic execution of malicious code.
9+
10+
1. Download the plugin for the Workbench.
11+
2. Open the `plugins` folder with the following path
12+
* For MacOs: `<usersHomeDir>/.redisinsight-v2.0/plugins`
13+
* For Windows: `C:\Users\{Username}\.redisinsight-v2.0\plugins`
14+
* For Linux: `<usersHomeDir>/.redisinsight-v2.0/plugins`
15+
3. Add the folder with plugin to the `plugins` folder
16+
17+
To see the uploaded plugin visualizations in the command results, reload the Workbench
18+
page and run Redis command relevant for this visualization.
19+
20+
21+
## Usage
22+
23+
The plugin may contain different visualizations for any Redis commands.
24+
Below you can find a guide to see command results in the uploaded plugin visualization:
25+
26+
1. Open RedisInsight
27+
2. Open a database added
28+
3. Open the Workbench
29+
4. Run the Redis command relevant for the plugin visualization
30+
5. Select the plugin visualization to display results in (if this visualization has not been set by default)

docs/plugins/introduction.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Introduction to plugins for the Workbench
2+
3+
Plugins allow the customization of visualizations for Redis commands executed
4+
in the Workbench inside the RedisInsight.
5+
6+
## Wiki
7+
8+
* [Installation and Usage](installation.md)
9+
* [Plugin Development](development.md)

electron-builder.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"dist/",
77
"node_modules/",
88
"index.html",
9+
"splash.html",
910
"main.prod.js",
1011
"main.prod.js.map",
1112
"package.json"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { BaseExceptionFilter } from '@nestjs/core';
2+
import { ArgumentsHost, Logger } from '@nestjs/common';
3+
import { Request, Response } from 'express';
4+
5+
export class GlobalExceptionFilter extends BaseExceptionFilter {
6+
private staticServerLogger = new Logger('StaticServerLogger');
7+
8+
catch(exception: Error, host: ArgumentsHost) {
9+
const ctx = host.switchToHttp();
10+
const request = ctx.getRequest<Request>();
11+
12+
if (/^\/(?:plugins|static)\//i.test(request.url)) {
13+
const response = ctx.getResponse<Response>();
14+
const statusCode = exception['statusCode'] || 500;
15+
const message = `Error when trying to fetch ${request.url}`;
16+
17+
this.staticServerLogger.error(message, { ...exception } as any);
18+
return response.status(statusCode)
19+
.json({
20+
statusCode,
21+
message,
22+
});
23+
}
24+
25+
return super.catch(exception, host);
26+
}
27+
}

redisinsight/api/src/main.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SwaggerModule } from '@nestjs/swagger';
33
import { NestApplicationOptions } from '@nestjs/common';
44
import * as bodyParser from 'body-parser';
55
import { WinstonModule } from 'nest-winston';
6+
import { GlobalExceptionFilter } from 'src/exceptions/global-exception.filter';
67
import { AppModule } from './app.module';
78
import SWAGGER_CONFIG from '../config/swagger';
89
import LOGGER_CONFIG from '../config/logger';
@@ -22,7 +23,7 @@ export default async function bootstrap() {
2223
}
2324

2425
const app = await NestFactory.create(AppModule, options);
25-
26+
app.useGlobalFilters(new GlobalExceptionFilter(app.getHttpAdapter()));
2627
app.use(bodyParser.json({ limit: '512mb' }));
2728
app.use(bodyParser.urlencoded({ limit: '512mb', extended: true }));
2829
app.enableCors();

redisinsight/main.dev.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export const getDisplayAppInTrayValue = (): boolean => {
9494
/**
9595
* Backend part...
9696
*/
97-
const port = 5000;
97+
const port = 5001;
9898
const launchApiServer = async () => {
9999
try {
100100
const detectPortConst = await detectPort(port);
@@ -134,19 +134,35 @@ const bootstrap = async () => {
134134

135135
export const windows = new Set<BrowserWindow>();
136136

137-
export const createWindow = async () => {
137+
const titleSplash = 'splash';
138+
export const createSplashScreen = async () => {
139+
const splash = new BrowserWindow({
140+
width: 500,
141+
height: 200,
142+
transparent: true,
143+
frame: false,
144+
resizable: false,
145+
alwaysOnTop: true,
146+
title: titleSplash,
147+
});
148+
149+
splash.loadURL(`file://${__dirname}/splash.html`);
150+
151+
return splash;
152+
};
153+
154+
export const createWindow = async (splash: BrowserWindow | null) => {
138155
const RESOURCES_PATH = app.isPackaged
139156
? path.join(process.resourcesPath, 'resources')
140157
: path.join(__dirname, '../resources');
141158

142-
const getAssetPath = (...paths: string[]): string => {
143-
return path.join(RESOURCES_PATH, ...paths);
144-
};
159+
const getAssetPath = (...paths: string[]): string => path.join(RESOURCES_PATH, ...paths);
145160

146161
let x;
147162
let y;
148163
const currentWindow = BrowserWindow.getFocusedWindow();
149-
if (currentWindow) {
164+
165+
if (currentWindow && currentWindow?.getTitle() !== titleSplash) {
150166
const [currentWindowX, currentWindowY] = currentWindow.getPosition();
151167
x = currentWindowX + 24;
152168
y = currentWindowY + 24;
@@ -188,8 +204,9 @@ export const createWindow = async () => {
188204
if (process.env.START_MINIMIZED) {
189205
newWindow.minimize();
190206
} else {
191-
newWindow.show();
192-
newWindow.focus();
207+
newWindow?.show();
208+
newWindow?.focus();
209+
splash?.close();
193210
}
194211
});
195212

@@ -293,7 +310,11 @@ app.on('continue-activity-error', (event, type, error) => {
293310
}
294311
});
295312

296-
app.whenReady().then(bootstrap).then(createWindow).catch(console.log);
313+
app.whenReady()
314+
.then(bootstrap)
315+
.then(createSplashScreen)
316+
.then(createWindow)
317+
.catch(console.log);
297318

298319
app.on('activate', () => {
299320
// On macOS it's common to re-create a window in the app when the

0 commit comments

Comments
 (0)