[Lens] Register all expression functions to the server (#107836)

Part of:  #97134

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Alexey Antonov 2021-08-11 13:59:49 +03:00 committed by GitHub
parent 46e0f0ba3e
commit 82bec98cd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 272 additions and 129 deletions

View file

@ -8,6 +8,7 @@
import { i18n } from '@kbn/i18n';
import { cloneDeep } from 'lodash';
import type {
ExecutionContext,
DatatableColumnMeta,
ExpressionFunctionDefinition,
} from '../../../../../../src/plugins/expressions/common';
@ -46,15 +47,13 @@ function isRange(meta: { params?: { id?: string } } | undefined) {
return meta?.params?.id === 'range';
}
export const getDatatable = ({
formatFactory,
}: {
formatFactory: FormatFactory;
}): ExpressionFunctionDefinition<
export const getDatatable = (
getFormatFactory: (context: ExecutionContext) => FormatFactory | Promise<FormatFactory>
): ExpressionFunctionDefinition<
'lens_datatable',
LensMultiTable,
DatatableArgs,
DatatableRender
Promise<DatatableRender>
> => ({
name: 'lens_datatable',
type: 'render',
@ -87,12 +86,13 @@ export const getDatatable = ({
help: '',
},
},
fn(data, args, context) {
async fn(data, args, context) {
let untransposedData: LensMultiTable | undefined;
// do the sorting at this level to propagate it also at CSV download
const [firstTable] = Object.values(data.tables);
const [layerId] = Object.keys(context.inspectorAdapters.tables || {});
const formatters: Record<string, ReturnType<FormatFactory>> = {};
const formatFactory = await getFormatFactory(context);
firstTable.columns.forEach((column) => {
formatters[column.id] = formatFactory(column.meta?.params);

View file

@ -8,6 +8,7 @@
"data",
"charts",
"expressions",
"fieldFormats",
"inspector",
"navigation",
"urlForwarding",

View file

@ -141,6 +141,7 @@ export const LensTopNavMenu = ({
}: LensTopNavMenuProps) => {
const {
data,
fieldFormats,
navigation,
uiSettings,
application,
@ -255,7 +256,7 @@ export const LensTopNavMenu = ({
content: exporters.datatableToCSV(datatable, {
csvSeparator: uiSettings.get('csv:separator', ','),
quoteValues: uiSettings.get('csv:quoteValues', true),
formatFactory: data.fieldFormats.deserialize,
formatFactory: fieldFormats.deserialize,
escapeFormulaValues: false,
}),
type: exporters.CSV_MIME_TYPE,
@ -305,7 +306,7 @@ export const LensTopNavMenu = ({
activeData,
attributeService,
dashboardFeatureFlag.allowByValueEmbeddables,
data.fieldFormats.deserialize,
fieldFormats.deserialize,
getIsByValueMode,
initialInput,
isLinkedToOriginatingApp,

View file

@ -46,7 +46,14 @@ export async function getLensServices(
startDependencies: LensPluginStartDependencies,
attributeService: () => Promise<LensAttributeService>
): Promise<LensAppServices> {
const { data, navigation, embeddable, savedObjectsTagging, usageCollection } = startDependencies;
const {
data,
navigation,
embeddable,
savedObjectsTagging,
usageCollection,
fieldFormats,
} = startDependencies;
const storage = new Storage(localStorage);
const stateTransfer = embeddable?.getStateTransfer();
@ -56,6 +63,7 @@ export async function getLensServices(
data,
storage,
navigation,
fieldFormats,
stateTransfer,
usageCollection,
savedObjectsTagging,

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { History } from 'history';
import { OnSaveProps } from 'src/plugins/saved_objects/public';
import {
import type { History } from 'history';
import type { OnSaveProps } from 'src/plugins/saved_objects/public';
import type {
ApplicationStart,
AppMountParameters,
ChromeStart,
@ -17,25 +17,27 @@ import {
OverlayStart,
SavedObjectsStart,
} from '../../../../../src/core/public';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import { UsageCollectionStart } from '../../../../../src/plugins/usage_collection/public';
import { DashboardStart } from '../../../../../src/plugins/dashboard/public';
import { LensEmbeddableInput } from '../embeddable/embeddable';
import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public';
import { LensAttributeService } from '../lens_attribute_service';
import { IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public';
import { DashboardFeatureFlagConfig } from '../../../../../src/plugins/dashboard/public';
import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import type { UsageCollectionStart } from '../../../../../src/plugins/usage_collection/public';
import type { DashboardStart } from '../../../../../src/plugins/dashboard/public';
import type { LensEmbeddableInput } from '../embeddable/embeddable';
import type { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public';
import type { LensAttributeService } from '../lens_attribute_service';
import type { IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public';
import type { DashboardFeatureFlagConfig } from '../../../../../src/plugins/dashboard/public';
import type { SavedObjectTaggingPluginStart } from '../../../saved_objects_tagging/public';
import {
VisualizeFieldContext,
ACTION_VISUALIZE_LENS_FIELD,
} from '../../../../../src/plugins/ui_actions/public';
import {
import type {
EmbeddableEditorState,
EmbeddableStateTransfer,
} from '../../../../../src/plugins/embeddable/public';
import { DatasourceMap, EditorFrameInstance, VisualizationMap } from '../types';
import { PresentationUtilPluginStart } from '../../../../../src/plugins/presentation_util/public';
import type { DatasourceMap, EditorFrameInstance, VisualizationMap } from '../types';
import type { PresentationUtilPluginStart } from '../../../../../src/plugins/presentation_util/public';
import type { FieldFormatsStart } from '../../../../../src/plugins/field_formats/public';
export interface RedirectToOriginProps {
input?: LensEmbeddableInput;
isCopied?: boolean;
@ -97,6 +99,7 @@ export interface LensAppServices {
overlays: OverlayStart;
storage: IStorageWrapper;
dashboard: DashboardStart;
fieldFormats: FieldFormatsStart;
data: DataPublicPluginStart;
uiSettings: IUiSettingsClient;
application: ApplicationStart;

View file

@ -5,10 +5,10 @@
* 2.0.
*/
import { DatatableProps } from '../../common/expressions';
import type { DatatableProps } from '../../common/expressions';
import type { LensMultiTable } from '../../common';
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
import type { IFieldFormat } from '../../../../../src/plugins/field_formats/common';
import type { FormatFactory } from '../../common';
import { getDatatable } from './expression';
function sampleArgs() {
@ -83,9 +83,9 @@ function sampleArgs() {
describe('datatable_expression', () => {
describe('datatable renders', () => {
test('it renders with the specified data and args', () => {
test('it renders with the specified data and args', async () => {
const { data, args } = sampleArgs();
const result = getDatatable({ formatFactory: (x) => x as IFieldFormat }).fn(
const result = await getDatatable(() => Promise.resolve((() => {}) as FormatFactory)).fn(
data,
args,
createMockExecutionContext()

View file

@ -17,7 +17,7 @@ interface DatatableVisualizationPluginStartPlugins {
}
export interface DatatableVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
editorFrame: EditorFrameSetup;
charts: ChartsPluginSetup;
}
@ -37,13 +37,12 @@ export class DatatableVisualization {
getDatatableVisualization,
} = await import('../async_services');
const palettes = await charts.palettes.getPalettes();
const resolvedFormatFactory = await formatFactory;
expressions.registerFunction(() => datatableColumn);
expressions.registerFunction(() => getDatatable({ formatFactory: resolvedFormatFactory }));
expressions.registerFunction(() => getDatatable(() => formatFactory));
expressions.registerRenderer(() =>
getDatatableRenderer({
formatFactory: resolvedFormatFactory,
formatFactory,
getType: core
.getStartServices()
.then(([_, { data: dataStart }]) => dataStart.search.aggs.types.get),

View file

@ -20,7 +20,7 @@ import type { HeatmapExpressionProps } from './types';
export { heatmapGridConfig, heatmapLegendConfig, heatmap } from '../../common/expressions';
export const getHeatmapRenderer = (dependencies: {
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
chartsThemeService: ChartsPluginSetup['theme'];
paletteService: PaletteRegistry;
timeZone: string;
@ -37,7 +37,6 @@ export const getHeatmapRenderer = (dependencies: {
config: HeatmapExpressionProps,
handlers: IInterpreterRenderHandlers
) => {
const formatFactory = await dependencies.formatFactory;
const onClickValue = (data: LensFilterEvent['data']) => {
handlers.event({ name: 'filter', data });
};
@ -53,7 +52,7 @@ export const getHeatmapRenderer = (dependencies: {
onClickValue={onClickValue}
onSelectRange={onSelectRange}
timeZone={dependencies.timeZone}
formatFactory={formatFactory}
formatFactory={dependencies.formatFactory}
chartsThemeService={dependencies.chartsThemeService}
paletteService={dependencies.paletteService}
/>

View file

@ -14,7 +14,7 @@ import type { FormatFactory } from '../../common';
export interface HeatmapVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
editorFrame: EditorFrameSetup;
charts: ChartsPluginSetup;
}

View file

@ -20,6 +20,7 @@ import { ChangeIndexPattern } from './change_indexpattern';
import { EuiProgress, EuiLoadingSpinner } from '@elastic/eui';
import { documentField } from './document_field';
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
import { indexPatternFieldEditorPluginMock } from '../../../../../src/plugins/index_pattern_field_editor/public/mocks';
import { getFieldByNameFactory } from './pure_helpers';
import { uiActionsPluginMock } from '../../../../../src/plugins/ui_actions/public/mocks';
@ -251,6 +252,7 @@ describe('IndexPattern Data Panel', () => {
indexPatternRefs: [],
existingFields: {},
data: dataPluginMock.createStartContract(),
fieldFormats: fieldFormatsServiceMock.createStartContract(),
indexPatternFieldEditor: indexPatternFieldEditorPluginMock.createStartContract(),
onUpdateIndexPattern: jest.fn(),
dragDropContext: createMockedDragDropContext(),

View file

@ -23,14 +23,15 @@ import {
EuiButtonIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EsQueryConfig, Query, Filter } from '@kbn/es-query';
import type { EsQueryConfig, Query, Filter } from '@kbn/es-query';
import { FormattedMessage } from '@kbn/i18n/react';
import { CoreStart } from 'kibana/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import type { CoreStart } from 'kibana/public';
import type { DataPublicPluginStart } from 'src/plugins/data/public';
import type { FieldFormatsStart } from 'src/plugins/field_formats/public';
import { htmlIdGenerator } from '@elastic/eui';
import { DatasourceDataPanelProps, DataType, StateSetter } from '../types';
import type { DatasourceDataPanelProps, DataType, StateSetter } from '../types';
import { ChildDragDropProvider, DragContextState } from '../drag_drop';
import {
import type {
IndexPattern,
IndexPatternPrivateState,
IndexPatternField,
@ -46,6 +47,7 @@ import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../../src/plugins/ui_actio
export type Props = Omit<DatasourceDataPanelProps<IndexPatternPrivateState>, 'core'> & {
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
changeIndexPattern: (
id: string,
state: IndexPatternPrivateState,
@ -118,6 +120,7 @@ export function IndexPatternDataPanel({
dragDropContext,
core,
data,
fieldFormats,
query,
filters,
dateRange,
@ -231,6 +234,7 @@ export function IndexPatternDataPanel({
dragDropContext={dragDropContext}
core={core}
data={data}
fieldFormats={fieldFormats}
charts={charts}
indexPatternFieldEditor={indexPatternFieldEditor}
onChangeIndexPattern={onChangeIndexPattern}
@ -289,6 +293,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
onUpdateIndexPattern,
core,
data,
fieldFormats,
indexPatternFieldEditor,
existingFields,
charts,
@ -297,6 +302,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
uiActions,
}: Omit<DatasourceDataPanelProps, 'state' | 'setState' | 'showNoDataPopover' | 'core'> & {
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
core: CoreStart;
currentIndexPatternId: string;
indexPatternRefs: IndexPatternRef[];
@ -565,6 +571,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
() => ({
core,
data,
fieldFormats,
indexPattern: currentIndexPattern,
highlight: localState.nameFilter.toLowerCase(),
dateRange,
@ -575,6 +582,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
[
core,
data,
fieldFormats,
currentIndexPattern,
dateRange,
query,

View file

@ -12,12 +12,12 @@ import { EuiLoadingSpinner, EuiPopover } from '@elastic/eui';
import { InnerFieldItem, FieldItemProps } from './field_item';
import { coreMock } from 'src/core/public/mocks';
import { mountWithIntl } from '@kbn/test/jest';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
import { IndexPattern } from './types';
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
import { documentField } from './document_field';
import { uiActionsPluginMock } from '../../../../../src/plugins/ui_actions/public/mocks';
import { FieldFormatsStart } from '../../../../../src/plugins/field_formats/public';
const chartsThemeService = chartPluginMock.createSetupContract().theme;
@ -29,7 +29,6 @@ describe('IndexPattern Field Item', () => {
let defaultProps: FieldItemProps;
let indexPattern: IndexPattern;
let core: ReturnType<typeof coreMock['createSetup']>;
let data: DataPublicPluginStart;
beforeEach(() => {
indexPattern = {
@ -84,11 +83,15 @@ describe('IndexPattern Field Item', () => {
} as IndexPattern;
core = coreMock.createSetup();
data = dataPluginMock.createStartContract();
core.http.post.mockClear();
defaultProps = {
indexPattern,
data,
fieldFormats: ({
...fieldFormatsServiceMock.createStartContract(),
getDefaultInstance: jest.fn(() => ({
convert: jest.fn((s: unknown) => JSON.stringify(s)),
})),
} as unknown) as FieldFormatsStart,
core,
highlight: '',
dateRange: {
@ -112,12 +115,6 @@ describe('IndexPattern Field Item', () => {
hasSuggestionForField: () => false,
uiActions: uiActionsPluginMock.createStartContract(),
};
data.fieldFormats = ({
getDefaultInstance: jest.fn(() => ({
convert: jest.fn((s: unknown) => JSON.stringify(s)),
})),
} as unknown) as DataPublicPluginStart['fieldFormats'];
});
it('should display displayName of a field', () => {

View file

@ -36,7 +36,7 @@ import {
TooltipType,
} from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import type { FieldFormatsStart } from 'src/plugins/field_formats/public';
import { EuiHighlight } from '@elastic/eui';
import {
Query,
@ -61,7 +61,7 @@ import { debouncedComponent } from '../debounced_component';
export interface FieldItemProps {
core: DatasourceDataPanelProps['core'];
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
field: IndexPatternField;
indexPattern: IndexPattern;
highlight?: string;
@ -395,7 +395,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
core,
sampledValues,
chartsThemeService,
data: { fieldFormats },
fieldFormats,
dropOntoWorkspace,
editField,
removeField,

View file

@ -9,8 +9,7 @@ import React from 'react';
import { EuiLoadingSpinner, EuiNotificationBadge } from '@elastic/eui';
import { coreMock } from 'src/core/public/mocks';
import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
import { IndexPattern } from './types';
import { FieldItem } from './field_item';
import { FieldsAccordion, FieldsAccordionProps, FieldItemSharedProps } from './fields_accordion';
@ -21,7 +20,6 @@ describe('Fields Accordion', () => {
let defaultProps: FieldsAccordionProps;
let indexPattern: IndexPattern;
let core: ReturnType<typeof coreMock['createSetup']>;
let data: DataPublicPluginStart;
let fieldProps: FieldItemSharedProps;
beforeEach(() => {
@ -45,12 +43,11 @@ describe('Fields Accordion', () => {
],
} as IndexPattern;
core = coreMock.createSetup();
data = dataPluginMock.createStartContract();
core.http.post.mockClear();
fieldProps = {
indexPattern,
data,
fieldFormats: fieldFormatsServiceMock.createStartContract(),
core,
highlight: '',
dateRange: {

View file

@ -17,7 +17,7 @@ import {
EuiIconTip,
} from '@elastic/eui';
import classNames from 'classnames';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { FieldFormatsStart } from 'src/plugins/field_formats/public';
import { IndexPatternField } from './types';
import { FieldItem } from './field_item';
import { Query, Filter } from '../../../../../src/plugins/data/public';
@ -28,7 +28,7 @@ import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
export interface FieldItemSharedProps {
core: DatasourceDataPanelProps['core'];
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
chartsThemeService: ChartsPluginSetup['theme'];
indexPattern: IndexPattern;
highlight?: string;

View file

@ -5,20 +5,25 @@
* 2.0.
*/
import { CoreSetup } from 'kibana/public';
import type { CoreSetup } from 'kibana/public';
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
import { IndexPatternFieldEditorStart } from '../../../../../src/plugins/index_pattern_field_editor/public';
import {
import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
import type { IndexPatternFieldEditorStart } from '../../../../../src/plugins/index_pattern_field_editor/public';
import type {
DataPublicPluginSetup,
DataPublicPluginStart,
} from '../../../../../src/plugins/data/public';
import { Datasource, EditorFrameSetup } from '../types';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import type { Datasource, EditorFrameSetup } from '../types';
import type { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import type {
FieldFormatsStart,
FieldFormatsSetup,
} from '../../../../../src/plugins/field_formats/public';
export interface IndexPatternDatasourceSetupPlugins {
expressions: ExpressionsSetup;
fieldFormats: FieldFormatsSetup;
data: DataPublicPluginSetup;
editorFrame: EditorFrameSetup;
charts: ChartsPluginSetup;
@ -26,6 +31,7 @@ export interface IndexPatternDatasourceSetupPlugins {
export interface IndexPatternDatasourceStartPlugins {
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
uiActions: UiActionsStart;
}
@ -35,7 +41,12 @@ export class IndexPatternDatasource {
setup(
core: CoreSetup<IndexPatternDatasourceStartPlugins>,
{ expressions, editorFrame, charts, data: dataSetup }: IndexPatternDatasourceSetupPlugins
{
fieldFormats: fieldFormatsSetup,
expressions,
editorFrame,
charts,
}: IndexPatternDatasourceSetupPlugins
) {
editorFrame.registerDatasource(async () => {
const {
@ -48,10 +59,11 @@ export class IndexPatternDatasource {
} = await import('../async_services');
return core
.getStartServices()
.then(([coreStart, { indexPatternFieldEditor, uiActions, data }]) => {
const suffixFormatter = getSuffixFormatter(data.fieldFormats.deserialize);
if (!dataSetup.fieldFormats.has(suffixFormatter.id)) {
dataSetup.fieldFormats.register([suffixFormatter]);
.then(([coreStart, { indexPatternFieldEditor, uiActions, data, fieldFormats }]) => {
const suffixFormatter = getSuffixFormatter(fieldFormats.deserialize);
if (!fieldFormats.has(suffixFormatter.id)) {
// todo: this code should be executed on setup phase.
fieldFormatsSetup.register([suffixFormatter]);
}
expressions.registerFunction(timeScale);
expressions.registerFunction(counterRate);
@ -59,6 +71,7 @@ export class IndexPatternDatasource {
expressions.registerFunction(formatColumn);
return getIndexPatternDatasource({
core: coreStart,
fieldFormats,
storage: new Storage(localStorage),
data,
charts,

View file

@ -20,6 +20,8 @@ import { operationDefinitionMap, getErrorMessages } from './operations';
import { createMockedFullReference } from './operations/mocks';
import { indexPatternFieldEditorPluginMock } from 'src/plugins/index_pattern_field_editor/public/mocks';
import { uiActionsPluginMock } from '../../../../../src/plugins/ui_actions/public/mocks';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
jest.mock('./loader');
jest.mock('../id_generator');
jest.mock('./operations');
@ -172,6 +174,7 @@ describe('IndexPattern Data Source', () => {
storage: {} as IStorageWrapper,
core: coreMock.createStart(),
data: dataPluginMock.createStartContract(),
fieldFormats: fieldFormatsServiceMock.createStartContract(),
charts: chartPluginMock.createSetupContract(),
indexPatternFieldEditor: indexPatternFieldEditorPluginMock.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),

View file

@ -8,11 +8,12 @@
import React from 'react';
import { render } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { CoreStart, SavedObjectReference } from 'kibana/public';
import type { CoreStart, SavedObjectReference } from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { IndexPatternFieldEditorStart } from '../../../../../src/plugins/index_pattern_field_editor/public';
import {
import type { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import type { FieldFormatsStart } from 'src/plugins/field_formats/public';
import type { IndexPatternFieldEditorStart } from '../../../../../src/plugins/index_pattern_field_editor/public';
import type {
DatasourceDimensionEditorProps,
DatasourceDimensionTriggerProps,
DatasourceDataPanelProps,
@ -83,6 +84,7 @@ export function getIndexPatternDatasource({
core,
storage,
data,
fieldFormats,
charts,
indexPatternFieldEditor,
uiActions,
@ -90,6 +92,7 @@ export function getIndexPatternDatasource({
core: CoreStart;
storage: IStorageWrapper;
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
charts: ChartsPluginSetup;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
uiActions: UiActionsStart;
@ -202,6 +205,7 @@ export function getIndexPatternDatasource({
<IndexPatternDataPanel
changeIndexPattern={handleChangeIndexPattern}
data={data}
fieldFormats={fieldFormats}
charts={charts}
indexPatternFieldEditor={indexPatternFieldEditor}
{...props}
@ -259,6 +263,7 @@ export function getIndexPatternDatasource({
storage,
uiSettings,
data,
fieldFormats,
savedObjects: core.savedObjects,
docLinks: core.docLinks,
}}
@ -284,6 +289,7 @@ export function getIndexPatternDatasource({
storage,
uiSettings,
data,
fieldFormats,
savedObjects: core.savedObjects,
docLinks: core.docLinks,
http: core.http,
@ -365,7 +371,7 @@ export function getIndexPatternDatasource({
// Reset the temporary invalid state when closing the editor, but don't
// update the state if it's not needed
updateStateOnCloseDimension: ({ state, layerId, columnId }) => {
updateStateOnCloseDimension: ({ state, layerId }) => {
const layer = state.layers[layerId];
if (!Object.values(layer.incompleteColumns || {}).length) {
return;
@ -408,7 +414,7 @@ export function getIndexPatternDatasource({
getDatasourceSuggestionsFromCurrentState,
getDatasourceSuggestionsForVisualizeField,
getErrorMessages(state, layersGroups) {
getErrorMessages(state) {
if (!state) {
return;
}

View file

@ -24,22 +24,17 @@ export { metricChart } from '../../common/expressions';
export type { MetricState, MetricConfig } from '../../common/expressions';
export const getMetricChartRenderer = (
formatFactory: Promise<FormatFactory>
formatFactory: FormatFactory
): ExpressionRenderDefinition<MetricChartProps> => ({
name: 'lens_metric_chart_renderer',
displayName: 'Metric chart',
help: 'Metric chart renderer',
validate: () => undefined,
reuseDomNode: true,
render: async (
domNode: Element,
config: MetricChartProps,
handlers: IInterpreterRenderHandlers
) => {
const resolvedFormatFactory = await formatFactory;
render: (domNode: Element, config: MetricChartProps, handlers: IInterpreterRenderHandlers) => {
ReactDOM.render(
<I18nProvider>
<MetricChart {...config} formatFactory={resolvedFormatFactory} />
<MetricChart {...config} formatFactory={formatFactory} />
</I18nProvider>,
domNode,
() => {

View file

@ -12,7 +12,7 @@ import type { FormatFactory } from '../../common';
export interface MetricVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
editorFrame: EditorFrameSetup;
}

View file

@ -20,11 +20,11 @@ import { DeepPartial } from '@reduxjs/toolkit';
import { LensPublicStart } from '.';
import { visualizationTypes } from './xy_visualization/types';
import { navigationPluginMock } from '../../../../src/plugins/navigation/public/mocks';
import { LensAppServices } from './app_plugin/types';
import type { LensAppServices } from './app_plugin/types';
import { DOC_TYPE } from '../common';
import { DataPublicPluginStart, esFilters, UI_SETTINGS } from '../../../../src/plugins/data/public';
import { dashboardPluginMock } from '../../../../src/plugins/dashboard/public/mocks';
import {
import type {
LensByValueInput,
LensSavedObjectAttributes,
LensByReferenceInput,
@ -33,8 +33,9 @@ import {
mockAttributeService,
createEmbeddableStateTransferMock,
} from '../../../../src/plugins/embeddable/public/mocks';
import { LensAttributeService } from './lens_attribute_service';
import { EmbeddableStateTransfer } from '../../../../src/plugins/embeddable/public';
import { fieldFormatsServiceMock } from '../../../../src/plugins/field_formats/public/mocks';
import type { LensAttributeService } from './lens_attribute_service';
import type { EmbeddableStateTransfer } from '../../../../src/plugins/embeddable/public';
import { makeConfigureStore, LensAppState, LensState } from './state_management/index';
import { getResolvedDateRange } from './utils';
@ -391,6 +392,7 @@ export function makeDefaultServices(
getUrlForApp: jest.fn((appId: string) => `/testbasepath/app/${appId}#/`),
},
data: mockDataPlugin(sessionIdSubject),
fieldFormats: fieldFormatsServiceMock.createStartContract(),
storage: {
get: jest.fn(),
set: jest.fn(),

View file

@ -22,7 +22,7 @@ import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plug
export { pie } from '../../common/expressions';
export const getPieRenderer = (dependencies: {
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
chartsThemeService: ChartsPluginSetup['theme'];
paletteService: PaletteRegistry;
}): ExpressionRenderDefinition<PieExpressionProps> => ({
@ -33,20 +33,16 @@ export const getPieRenderer = (dependencies: {
help: '',
validate: () => undefined,
reuseDomNode: true,
render: async (
domNode: Element,
config: PieExpressionProps,
handlers: IInterpreterRenderHandlers
) => {
render: (domNode: Element, config: PieExpressionProps, handlers: IInterpreterRenderHandlers) => {
const onClickValue = (data: LensFilterEvent['data']) => {
handlers.event({ name: 'filter', data });
};
const formatFactory = await dependencies.formatFactory;
ReactDOM.render(
<I18nProvider>
<MemoizedChart
{...config}
formatFactory={formatFactory}
formatFactory={dependencies.formatFactory}
chartsThemeService={dependencies.chartsThemeService}
paletteService={dependencies.paletteService}
onClickValue={onClickValue}

View file

@ -15,7 +15,7 @@ import type { FormatFactory } from '../../common';
export interface PieVisualizationPluginSetupPlugins {
editorFrame: EditorFrameSetup;
expressions: ExpressionsSetup;
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
charts: ChartsPluginSetup;
}

View file

@ -7,6 +7,7 @@
import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public';
import type { Start as InspectorStartContract } from 'src/plugins/inspector/public';
import type { FieldFormatsSetup, FieldFormatsStart } from 'src/plugins/field_formats/public';
import { UsageCollectionSetup, UsageCollectionStart } from 'src/plugins/usage_collection/public';
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
import { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public';
@ -57,7 +58,7 @@ import {
ACTION_VISUALIZE_FIELD,
VISUALIZE_FIELD_TRIGGER,
} from '../../../../src/plugins/ui_actions/public';
import { APP_ID, getEditPath, NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common';
import { APP_ID, FormatFactory, getEditPath, NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common';
import type { EditorFrameStart, VisualizationType } from './types';
import { getLensAliasConfig } from './vis_type_alias';
import { visualizeFieldAction } from './trigger_actions/visualize_field_actions';
@ -77,6 +78,7 @@ export interface LensPluginSetupDependencies {
urlForwarding: UrlForwardingSetup;
expressions: ExpressionsSetup;
data: DataPublicPluginSetup;
fieldFormats: FieldFormatsSetup;
embeddable?: EmbeddableSetup;
visualizations: VisualizationsSetup;
charts: ChartsPluginSetup;
@ -86,6 +88,7 @@ export interface LensPluginSetupDependencies {
export interface LensPluginStartDependencies {
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
expressions: ExpressionsStart;
navigation: NavigationPublicPluginStart;
uiActions: UiActionsStart;
@ -158,6 +161,7 @@ export class LensPlugin {
urlForwarding,
expressions,
data,
fieldFormats,
embeddable,
visualizations,
charts,
@ -170,9 +174,21 @@ export class LensPlugin {
const [coreStart, startDependencies] = await core.getStartServices();
return getLensAttributeService(coreStart, startDependencies);
};
const getStartServices = async (): Promise<LensEmbeddableStartServices> => {
const [coreStart, deps] = await core.getStartServices();
this.initParts(core, data, embeddable, charts, expressions, usageCollection);
this.initParts(
core,
data,
embeddable,
charts,
expressions,
usageCollection,
fieldFormats,
deps.fieldFormats.deserialize
);
return {
attributeService: await this.attributeService!(),
capabilities: coreStart.application.capabilities,
@ -210,7 +226,19 @@ export class LensPlugin {
title: NOT_INTERNATIONALIZED_PRODUCT_NAME,
navLinkStatus: AppNavLinkStatus.hidden,
mount: async (params: AppMountParameters) => {
await this.initParts(core, data, embeddable, charts, expressions, usageCollection);
const [, deps] = await core.getStartServices();
await this.initParts(
core,
data,
embeddable,
charts,
expressions,
usageCollection,
fieldFormats,
deps.fieldFormats.deserialize
);
const { mountApp, stopReportManager } = await import('./async_services');
this.stopReportManager = stopReportManager;
await ensureDefaultIndexPattern();
@ -245,7 +273,9 @@ export class LensPlugin {
embeddable: EmbeddableSetup | undefined,
charts: ChartsPluginSetup,
expressions: ExpressionsServiceSetup,
usageCollection: UsageCollectionSetup | undefined
usageCollection: UsageCollectionSetup | undefined,
fieldFormats: FieldFormatsSetup,
formatFactory: FormatFactory
) {
const {
DatatableVisualization,
@ -277,11 +307,10 @@ export class LensPlugin {
PieVisualizationPluginSetupPlugins = {
expressions,
data,
fieldFormats,
charts,
editorFrame: editorFrameSetupInterface,
formatFactory: core
.getStartServices()
.then(([_, { data: dataStart }]) => dataStart.fieldFormats.deserialize),
formatFactory,
};
this.indexpatternDatasource.setup(core, dependencies);
this.xyVisualization.setup(core, dependencies);

View file

@ -123,7 +123,7 @@ export function calculateMinInterval({ args: { layers }, data }: XYChartProps) {
}
export const getXyChartRenderer = (dependencies: {
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
chartsThemeService: ChartsPluginStart['theme'];
chartsActiveCursorService: ChartsPluginStart['activeCursor'];
paletteService: PaletteRegistry;
@ -148,12 +148,12 @@ export const getXyChartRenderer = (dependencies: {
const onSelectRange = (data: LensBrushEvent['data']) => {
handlers.event({ name: 'brush', data });
};
const formatFactory = await dependencies.formatFactory;
ReactDOM.render(
<I18nProvider>
<XYChartReportable
{...config}
formatFactory={formatFactory}
formatFactory={dependencies.formatFactory}
chartsActiveCursorService={dependencies.chartsActiveCursorService}
chartsThemeService={dependencies.chartsThemeService}
paletteService={dependencies.paletteService}

View file

@ -15,7 +15,7 @@ import type { FormatFactory } from '../../common';
export interface XyVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
formatFactory: Promise<FormatFactory>;
formatFactory: FormatFactory;
editorFrame: EditorFrameSetup;
charts: ChartsPluginSetup;
}
@ -41,7 +41,7 @@ export class XyVisualization {
getXyChartRenderer,
getXyVisualization,
} = await import('../async_services');
const [, { data, charts }] = await core.getStartServices();
const [, { charts, fieldFormats }] = await core.getStartServices();
const palettes = await charts.palettes.getPalettes();
expressions.registerFunction(() => legendConfig);
expressions.registerFunction(() => yAxisConfig);
@ -62,7 +62,7 @@ export class XyVisualization {
timeZone: getTimeZone(core.uiSettings),
})
);
return getXyVisualization({ paletteService: palettes, data });
return getXyVisualization({ paletteService: palettes, fieldFormats });
});
}
}

View file

@ -11,12 +11,12 @@ import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'
import { getXyVisualization } from './xy_visualization';
import { Operation } from '../types';
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
describe('#toExpression', () => {
const xyVisualization = getXyVisualization({
paletteService: chartPluginMock.createPaletteRegistry(),
data: dataPluginMock.createStartContract(),
fieldFormats: fieldFormatsServiceMock.createStartContract(),
});
let mockDatasource: ReturnType<typeof createMockDatasource>;
let frame: ReturnType<typeof createMockFramePublicAPI>;

View file

@ -13,7 +13,7 @@ import type { SeriesType, XYLayerConfig } from '../../common/expressions';
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
import { LensIconChartBar } from '../assets/chart_bar';
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
function exampleState(): State {
return {
@ -32,11 +32,11 @@ function exampleState(): State {
};
}
const paletteServiceMock = chartPluginMock.createPaletteRegistry();
const dataMock = dataPluginMock.createStartContract();
const fieldFormatsMock = fieldFormatsServiceMock.createStartContract();
const xyVisualization = getXyVisualization({
paletteService: paletteServiceMock,
data: dataMock,
fieldFormats: fieldFormatsMock,
});
describe('xy_visualization', () => {

View file

@ -12,7 +12,7 @@ import { Position } from '@elastic/charts';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { PaletteRegistry } from 'src/plugins/charts/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { FieldFormatsStart } from 'src/plugins/field_formats/public';
import { getSuggestions } from './xy_suggestions';
import { LayerContextMenu, XyToolbar, DimensionEditor } from './xy_config_panel';
import type {
@ -87,10 +87,10 @@ function getDescription(state?: State) {
export const getXyVisualization = ({
paletteService,
data,
fieldFormats,
}: {
paletteService: PaletteRegistry;
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
}): Visualization<State> => ({
id: 'lnsXY',
@ -190,7 +190,7 @@ export const getXyVisualization = ({
const colorAssignments = getColorAssignments(
state.layers,
{ tables: frame.activeData },
data.fieldFormats.deserialize
fieldFormats.deserialize
);
mappedAccessors = getAccessorColorConfig(
colorAssignments,
@ -336,7 +336,7 @@ export const getXyVisualization = ({
<I18nProvider>
<DimensionEditor
{...props}
formatFactory={data.fieldFormats.deserialize}
formatFactory={fieldFormats.deserialize}
paletteService={paletteService}
/>
</I18nProvider>,

View file

@ -6,19 +6,19 @@
*/
import { getSuggestions } from './xy_suggestions';
import { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } from '../types';
import type { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } from '../types';
import { State, XYState, visualizationTypes } from './types';
import { generateId } from '../id_generator';
import { getXyVisualization } from './xy_visualization';
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
import { PaletteOutput } from 'src/plugins/charts/public';
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
import type { PaletteOutput } from 'src/plugins/charts/public';
jest.mock('../id_generator');
const xyVisualization = getXyVisualization({
paletteService: chartPluginMock.createPaletteRegistry(),
data: dataPluginMock.createStartContract(),
fieldFormats: fieldFormatsServiceMock.createStartContract(),
});
describe('xy_suggestions', () => {

View file

@ -0,0 +1,71 @@
/*
* 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 type { CoreSetup, CoreStart, KibanaRequest } from 'kibana/server';
import {
pie,
xyChart,
timeScale,
counterRate,
metricChart,
yAxisConfig,
layerConfig,
formatColumn,
legendConfig,
renameColumns,
gridlinesConfig,
datatableColumn,
tickLabelsConfig,
axisTitlesVisibilityConfig,
getDatatable,
} from '../../common/expressions';
import type { PluginStartContract } from '../plugin';
import type {
ExecutionContext,
ExpressionsServerSetup,
} from '../../../../../src/plugins/expressions/server';
const getUiSettings = (coreStart: CoreStart, kibanaRequest: KibanaRequest) =>
coreStart.uiSettings.asScopedToClient(coreStart.savedObjects.getScopedClient(kibanaRequest));
export const setupExpressions = (
core: CoreSetup<PluginStartContract>,
expressions: ExpressionsServerSetup
) => {
const getFormatFactory = async (context: ExecutionContext) => {
const [coreStart, { fieldFormats: fieldFormatsStart }] = await core.getStartServices();
const kibanaRequest = context.getKibanaRequest?.();
if (!kibanaRequest) {
throw new Error('"lens_datatable" expression function requires a KibanaRequest to execute');
}
const fieldFormats = await fieldFormatsStart.fieldFormatServiceFactory(
getUiSettings(coreStart, kibanaRequest)
);
return fieldFormats.deserialize;
};
[
pie,
xyChart,
timeScale,
counterRate,
metricChart,
yAxisConfig,
layerConfig,
formatColumn,
legendConfig,
renameColumns,
gridlinesConfig,
datatableColumn,
tickLabelsConfig,
axisTitlesVisibilityConfig,
getDatatable(getFormatFactory),
].forEach((expressionFn) => expressions.registerFunction(expressionFn));
};

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { setupExpressions } from './expressions';

View file

@ -10,6 +10,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { Observable } from 'rxjs';
import { PluginStart as DataPluginStart } from 'src/plugins/data/server';
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
import { FieldFormatsStart } from 'src/plugins/field_formats/server';
import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server';
import { setupRoutes } from './routes';
import {
@ -20,6 +21,7 @@ import {
import { setupSavedObjects } from './saved_objects';
import { EmbeddableSetup } from '../../../../src/plugins/embeddable/server';
import { lensEmbeddableFactory } from './embeddable/lens_embeddable_factory';
import { setupExpressions } from './expressions';
export interface PluginSetupContract {
usageCollection?: UsageCollectionSetup;
@ -30,6 +32,7 @@ export interface PluginSetupContract {
export interface PluginStartContract {
taskManager?: TaskManagerStartContract;
fieldFormats: FieldFormatsStart;
data: DataPluginStart;
}
@ -44,6 +47,8 @@ export class LensServerPlugin implements Plugin<{}, {}, {}, {}> {
setup(core: CoreSetup<PluginStartContract>, plugins: PluginSetupContract) {
setupSavedObjects(core);
setupRoutes(core, this.initializerContext.logger.get());
setupExpressions(core, plugins.expressions);
if (plugins.usageCollection && plugins.taskManager) {
registerLensUsageCollector(
plugins.usageCollection,