Skip to content

Commit

Permalink
docs: ✏️ add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Feb 10, 2020
1 parent 04d6549 commit d621956
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 25 deletions.
28 changes: 28 additions & 0 deletions src/plugins/expressions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# `expressions` plugin

Expressions power visualizations in Dashboard and Lens, as well as, every
*element* in Canvas is backed by an expression.

Expression pipeline is a chain of functions that *pipe* its output to the
input of the next function. Functions can be configured using arguments provided
by the user. The final output of the expression pipeline can be rendered using
one of the *renderers* registered in `expressions` plugin.

Below is an example of one Canvas element that fetches data using `essql` function,
pipes it further to `math` and `metric` functions, and final `render` function
renders the result.

```
filters
| essql
query="SELECT COUNT(timestamp) as total_errors
FROM kibana_sample_data_logs
WHERE tags LIKE '%warning%' OR tags LIKE '%error%'"
| math "total_errors"
| metric "TOTAL ISSUES"
metricFont={font family="'Open Sans', Helvetica, Arial, sans-serif" size=48 align="left" color="#FFFFFF" weight="normal" underline=false italic=false}
labelFont={font family="'Open Sans', Helvetica, Arial, sans-serif" size=30 align="left" color="#FFFFFF" weight="lighter" underline=false italic=false}
| render
```

![image](https://user-images.githubusercontent.com/9773803/74162514-3250a880-4c21-11ea-9e68-86f66862a183.png)
113 changes: 88 additions & 25 deletions src/plugins/expressions/common/service/expressions_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,104 @@
import { Executor } from '../executor';
import { ExpressionRendererRegistry } from '../expression_renderers';

export interface ExpressionsServiceSetup {
readonly getFunctions: Executor['getFunctions'];
readonly getRenderer: ExpressionRendererRegistry['get'];
readonly getRenderers: ExpressionRendererRegistry['toJS'];
readonly getTypes: Executor['getTypes'];
readonly registerFunction: Executor['registerFunction'];
readonly registerRenderer: ExpressionRendererRegistry['register'];
readonly registerType: Executor['registerType'];
readonly run: Executor['run'];
}

export interface ExpressionsServiceStart {
readonly getFunctions: Executor['getFunctions'];
readonly getRenderer: ExpressionRendererRegistry['get'];
readonly getRenderers: ExpressionRendererRegistry['toJS'];
readonly getTypes: Executor['getTypes'];
readonly run: Executor['run'];
}
export type ExpressionsServiceSetup = ReturnType<ExpressionsService['setup']>;
export type ExpressionsServiceStart = ReturnType<ExpressionsService['start']>;

/**
* `ExpressionsService` is class is used for multiple purposes:
*
* 1. It implements the same Expressions service that can be used on both:
* (1) server-side and (2) browser-side.
* 2. It implements the same Expressions service that users can fork/clone,
* thus have their own instance of the Expressions plugin.
* 3. `ExpressionsService` defines the public contracts of *setup* and *start*
* Kibana Platform life-cycles for ease-of-use on server-side and browser-side.
* 4. `ExpressionsService` creates a bound version of all exported contract functions.
* 5. Functions are bound the way there are:
*
* ```ts
* registerFunction = (...args: Parameters<Executor['registerFunction']>
* ): ReturnType<Executor['registerFunction']> => this.executor.registerFunction(...args);
* ```
*
* so that JSDoc appears in developers IDE when they use those `plugins.expressions.registerFunction(`.
*/
export class ExpressionsService {
public readonly executor = Executor.createWithDefaults();
public readonly renderers = new ExpressionRendererRegistry();

public setup(): ExpressionsServiceSetup {
const { executor, renderers } = this;
/**
* Register an expression function, which will be possible to execute as
* part of the expression pipeline.
*
* Below we register a function which simply sleeps for given number of
* milliseconds to delay the execution and outputs its input as-is.
*
* ```ts
* expressions.registerFunction({
* name: 'sleep',
* args: {
* time: {
* aliases: ['_'],
* help: 'Time in milliseconds for how long to sleep',
* types: ['number'],
* },
* },
* help: '',
* fn: async (input, args, context) => {
* await new Promise(r => setTimeout(r, args.time));
* return input;
* },
* }
* ```
*
* The actual function is defined in the `fn` key. The function can be *async*.
* It receives three arguments: (1) `input` is the output of the previous function
* or the initial input of the expression if the function is first in chain;
* (2) `args` are function arguments as defined in expression string, that can
* be edited by user (e.g in case of Canvas); (3) `context` is a shared object
* passed to all functions that can be used for side-effects.
*/
public readonly registerFunction = (
...args: Parameters<Executor['registerFunction']>
): ReturnType<Executor['registerFunction']> => this.executor.registerFunction(...args);

/**
* Executes expression string or a parsed expression AST and immediately
* returns the result.
*
* Below example will execute `sleep 100 | clog` expression with `123` initial
* input to the first function.
*
* ```ts
* expressions.run('sleep 100 | clog', 123);
* ```
*
* - `sleep 100` will delay execution by 100 milliseconds and pass the `123` input as
* its output.
* - `clog` will print to console `123` and pass it as its output.
* - The final result of the execution will be `123`.
*
* Optionally, you can pass an object as the third argument which will be used
* to extend the `ExecutionContext`&mdash;an object passed to each function
* as the third argument, that allows functions to perform side-effects.
*
* ```ts
* expressions.run('...', null, { elasticsearchClient });
* ```
*/
public readonly run = (...args: Parameters<Executor['run']>): ReturnType<Executor['run']> =>
this.executor.run(...args);

public setup() {
const { executor, renderers, registerFunction, run } = this;

const getFunctions = executor.getFunctions.bind(executor);
const getRenderer = renderers.get.bind(renderers);
const getRenderers = renderers.toJS.bind(renderers);
const getTypes = executor.getTypes.bind(executor);
const registerFunction = executor.registerFunction.bind(executor);
const registerRenderer = renderers.register.bind(renderers);
const registerType = executor.registerType.bind(executor);
const run = executor.run.bind(executor);

return {
getFunctions,
Expand All @@ -67,14 +131,13 @@ export class ExpressionsService {
};
}

public start(): ExpressionsServiceStart {
const { executor, renderers } = this;
public start() {
const { executor, renderers, run } = this;

const getFunctions = executor.getFunctions.bind(executor);
const getRenderer = renderers.get.bind(renderers);
const getRenderers = renderers.toJS.bind(renderers);
const getTypes = executor.getTypes.bind(executor);
const run = executor.run.bind(executor);

return {
getFunctions,
Expand Down

0 comments on commit d621956

Please sign in to comment.