Skip to content

Commit

Permalink
[Expressions] Create expressions function to get UI settings (#101317)
Browse files Browse the repository at this point in the history
  • Loading branch information
dokmic committed Jun 10, 2021
1 parent 2621efd commit 0b43085
Show file tree
Hide file tree
Showing 26 changed files with 560 additions and 54 deletions.
27 changes: 27 additions & 0 deletions docs/canvas/canvas-function-reference.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -2893,6 +2893,33 @@ Alias: `type`
[[u_fns]]
== U

[float]
[[uiSetting_fn]]
=== `uiSetting`

Returns a UI settings parameter value.

*Accepts:* `null`

[cols="3*^<"]
|===
|Argument |Type |Description

|_Unnamed_ ***

Aliases: `parameter`
|`string`
|The parameter name.

|`default`
|`any`
|A default value in case of the parameter is not set.

Default: `null`
|===

*Returns:* `ui_setting`

[float]
[[urlparam_fn]]
=== `urlparam`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ The actual function is defined in the <code>fn</code> key. The function can be \
| Method | Modifiers | Description |
| --- | --- | --- |
| [setup()](./kibana-plugin-plugins-expressions-public.expressionsservice.setup.md) | | Returns Kibana Platform \*setup\* life-cycle contract. Useful to return the same contract on server-side and browser-side. |
| [start()](./kibana-plugin-plugins-expressions-public.expressionsservice.start.md) | | Returns Kibana Platform \*start\* life-cycle contract. Useful to return the same contract on server-side and browser-side. |
| [setup(args)](./kibana-plugin-plugins-expressions-public.expressionsservice.setup.md) | | Returns Kibana Platform \*setup\* life-cycle contract. Useful to return the same contract on server-side and browser-side. |
| [start(args)](./kibana-plugin-plugins-expressions-public.expressionsservice.start.md) | | Returns Kibana Platform \*start\* life-cycle contract. Useful to return the same contract on server-side and browser-side. |
| [stop()](./kibana-plugin-plugins-expressions-public.expressionsservice.stop.md) | | |
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ Returns Kibana Platform \*setup\* life-cycle contract. Useful to return the same
<b>Signature:</b>

```typescript
setup(): ExpressionsServiceSetup;
setup(...args: unknown[]): ExpressionsServiceSetup;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| args | <code>unknown[]</code> | |

<b>Returns:</b>

`ExpressionsServiceSetup`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ Returns Kibana Platform \*start\* life-cycle contract. Useful to return the same
<b>Signature:</b>

```typescript
start(): ExpressionsServiceStart;
start(...args: unknown[]): ExpressionsServiceStart;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| args | <code>unknown[]</code> | |

<b>Returns:</b>

`ExpressionsServiceStart`
Expand Down
26 changes: 16 additions & 10 deletions src/plugins/expressions/common/executor/executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ describe('Executor', () => {
executor.registerFunction(expressionFunctions.clog);
});

test('can register all functions', () => {
const executor = new Executor();
for (const functionDefinition of expressionFunctions.functionSpecs)
executor.registerFunction(functionDefinition);
});

test('can retrieve all functions', () => {
const executor = new Executor();
executor.registerFunction(expressionFunctions.clog);
Expand All @@ -67,12 +61,24 @@ describe('Executor', () => {

test('can retrieve all functions - 2', () => {
const executor = new Executor();
for (const functionDefinition of expressionFunctions.functionSpecs)
const functionSpecs = [
expressionFunctions.clog,
expressionFunctions.font,
expressionFunctions.variableSet,
expressionFunctions.variable,
expressionFunctions.theme,
expressionFunctions.cumulativeSum,
expressionFunctions.derivative,
expressionFunctions.movingAverage,
expressionFunctions.mapColumn,
expressionFunctions.math,
];
for (const functionDefinition of functionSpecs) {
executor.registerFunction(functionDefinition);
}
const functions = executor.getFunctions();
expect(Object.keys(functions).sort()).toEqual(
expressionFunctions.functionSpecs.map((spec) => spec.name).sort()
);

expect(Object.keys(functions).sort()).toEqual(functionSpecs.map((spec) => spec.name).sort());
});
});

Expand Down
3 changes: 1 addition & 2 deletions src/plugins/expressions/common/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { ExpressionType } from '../expression_types/expression_type';
import { AnyExpressionTypeDefinition } from '../expression_types/types';
import { ExpressionAstExpression, ExpressionAstFunction } from '../ast';
import { ExpressionValueError, typeSpecs } from '../expression_types/specs';
import { functionSpecs } from '../expression_functions/specs';
import { getByAlias } from '../util';
import { SavedObjectReference } from '../../../../core/types';
import { PersistableStateService, SerializableState } from '../../../kibana_utils/common';
Expand Down Expand Up @@ -85,7 +84,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
): Executor<Ctx> {
const executor = new Executor<Ctx>(state);
for (const type of typeSpecs) executor.registerType(type);
for (const func of functionSpecs) executor.registerFunction(func);

return executor;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,6 @@
* Side Public License, v 1.
*/

import { clog } from './clog';
import { font } from './font';
import { variableSet } from './var_set';
import { variable } from './var';
import { AnyExpressionFunctionDefinition } from '../types';
import { theme } from './theme';
import { cumulativeSum } from './cumulative_sum';
import { derivative } from './derivative';
import { movingAverage } from './moving_average';
import { mapColumn } from './map_column';
import { math } from './math';

export const functionSpecs: AnyExpressionFunctionDefinition[] = [
clog,
font,
variableSet,
variable,
theme,
cumulativeSum,
derivative,
movingAverage,
mapColumn,
math,
];

export * from './clog';
export * from './font';
export * from './var_set';
Expand All @@ -39,5 +14,6 @@ export * from './theme';
export * from './cumulative_sum';
export * from './derivative';
export * from './moving_average';
export * from './ui_setting';
export { mapColumn, MapColumnArguments } from './map_column';
export { math, MathArguments, MathInput } from './math';
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

jest.mock('../../../../common');

import { IUiSettingsClient } from 'src/core/public';
import { getUiSettingFn } from '../ui_setting';

describe('uiSetting', () => {
describe('fn', () => {
let getStartDependencies: jest.MockedFunction<
Parameters<typeof getUiSettingFn>[0]['getStartDependencies']
>;
let uiSetting: ReturnType<typeof getUiSettingFn>;
let uiSettings: jest.Mocked<IUiSettingsClient>;

beforeEach(() => {
uiSettings = ({
get: jest.fn(),
} as unknown) as jest.Mocked<IUiSettingsClient>;
getStartDependencies = (jest.fn(async () => ({
uiSettings,
})) as unknown) as typeof getStartDependencies;

uiSetting = getUiSettingFn({ getStartDependencies });
});

it('should return a value', () => {
uiSettings.get.mockReturnValueOnce('value');

expect(uiSetting.fn(null, { parameter: 'something' }, {} as any)).resolves.toEqual({
type: 'ui_setting',
key: 'something',
value: 'value',
});
});

it('should pass a default value', async () => {
await uiSetting.fn(null, { parameter: 'something', default: 'default' }, {} as any);

expect(uiSettings.get).toHaveBeenCalledWith('something', 'default');
});

it('should throw an error when parameter does not exist', () => {
uiSettings.get.mockImplementationOnce(() => {
throw new Error();
});

expect(uiSetting.fn(null, { parameter: 'something' }, {} as any)).rejects.toEqual(
new Error('Invalid parameter "something".')
);
});

it('should get a request instance on the server-side', async () => {
const request = {};
await uiSetting.fn(null, { parameter: 'something' }, {
getKibanaRequest: () => request,
} as any);

const [[getKibanaRequest]] = getStartDependencies.mock.calls;

expect(getKibanaRequest()).toBe(request);
});

it('should throw an error if request is not provided on the server-side', async () => {
await uiSetting.fn(null, { parameter: 'something' }, {} as any);

const [[getKibanaRequest]] = getStartDependencies.mock.calls;

expect(getKibanaRequest).toThrow();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import type { KibanaRequest } from 'src/core/server';
import { i18n } from '@kbn/i18n';
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
import { UiSetting } from '../../expression_types/specs/ui_setting';

interface UiSettingsClient {
get<T>(key: string, defaultValue?: T): T | Promise<T>;
}

interface UiSettingStartDependencies {
uiSettings: UiSettingsClient;
}

interface UiSettingFnArguments {
getStartDependencies(getKibanaRequest: () => KibanaRequest): Promise<UiSettingStartDependencies>;
}

export interface UiSettingArguments {
default?: unknown;
parameter: string;
}

export type ExpressionFunctionUiSetting = ExpressionFunctionDefinition<
'uiSetting',
unknown,
UiSettingArguments,
Promise<UiSetting>
>;

export function getUiSettingFn({
getStartDependencies,
}: UiSettingFnArguments): ExpressionFunctionUiSetting {
return {
name: 'uiSetting',
help: i18n.translate('expressions.functions.uiSetting.help', {
defaultMessage: 'Returns a UI settings parameter value.',
}),
args: {
default: {
help: i18n.translate('expressions.functions.uiSetting.args.default', {
defaultMessage: 'A default value in case of the parameter is not set.',
}),
},
parameter: {
aliases: ['_'],
help: i18n.translate('expressions.functions.uiSetting.args.parameter', {
defaultMessage: 'The parameter name.',
}),
required: true,
types: ['string'],
},
},
async fn(input, { default: defaultValue, parameter }, { getKibanaRequest }) {
const { uiSettings } = await getStartDependencies(() => {
const request = getKibanaRequest?.();
if (!request) {
throw new Error(
i18n.translate('expressions.functions.uiSetting.error.kibanaRequest', {
defaultMessage:
'A KibanaRequest is required to get UI settings on the server. ' +
'Please provide a request object to the expression execution params.',
})
);
}

return request;
});

try {
return {
type: 'ui_setting',
key: parameter,
value: await uiSettings.get(parameter, defaultValue),
};
} catch {
throw new Error(
i18n.translate('expressions.functions.uiSetting.error.parameter', {
defaultMessage: 'Invalid parameter "{parameter}".',
values: { parameter },
})
);
}
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { shape } from './shape';
import { string } from './string';
import { style } from './style';
import { AnyExpressionTypeDefinition } from '../types';
import { uiSetting } from './ui_setting';

export const typeSpecs: AnyExpressionTypeDefinition[] = [
boolean,
Expand All @@ -37,6 +38,7 @@ export const typeSpecs: AnyExpressionTypeDefinition[] = [
shape,
string,
style,
uiSetting,
];

export * from './boolean';
Expand All @@ -53,3 +55,4 @@ export * from './render';
export * from './shape';
export * from './string';
export * from './style';
export * from './ui_setting';
Loading

0 comments on commit 0b43085

Please sign in to comment.