Skip to content

Commit

Permalink
[Canvas] Adds Save and Return Workflow (#111411)
Browse files Browse the repository at this point in the history
  • Loading branch information
cqliu1 committed Oct 28, 2021
1 parent 1710809 commit cfee5e1
Show file tree
Hide file tree
Showing 28 changed files with 365 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
*/

import { ExpressionTypeDefinition } from '../../../../../src/plugins/expressions';
import { EmbeddableInput } from '../../../../../src/plugins/embeddable/common/';
import { EmbeddableInput } from '../../types';
import { EmbeddableTypes } from './embeddable_types';

export const EmbeddableExpressionType = 'embeddable';
export { EmbeddableTypes, EmbeddableInput };

export interface EmbeddableExpression<Input extends EmbeddableInput> {
/**
* The type of the expression result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,8 @@
*/

import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
import { TimeRange } from 'src/plugins/data/public';
import { Filter } from '@kbn/es-query';
import { ExpressionValueFilter } from '../../../types';
import {
EmbeddableExpressionType,
EmbeddableExpression,
EmbeddableInput as Input,
} from '../../expression_types';
import { ExpressionValueFilter, EmbeddableInput } from '../../../types';
import { EmbeddableExpressionType, EmbeddableExpression } from '../../expression_types';
import { getFunctionHelp } from '../../../i18n';
import { SavedObjectReference } from '../../../../../../src/core/types';
import { getQueryFilters } from '../../../common/lib/build_embeddable_filters';
Expand All @@ -29,12 +23,6 @@ const defaultTimeRange = {
to: 'now',
};

export type EmbeddableInput = Input & {
timeRange?: TimeRange;
filters?: Filter[];
savedObjectId: string;
};

const baseEmbeddableInput = {
timeRange: defaultTimeRange,
disableTriggers: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
import { PaletteOutput } from 'src/plugins/charts/common';
import { Filter as DataFilter } from '@kbn/es-query';
import { TimeRange } from 'src/plugins/data/common';
import { EmbeddableInput } from 'src/plugins/embeddable/common';
import { getQueryFilters } from '../../../common/lib/build_embeddable_filters';
import { ExpressionValueFilter, TimeRange as TimeRangeArg } from '../../../types';
import { ExpressionValueFilter, EmbeddableInput, TimeRange as TimeRangeArg } from '../../../types';
import {
EmbeddableTypes,
EmbeddableExpressionType,
Expand All @@ -27,7 +26,7 @@ interface Arguments {
}

export type SavedLensInput = EmbeddableInput & {
id: string;
savedObjectId: string;
timeRange?: TimeRange;
filters: DataFilter[];
palette?: PaletteOutput;
Expand Down Expand Up @@ -73,18 +72,19 @@ export function savedLens(): ExpressionFunctionDefinition<
},
},
type: EmbeddableExpressionType,
fn: (input, args) => {
fn: (input, { id, timerange, title, palette }) => {
const filters = input ? input.and : [];

return {
type: EmbeddableExpressionType,
input: {
id: args.id,
id,
savedObjectId: id,
filters: getQueryFilters(filters),
timeRange: args.timerange || defaultTimeRange,
title: args.title === null ? undefined : args.title,
timeRange: timerange || defaultTimeRange,
title: title === null ? undefined : title,
disableTriggers: true,
palette: args.palette,
palette,
},
embeddableType: EmbeddableTypes.lens,
generatedAt: Date.now(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const defaultTimeRange = {
to: 'now',
};

type Output = EmbeddableExpression<MapEmbeddableInput>;
type Output = EmbeddableExpression<MapEmbeddableInput & { savedObjectId: string }>;

export function savedMap(): ExpressionFunctionDefinition<
'savedMap',
Expand Down Expand Up @@ -85,8 +85,9 @@ export function savedMap(): ExpressionFunctionDefinition<
return {
type: EmbeddableExpressionType,
input: {
attributes: { title: '' },
id: args.id,
attributes: { title: '' },
savedObjectId: args.id,
filters: getQueryFilters(filters),
timeRange: args.timerange || defaultTimeRange,
refreshConfig: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface Arguments {
title: string | null;
}

type Output = EmbeddableExpression<VisualizeInput>;
type Output = EmbeddableExpression<VisualizeInput & { savedObjectId: string }>;

const defaultTimeRange = {
from: 'now-15m',
Expand Down Expand Up @@ -94,6 +94,7 @@ export function savedVisualization(): ExpressionFunctionDefinition<
type: EmbeddableExpressionType,
input: {
id,
savedObjectId: id,
disableTriggers: true,
timeRange: timerange || defaultTimeRange,
filters: getQueryFilters(filters),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import {
IEmbeddable,
EmbeddableFactory,
EmbeddableFactoryNotFoundError,
isErrorEmbeddable,
} from '../../../../../../src/plugins/embeddable/public';
import { EmbeddableExpression } from '../../expression_types/embeddable';
import { RendererStrings } from '../../../i18n';
import { embeddableInputToExpression } from './embeddable_input_to_expression';
import { EmbeddableInput } from '../../expression_types';
import { RendererFactory } from '../../../types';
import { RendererFactory, EmbeddableInput } from '../../../types';
import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib';

const { embeddable: strings } = RendererStrings;
Expand Down Expand Up @@ -71,16 +71,27 @@ export const embeddableRendererFactory = (
throw new EmbeddableFactoryNotFoundError(embeddableType);
}

const embeddablePromise = factory
.createFromSavedObject(input.id, input)
.then((embeddable) => {
// stores embeddable in registrey
embeddablesRegistry[uniqueId] = embeddable;
return embeddable;
});
embeddablesRegistry[uniqueId] = embeddablePromise;

const embeddableObject = await (async () => embeddablePromise)();
const embeddableInput = { ...input, id: uniqueId };

const embeddablePromise = input.savedObjectId
? factory
.createFromSavedObject(input.savedObjectId, embeddableInput)
.then((embeddable) => {
// stores embeddable in registrey
embeddablesRegistry[uniqueId] = embeddable;
return embeddable;
})
: factory.create(embeddableInput).then((embeddable) => {
if (!embeddable || isErrorEmbeddable(embeddable)) {
return;
}
// stores embeddable in registry
embeddablesRegistry[uniqueId] = embeddable as IEmbeddable;
return embeddable;
});
embeddablesRegistry[uniqueId] = embeddablePromise as Promise<IEmbeddable>;

const embeddableObject = (await (async () => embeddablePromise)()) as IEmbeddable;

const palettes = await plugins.charts.palettes.getPalettes();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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 { toExpression } from './embeddable';
import { EmbeddableInput } from '../../../../types';
import { decode } from '../../../../common/lib/embeddable_dataurl';
import { fromExpression } from '@kbn/interpreter/common';

describe('toExpression', () => {
describe('by-reference embeddable input', () => {
const baseEmbeddableInput = {
id: 'elementId',
savedObjectId: 'embeddableId',
filters: [],
};

it('converts to an embeddable expression', () => {
const input: EmbeddableInput = baseEmbeddableInput;

const expression = toExpression(input, 'visualization');
const ast = fromExpression(expression);

expect(ast.type).toBe('expression');
expect(ast.chain[0].function).toBe('embeddable');
expect(ast.chain[0].arguments.type[0]).toBe('visualization');

const config = decode(ast.chain[0].arguments.config[0] as string);

expect(config.savedObjectId).toStrictEqual(input.savedObjectId);
});

it('includes optional input values', () => {
const input: EmbeddableInput = {
...baseEmbeddableInput,
title: 'title',
timeRange: {
from: 'now-1h',
to: 'now',
},
};

const expression = toExpression(input, 'visualization');
const ast = fromExpression(expression);

const config = decode(ast.chain[0].arguments.config[0] as string);

expect(config).toHaveProperty('title', input.title);
expect(config).toHaveProperty('timeRange');
expect(config.timeRange).toHaveProperty('from', input.timeRange?.from);
expect(config.timeRange).toHaveProperty('to', input.timeRange?.to);
});

it('includes empty panel title', () => {
const input: EmbeddableInput = {
...baseEmbeddableInput,
title: '',
};

const expression = toExpression(input, 'visualization');
const ast = fromExpression(expression);

const config = decode(ast.chain[0].arguments.config[0] as string);

expect(config).toHaveProperty('title', input.title);
});
});

describe('by-value embeddable input', () => {
const baseEmbeddableInput = {
id: 'elementId',
disableTriggers: true,
filters: [],
};
it('converts to an embeddable expression', () => {
const input: EmbeddableInput = baseEmbeddableInput;

const expression = toExpression(input, 'visualization');
const ast = fromExpression(expression);

expect(ast.type).toBe('expression');
expect(ast.chain[0].function).toBe('embeddable');
expect(ast.chain[0].arguments.type[0]).toBe('visualization');

const config = decode(ast.chain[0].arguments.config[0] as string);
expect(config.filters).toStrictEqual(input.filters);
expect(config.disableTriggers).toStrictEqual(input.disableTriggers);
});

it('includes optional input values', () => {
const input: EmbeddableInput = {
...baseEmbeddableInput,
title: 'title',
timeRange: {
from: 'now-1h',
to: 'now',
},
};

const expression = toExpression(input, 'visualization');
const ast = fromExpression(expression);

const config = decode(ast.chain[0].arguments.config[0] as string);

expect(config).toHaveProperty('title', input.title);
expect(config).toHaveProperty('timeRange');
expect(config.timeRange).toHaveProperty('from', input.timeRange?.from);
expect(config.timeRange).toHaveProperty('to', input.timeRange?.to);
});

it('includes empty panel title', () => {
const input: EmbeddableInput = {
...baseEmbeddableInput,
title: '',
};

const expression = toExpression(input, 'visualization');
const ast = fromExpression(expression);

const config = decode(ast.chain[0].arguments.config[0] as string);

expect(config).toHaveProperty('title', input.title);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { fromExpression, Ast } from '@kbn/interpreter/common';
import { chartPluginMock } from 'src/plugins/charts/public/mocks';

const baseEmbeddableInput = {
id: 'embeddableId',
id: 'elementId',
savedObjectId: 'embeddableId',
filters: [],
};

Expand All @@ -27,7 +28,7 @@ describe('toExpression', () => {
expect(ast.type).toBe('expression');
expect(ast.chain[0].function).toBe('savedLens');

expect(ast.chain[0].arguments.id).toStrictEqual([input.id]);
expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]);

expect(ast.chain[0].arguments).not.toHaveProperty('title');
expect(ast.chain[0].arguments).not.toHaveProperty('timerange');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function toExpression(input: SavedLensInput, palettes: PaletteRegistry):

expressionParts.push('savedLens');

expressionParts.push(`id="${input.id}"`);
expressionParts.push(`id="${input.savedObjectId}"`);

if (input.title !== undefined) {
expressionParts.push(`title="${input.title}"`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
*/

import { toExpression } from './map';
import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable';
import { fromExpression, Ast } from '@kbn/interpreter/common';

const baseSavedMapInput = {
id: 'elementId',
attributes: { title: '' },
id: 'embeddableId',
savedObjectId: 'embeddableId',
filters: [],
isLayerTOCOpen: false,
refreshConfig: {
Expand All @@ -23,7 +23,7 @@ const baseSavedMapInput = {

describe('toExpression', () => {
it('converts to a savedMap expression', () => {
const input: MapEmbeddableInput = {
const input = {
...baseSavedMapInput,
};

Expand All @@ -33,15 +33,15 @@ describe('toExpression', () => {
expect(ast.type).toBe('expression');
expect(ast.chain[0].function).toBe('savedMap');

expect(ast.chain[0].arguments.id).toStrictEqual([input.id]);
expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]);

expect(ast.chain[0].arguments).not.toHaveProperty('title');
expect(ast.chain[0].arguments).not.toHaveProperty('center');
expect(ast.chain[0].arguments).not.toHaveProperty('timerange');
});

it('includes optional input values', () => {
const input: MapEmbeddableInput = {
const input = {
...baseSavedMapInput,
mapCenter: {
lat: 1,
Expand Down Expand Up @@ -73,7 +73,7 @@ describe('toExpression', () => {
});

it('includes empty panel title', () => {
const input: MapEmbeddableInput = {
const input = {
...baseSavedMapInput,
title: '',
};
Expand Down
Loading

0 comments on commit cfee5e1

Please sign in to comment.