-
Notifications
You must be signed in to change notification settings - Fork 8.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Canvas] Generic embeddable function #104499
Changes from 1 commit
180f47e
81a15dc
fd80ede
5163ffd
2b3fe3d
db4a95a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* 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 { embeddable } from './embeddable'; | ||
import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; | ||
import { ExpressionValueFilter } from '../../../types'; | ||
import { encode } from '../../../common/lib/embeddable_dataurl'; | ||
|
||
const filterContext: ExpressionValueFilter = { | ||
type: 'filter', | ||
and: [ | ||
{ | ||
type: 'filter', | ||
and: [], | ||
value: 'filter-value', | ||
column: 'filter-column', | ||
filterType: 'exactly', | ||
}, | ||
{ | ||
type: 'filter', | ||
and: [], | ||
column: 'time-column', | ||
filterType: 'time', | ||
from: '2019-06-04T04:00:00.000Z', | ||
to: '2019-06-05T04:00:00.000Z', | ||
}, | ||
], | ||
}; | ||
|
||
describe('embeddable', () => { | ||
const fn = embeddable().fn; | ||
const config = { | ||
id: 'some-id', | ||
timerange: { from: '15m', to: 'now' }, | ||
title: 'test embeddable', | ||
}; | ||
|
||
const args = { | ||
config: encode(config), | ||
type: 'visualization', | ||
}; | ||
|
||
it('accepts null context', () => { | ||
const expression = fn(null, args, {} as any); | ||
|
||
expect(expression.input.filters).toEqual([]); | ||
}); | ||
|
||
it('accepts filter context', () => { | ||
const expression = fn(filterContext, args, {} as any); | ||
const embeddableFilters = getQueryFilters(filterContext.and); | ||
|
||
expect(expression.input.filters).toEqual(embeddableFilters); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* 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 { 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 { getFunctionHelp } from '../../../i18n'; | ||
import { SavedObjectReference } from '../../../../../../src/core/types'; | ||
import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; | ||
import { decode } from '../../../common/lib/embeddable_dataurl'; | ||
|
||
interface Arguments { | ||
config: string; | ||
type: string; | ||
} | ||
|
||
const defaultTimeRange = { | ||
from: 'now-15m', | ||
to: 'now', | ||
}; | ||
|
||
export type EmbeddableInput = Input & { | ||
timeRange?: TimeRange; | ||
filters?: Filter[]; | ||
savedObjectId: string; | ||
}; | ||
|
||
const baseEmbeddableInput = { | ||
timeRange: defaultTimeRange, | ||
disableTriggers: true, | ||
renderMode: 'noInteractivity', | ||
}; | ||
|
||
type Return = EmbeddableExpression<EmbeddableInput>; | ||
|
||
export function embeddable(): ExpressionFunctionDefinition< | ||
'embeddable', | ||
ExpressionValueFilter | null, | ||
Arguments, | ||
Return | ||
> { | ||
const { help, args: argHelp } = getFunctionHelp().embeddable; | ||
|
||
return { | ||
name: 'embeddable', | ||
help, | ||
args: { | ||
config: { | ||
aliases: ['_'], | ||
types: ['string'], | ||
required: true, | ||
help: argHelp.config, | ||
}, | ||
type: { | ||
types: ['string'], | ||
required: true, | ||
help: argHelp.type, | ||
}, | ||
}, | ||
context: { | ||
types: ['filter'], | ||
}, | ||
type: EmbeddableExpressionType, | ||
fn: (input, args) => { | ||
const filters = input ? input.and : []; | ||
|
||
const embeddableInput = decode(args.config) as EmbeddableInput; | ||
|
||
return { | ||
type: EmbeddableExpressionType, | ||
input: { | ||
...baseEmbeddableInput, | ||
...embeddableInput, | ||
filters: getQueryFilters(filters), | ||
}, | ||
generatedAt: Date.now(), | ||
embeddableType: args.type, | ||
}; | ||
}, | ||
|
||
extract(state) { | ||
const refName = 'embeddable.id'; | ||
const refType = 'embeddable.embeddableType'; | ||
const references: SavedObjectReference[] = [ | ||
{ | ||
name: refName, | ||
type: refType, | ||
id: state.id[0] as string, | ||
}, | ||
]; | ||
return { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is a "by value" embeddable, then the state should be passed on to the EmbeddablePersistableStateService for extract. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll tackle this in a separate PR to address extracting/injecting references for by-value embeddables. |
||
state: { | ||
...state, | ||
id: [refName], | ||
}, | ||
references, | ||
}; | ||
}, | ||
|
||
inject(state, references) { | ||
const reference = references.find((ref) => ref.name === 'embeddable.id'); | ||
if (reference) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here should also be passed on to EmbeddablePersistableStateService for inject |
||
state.id[0] = reference.id; | ||
} | ||
return state; | ||
}, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* 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 { encode } from '../../../../common/lib/embeddable_dataurl'; | ||
import { EmbeddableInput } from '../../../expression_types'; | ||
|
||
export function toExpression(input: EmbeddableInput, embeddableType: string): string { | ||
const expressionParts = [] as string[]; | ||
|
||
expressionParts.push('embeddable'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since there's nothing conditional here, can you just build this as one big string instead of doing the parts+join. return `embeddable config="${encode(input)}" type="${embeddableType}"` |
||
|
||
expressionParts.push(`config="${encode(input)}"`); | ||
|
||
expressionParts.push(`type="${embeddableType}"`); | ||
|
||
return expressionParts.join(' '); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* | ||
* 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 { EmbeddableInput } from '../../canvas_plugin_src/expression_types'; | ||
|
||
export const encode = (input: Partial<EmbeddableInput>) => | ||
Buffer.from(JSON.stringify(input)).toString('base64'); | ||
export const decode = (serializedInput: string) => | ||
JSON.parse(Buffer.from(serializedInput, 'base64').toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting an internal server error when attempting to add an embeddable by-reference to a workpad:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah I've encountered that before. It's throwing the error from the extract function for extracting saved object references. I have a fix for it that went into a different PR. I'll add the fix to this PR too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 81a15dc.