Skip to content

Commit

Permalink
lens should register expression functions in setup contract (#110639)
Browse files Browse the repository at this point in the history
* lens should register expression functions in setup contract

Closes: #106510

* fix CI

* build optimization

* build optimizations - step 3

* fix CI

* try to optimize bundle

* Update x-pack/plugins/lens/common/expressions/time_scale/types.ts

Co-authored-by: Marta Bondyra <[email protected]>

* Update types.ts

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Marta Bondyra <[email protected]>
  • Loading branch information
3 people committed Sep 7, 2021
1 parent b7014c9 commit 0ca1c89
Show file tree
Hide file tree
Showing 60 changed files with 883 additions and 758 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import { functionWrapper } from 'src/plugins/expressions/common/expression_funct

describe('lens_counter_rate', () => {
const fn = functionWrapper(counterRate);
const runFn = (input: Datatable, args: CounterRateArgs) => fn(input, args) as Datatable;
const runFn = (input: Datatable, args: CounterRateArgs) => fn(input, args) as Promise<Datatable>;

it('calculates counter rate', () => {
const result = runFn(
it('calculates counter rate', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
Expand All @@ -31,8 +31,8 @@ describe('lens_counter_rate', () => {
expect(result.rows.map((row) => row.output)).toEqual([undefined, 0, 2, 3, 2]);
});

it('calculates counter rate with decreasing values in input', () => {
const result = runFn(
it('calculates counter rate with decreasing values in input', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
Expand All @@ -48,8 +48,8 @@ describe('lens_counter_rate', () => {
expect(result.rows.map((row) => row.output)).toEqual([undefined, 6, 5, 4]);
});

it('skips null or undefined values until there is real data', () => {
const result = runFn(
it('skips null or undefined values until there is real data', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
Expand Down Expand Up @@ -85,8 +85,8 @@ describe('lens_counter_rate', () => {
]);
});

it('treats 0 as real data', () => {
const result = runFn(
it('treats 0 as real data', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
Expand Down Expand Up @@ -123,8 +123,8 @@ describe('lens_counter_rate', () => {
]);
});

it('calculates counter rate for multiple series', () => {
const result = runFn(
it('calculates counter rate for multiple series', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand Down Expand Up @@ -157,8 +157,8 @@ describe('lens_counter_rate', () => {
]);
});

it('treats missing split column as separate series', () => {
const result = runFn(
it('treats missing split column as separate series', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand Down Expand Up @@ -190,8 +190,8 @@ describe('lens_counter_rate', () => {
]);
});

it('treats null like undefined and empty string for split columns', () => {
const result = runFn(
it('treats null like undefined and empty string for split columns', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand Down Expand Up @@ -225,8 +225,8 @@ describe('lens_counter_rate', () => {
]);
});

it('calculates counter rate for multiple series by multiple split columns', () => {
const result = runFn(
it('calculates counter rate for multiple series by multiple split columns', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand Down Expand Up @@ -259,8 +259,8 @@ describe('lens_counter_rate', () => {
]);
});

it('splits separate series by the string representation of the cell values', () => {
const result = runFn(
it('splits separate series by the string representation of the cell values', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand All @@ -280,8 +280,8 @@ describe('lens_counter_rate', () => {
expect(result.rows.map((row) => row.output)).toEqual([undefined, 2 - 1, undefined, 11 - 10]);
});

it('casts values to number before calculating counter rate', () => {
const result = runFn(
it('casts values to number before calculating counter rate', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
Expand All @@ -292,8 +292,8 @@ describe('lens_counter_rate', () => {
expect(result.rows.map((row) => row.output)).toEqual([undefined, 7 - 5, 3, 2]);
});

it('casts values to number before calculating counter rate for NaN like values', () => {
const result = runFn(
it('casts values to number before calculating counter rate for NaN like values', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
Expand All @@ -304,8 +304,8 @@ describe('lens_counter_rate', () => {
expect(result.rows.map((row) => row.output)).toEqual([undefined, 7 - 5, NaN, 2, 5 - 2]);
});

it('copies over meta information from the source column', () => {
const result = runFn(
it('copies over meta information from the source column', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand Down Expand Up @@ -346,8 +346,8 @@ describe('lens_counter_rate', () => {
});
});

it('sets output name on output column if specified', () => {
const result = runFn(
it('sets output name on output column if specified', async () => {
const result = await runFn(
{
type: 'datatable',
columns: [
Expand All @@ -370,7 +370,7 @@ describe('lens_counter_rate', () => {
});
});

it('returns source table if input column does not exist', () => {
it('returns source table if input column does not exist', async () => {
const input: Datatable = {
type: 'datatable',
columns: [
Expand All @@ -384,12 +384,16 @@ describe('lens_counter_rate', () => {
],
rows: [{ val: 5 }],
};
expect(runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe(input);
expect(await runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe(
input
);
});

it('throws an error if output column exists already', () => {
expect(() =>
runFn(
it('throws an error if output column exists already', async () => {
let error: Error | undefined;

try {
await runFn(
{
type: 'datatable',
columns: [
Expand All @@ -404,7 +408,13 @@ describe('lens_counter_rate', () => {
rows: [{ val: 5 }],
},
{ inputColumnId: 'val', outputColumnId: 'val' }
)
).toThrow();
);
} catch (e) {
error = e;
}

expect(error).toMatchInlineSnapshot(
`[Error: Specified outputColumnId val already exists. Please pick another column id.]`
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
buildResultColumns,
getBucketIdentifier,
} from '../../../../../../src/plugins/expressions/common';
import type { CounterRateExpressionFunction } from './types';

export const counterRateFn: CounterRateExpressionFunction['fn'] = (
input,
{ by, inputColumnId, outputColumnId, outputColumnName }
) => {
const resultColumns = buildResultColumns(input, outputColumnId, inputColumnId, outputColumnName);

if (!resultColumns) {
return input;
}
const previousValues: Partial<Record<string, number>> = {};

return {
...input,
columns: resultColumns,
rows: input.rows.map((row) => {
const newRow = { ...row };

const bucketIdentifier = getBucketIdentifier(row, by);
const previousValue = previousValues[bucketIdentifier];
const currentValue = newRow[inputColumnId];
if (currentValue != null && previousValue != null) {
const currentValueAsNumber = Number(currentValue);
if (currentValueAsNumber >= previousValue) {
newRow[outputColumnId] = currentValueAsNumber - previousValue;
} else {
newRow[outputColumnId] = currentValueAsNumber;
}
} else {
newRow[outputColumnId] = undefined;
}

if (currentValue != null) {
previousValues[bucketIdentifier] = Number(currentValue);
} else {
previousValues[bucketIdentifier] = undefined;
}

return newRow;
}),
};
};
64 changes: 7 additions & 57 deletions x-pack/plugins/lens/common/expressions/counter_rate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,8 @@
*/

import { i18n } from '@kbn/i18n';
import {
getBucketIdentifier,
buildResultColumns,
} from '../../../../../../src/plugins/expressions/common';
import type {
ExpressionFunctionDefinition,
Datatable,
} from '../../../../../../src/plugins/expressions/common';

import type { CounterRateExpressionFunction } from './types';

export interface CounterRateArgs {
by?: string[];
Expand All @@ -22,13 +16,6 @@ export interface CounterRateArgs {
outputColumnName?: string;
}

export type ExpressionFunctionCounterRate = ExpressionFunctionDefinition<
'lens_counter_rate',
Datatable,
CounterRateArgs,
Datatable
>;

/**
* Calculates the counter rate of a specified column in the data table.
*
Expand Down Expand Up @@ -59,7 +46,7 @@ export type ExpressionFunctionCounterRate = ExpressionFunctionDefinition<
* before comparison. If the values are objects, the return value of their `toString` method will be used for comparison.
* Missing values (`null` and `undefined`) will be treated as empty strings.
*/
export const counterRate: ExpressionFunctionCounterRate = {
export const counterRate: CounterRateExpressionFunction = {
name: 'lens_counter_rate',
type: 'datatable',

Expand Down Expand Up @@ -101,46 +88,9 @@ export const counterRate: ExpressionFunctionCounterRate = {
},
},

fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) {
const resultColumns = buildResultColumns(
input,
outputColumnId,
inputColumnId,
outputColumnName
);

if (!resultColumns) {
return input;
}
const previousValues: Partial<Record<string, number>> = {};
return {
...input,
columns: resultColumns,
rows: input.rows.map((row) => {
const newRow = { ...row };

const bucketIdentifier = getBucketIdentifier(row, by);
const previousValue = previousValues[bucketIdentifier];
const currentValue = newRow[inputColumnId];
if (currentValue != null && previousValue != null) {
const currentValueAsNumber = Number(currentValue);
if (currentValueAsNumber >= previousValue) {
newRow[outputColumnId] = currentValueAsNumber - previousValue;
} else {
newRow[outputColumnId] = currentValueAsNumber;
}
} else {
newRow[outputColumnId] = undefined;
}

if (currentValue != null) {
previousValues[bucketIdentifier] = Number(currentValue);
} else {
previousValues[bucketIdentifier] = undefined;
}

return newRow;
}),
};
async fn(...args) {
/** Build optimization: prevent adding extra code into initial bundle **/
const { counterRateFn } = await import('./counter_rate_fn');
return counterRateFn(...args);
},
};
16 changes: 16 additions & 0 deletions x-pack/plugins/lens/common/expressions/counter_rate/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Datatable, ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions';
import { CounterRateArgs } from './index';

export type CounterRateExpressionFunction = ExpressionFunctionDefinition<
'lens_counter_rate',
Datatable,
CounterRateArgs,
Datatable | Promise<Datatable>
>;
Loading

0 comments on commit 0ca1c89

Please sign in to comment.