240 lines
6.8 KiB
Plaintext
240 lines
6.8 KiB
Plaintext
---
|
||
id: canvasPlugins
|
||
slug: /playground/kibana/canvas-plugins
|
||
title: Develop Canvas plugins
|
||
summary: Introduction to
|
||
date: 2021-02-18
|
||
tags: ['kibana', 'canvas', 'plugins']
|
||
related: []
|
||
---
|
||
|
||
To develop your own Canvas plugins, you simply create a Kibana plugin, and register your customizations with Canvas.
|
||
|
||
The following is a step-by-step guide to adding your own custom random number Canvas plugin.
|
||
|
||
## Generating a Kibana plugin
|
||
|
||
```bash
|
||
# in the kibana directory
|
||
# Rename canvas_example to whatever you want your plugin to be named
|
||
node scripts/generate_plugin.js canvas_example
|
||
```
|
||
|
||
This will prompt you for some input. Generally, you can answer as follows:
|
||
|
||
```
|
||
❯ node scripts/generate_plugin.js canvas_example
|
||
? Would you like to create the plugin in a different folder? No
|
||
? Provide a short description An awesome Kibana plugin
|
||
? What Kibana version are you targeting? master
|
||
? Should an app component be generated? No
|
||
? Should a server API be generated? No
|
||
? Should translation files be generated? No
|
||
? Would you like to use a custom eslint file? No
|
||
```
|
||
|
||
Once this has completed, go to your plugin directory:
|
||
|
||
```bash
|
||
cd plugins/canvas_example
|
||
```
|
||
|
||
Open that folder in your code editor of choice: `code .`
|
||
|
||
### Creating a Canvas element and function
|
||
Open your plugin's `kibana.json` file. Make sure that `ui` has a value of true, and that `'canvas'` is included in `requiredPlugins`. It should look something like this.
|
||
|
||
```json
|
||
{
|
||
"id": "canvasExample",
|
||
"version": "7.8.0",
|
||
"server": false,
|
||
"ui": true,
|
||
"requiredPlugins": ["canvas"],
|
||
"optionalPlugins": []
|
||
}
|
||
```
|
||
|
||
In your plugin folder, create a new folder `public` and an `index.ts` file within it.
|
||
|
||
This `index.ts` will need export a Kibana Plugin. You can use this as a starting point for your plugin.
|
||
|
||
```typescript
|
||
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
|
||
import { CanvasSetup } from '../../../x-pack/plugins/canvas/public';
|
||
|
||
interface CanvasExampleSetupPlugins {
|
||
canvas: CanvasSetup;
|
||
}
|
||
|
||
interface CanvasExampleStartPlugins {}
|
||
|
||
class CanvasExamplePlugin
|
||
implements Plugin<void, void, CanvasExampleSetupPlugins, CanvasExampleStartPlugins> {
|
||
setup(core: CoreSetup, plugins: CanvasExampleSetupPlugins) {}
|
||
|
||
start(core: CoreStart) {}
|
||
}
|
||
|
||
export const plugin = () => new CanvasExamplePlugin();
|
||
```
|
||
|
||
|
||
Now that the Kibana plugin boilerplate is out of the way, you can start adding functionality to Canvas.
|
||
|
||
Let's start by adding a new function.
|
||
|
||
In your `index.ts` add a new function definition:
|
||
|
||
```typescript
|
||
const canvasFunctions = [
|
||
() => ({
|
||
name: 'random',
|
||
help: 'Make a random number between 1 and 100',
|
||
args: {},
|
||
fn() {
|
||
return Math.floor(Math.random() * 100) + 1;
|
||
}
|
||
}),
|
||
];
|
||
```
|
||
|
||
Then, in the `setup` method of your plugin, you can add this new function definition to Canvas:
|
||
|
||
```typescript
|
||
setup(core: CoreSetup, plugins: CanvasExampleSetupPlugins) {
|
||
plugins.canvas.addFunctions(canvasFunctions);
|
||
}
|
||
```
|
||
|
||
Now, let's add a new Element type. In your `index.ts` add a new element definition:
|
||
|
||
```typescript
|
||
const elements = [
|
||
() => ({
|
||
name: 'randomNumber',
|
||
displayName: 'Random Number',
|
||
help: 'A random number between 1 and 100',
|
||
image: 'https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/bltb59c89a07c05b937/5c583a6602ac90e80ba0ab8f/icon-white-circle-elastic-stack.svg',
|
||
expression: 'random | metric "Random Number"',
|
||
}),
|
||
];
|
||
```
|
||
|
||
And then, in the `setup` method of the plugin, add this new element definition to Canvas, just like you did with the function:
|
||
|
||
```typescript
|
||
setup(core: CoreSetup, plugins: CanvasExampleSetupPlugins) {
|
||
plugins.canvas.addFunctions(canvasFunctions);
|
||
plugins.canvas.addElements(elements);
|
||
}
|
||
```
|
||
|
||
Now, your 'Random Number' element will show up in the list of other Canvas elements.
|
||
|
||
### Trying out your new plugin
|
||
|
||
In the terminal, in your plugin's directory, run:
|
||
|
||
```bash
|
||
# In plugins/canvas_example
|
||
yarn start
|
||
```
|
||
|
||
- Pull up Kibana in your browser: `http://localhost:5601`
|
||
- Go to canvas, and click: "Create workpad"
|
||
- Click: "Add element"
|
||
- Click: "Other"
|
||
- Click: "Random Number"
|
||
|
||
### Adding a server-side function
|
||
|
||
> Server side functions may be deprecated in a later version of Kibana
|
||
|
||
Now, let's add a function which runs on the server.
|
||
|
||
In your plugin's `kibana.json` file, set `server` to true, and add `"expressions"` as a requiredPlugin.
|
||
|
||
```typescript
|
||
{
|
||
"id": "canvasExample",
|
||
"version": "8.0.0",
|
||
"server": false,
|
||
"ui": true,
|
||
"requiredPlugins": ["canvas", "expressions"],
|
||
"optionalPlugins": []
|
||
}
|
||
```
|
||
|
||
Now, much like we made the client plugin, we'll make a server plugin.
|
||
|
||
Start by making the `server` directory and an `index.ts` file with a shell for your server plugin:
|
||
|
||
```typescript
|
||
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/server';
|
||
import { ExpressionsServerSetup } from '../../../src/plugins/expressions/server';
|
||
|
||
interface CanvasExamplePluginsSetup {
|
||
expressions: ExpressionsServerSetup;
|
||
}
|
||
|
||
class CanvasExamplePlugin implements Plugin {
|
||
setup(core: CoreSetup, plugins: CanvasExamplePluginsSetup) {}
|
||
|
||
start(core: CoreStart) {}
|
||
}
|
||
|
||
export const plugin = () => new CanvasExamplePlugin();
|
||
```
|
||
|
||
Now, we'll create a simple function definition that we will register on the server:
|
||
|
||
```typescript
|
||
const serverFunctions = [
|
||
() => ({
|
||
name: 'serverTime',
|
||
help: 'Get the server time in milliseconds',
|
||
args: {},
|
||
fn() {
|
||
return Date.now();
|
||
},
|
||
}),
|
||
];
|
||
```
|
||
|
||
And then in our setup method, register it with the Expressions plugin:
|
||
|
||
```typescript
|
||
setup(core: CoreSetup, plugins: CanvasExamplePluginsSetup) {
|
||
serverFunctions.forEach((f) => plugins.expressions.registerFunction(f));
|
||
}
|
||
```
|
||
|
||
Now, let's try out our new server function.
|
||
|
||
- Refresh your browser.
|
||
- In the same Canvas workpad:
|
||
- Add another Random Number element as before
|
||
- Click that element to select it
|
||
- Click "Expression editor"
|
||
- Modify the expression to look like this: `serverTime | metric "Server Time in ms"`
|
||
- Click "Run"
|
||
|
||
You should now see one random number and one "Server Time in ms" value.
|
||
|
||
> More information about building Kibana Plugins can be found in [src/core](https://github.com/elastic/kibana/blob/master/src/core/README.md)
|
||
|
||
### My Canvas Plugin stopped working
|
||
|
||
If your Kibana Server is crashing on startup with a message like
|
||
|
||
> **FATAL** Error: Unmet requirement "canvas" for plugin "your_plugin_name"
|
||
|
||
or
|
||
|
||
> **FATAL** Error: Unmet requirement "interpreter" for plugin "your_plugin_name"
|
||
|
||
then your plugin was likely created to work on a previous version of Kibana. Starting with version 7.8, the plugin system was redesigned and caused breaking changes to these earlier plugins.
|
||
|
||
The good news is that all of your existing Canvas extension code can be reused, it just needs to be in an updated Kibana plugin. Follow the [instructions](#generating-a-kibana-plugin) for creating a new Canvas Kibana plugin, and then add in your existing functions and elements.
|