mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
349 lines
9.4 KiB
TypeScript
349 lines
9.4 KiB
TypeScript
/*
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
* or more contributor license agreements. Licensed under the Elastic License;
|
|
* you may not use this file except in compliance with the Elastic License.
|
|
*/
|
|
|
|
import _ from 'lodash';
|
|
import React from 'react';
|
|
import { render } from 'react-dom';
|
|
import { I18nProvider } from '@kbn/i18n/react';
|
|
import { CoreStart, SavedObjectReference } from 'kibana/public';
|
|
import { i18n } from '@kbn/i18n';
|
|
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
|
|
import {
|
|
DatasourceDimensionEditorProps,
|
|
DatasourceDimensionTriggerProps,
|
|
DatasourceDataPanelProps,
|
|
Operation,
|
|
DatasourceLayerPanelProps,
|
|
PublicAPIProps,
|
|
} from '../types';
|
|
import {
|
|
loadInitialState,
|
|
changeIndexPattern,
|
|
changeLayerIndexPattern,
|
|
extractReferences,
|
|
} from './loader';
|
|
import { toExpression } from './to_expression';
|
|
import {
|
|
IndexPatternDimensionTrigger,
|
|
IndexPatternDimensionEditor,
|
|
canHandleDrop,
|
|
onDrop,
|
|
} from './dimension_panel';
|
|
import { IndexPatternDataPanel } from './datapanel';
|
|
import {
|
|
getDatasourceSuggestionsForField,
|
|
getDatasourceSuggestionsFromCurrentState,
|
|
} from './indexpattern_suggestions';
|
|
|
|
import { isDraggedField, normalizeOperationDataType } from './utils';
|
|
import { LayerPanel } from './layerpanel';
|
|
import { IndexPatternColumn } from './operations';
|
|
import {
|
|
IndexPatternField,
|
|
IndexPatternLayer,
|
|
IndexPatternPrivateState,
|
|
IndexPatternPersistedState,
|
|
} from './types';
|
|
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
|
|
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
|
import { deleteColumn } from './state_helpers';
|
|
import { Datasource, StateSetter } from '../index';
|
|
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
|
|
|
export { OperationType, IndexPatternColumn } from './operations';
|
|
|
|
export interface DraggedField {
|
|
field: IndexPatternField;
|
|
indexPatternId: string;
|
|
}
|
|
|
|
export function columnToOperation(column: IndexPatternColumn, uniqueLabel?: string): Operation {
|
|
const { dataType, label, isBucketed, scale } = column;
|
|
return {
|
|
dataType: normalizeOperationDataType(dataType),
|
|
isBucketed,
|
|
scale,
|
|
label: uniqueLabel || label,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Return a map of columnId => unique column label. Exported for testing reasons.
|
|
*/
|
|
export function uniqueLabels(layers: Record<string, IndexPatternLayer>) {
|
|
const columnLabelMap = {} as Record<string, string>;
|
|
const counts = {} as Record<string, number>;
|
|
|
|
const makeUnique = (label: string) => {
|
|
let uniqueLabel = label;
|
|
|
|
while (counts[uniqueLabel] >= 0) {
|
|
const num = ++counts[uniqueLabel];
|
|
uniqueLabel = i18n.translate('xpack.lens.indexPattern.uniqueLabel', {
|
|
defaultMessage: '{label} [{num}]',
|
|
values: { label, num },
|
|
});
|
|
}
|
|
|
|
counts[uniqueLabel] = 0;
|
|
return uniqueLabel;
|
|
};
|
|
|
|
Object.values(layers).forEach((layer) => {
|
|
if (!layer.columns) {
|
|
return;
|
|
}
|
|
Object.entries(layer.columns).forEach(([columnId, column]) => {
|
|
columnLabelMap[columnId] = makeUnique(column.label);
|
|
});
|
|
});
|
|
|
|
return columnLabelMap;
|
|
}
|
|
|
|
export * from './rename_columns';
|
|
|
|
export function getIndexPatternDatasource({
|
|
core,
|
|
storage,
|
|
data,
|
|
charts,
|
|
}: {
|
|
core: CoreStart;
|
|
storage: IStorageWrapper;
|
|
data: DataPublicPluginStart;
|
|
charts: ChartsPluginSetup;
|
|
}) {
|
|
const savedObjectsClient = core.savedObjects.client;
|
|
const uiSettings = core.uiSettings;
|
|
const onIndexPatternLoadError = (err: Error) =>
|
|
core.notifications.toasts.addError(err, {
|
|
title: i18n.translate('xpack.lens.indexPattern.indexPatternLoadError', {
|
|
defaultMessage: 'Error loading index pattern',
|
|
}),
|
|
});
|
|
|
|
const indexPatternsService = data.indexPatterns;
|
|
|
|
// Not stateful. State is persisted to the frame
|
|
const indexPatternDatasource: Datasource<IndexPatternPrivateState, IndexPatternPersistedState> = {
|
|
id: 'indexpattern',
|
|
|
|
async initialize(
|
|
persistedState?: IndexPatternPersistedState,
|
|
references?: SavedObjectReference[]
|
|
) {
|
|
return loadInitialState({
|
|
persistedState,
|
|
references,
|
|
savedObjectsClient: await savedObjectsClient,
|
|
defaultIndexPatternId: core.uiSettings.get('defaultIndex'),
|
|
storage,
|
|
indexPatternsService,
|
|
});
|
|
},
|
|
|
|
getPersistableState(state: IndexPatternPrivateState) {
|
|
return extractReferences(state);
|
|
},
|
|
|
|
insertLayer(state: IndexPatternPrivateState, newLayerId: string) {
|
|
return {
|
|
...state,
|
|
layers: {
|
|
...state.layers,
|
|
[newLayerId]: blankLayer(state.currentIndexPatternId),
|
|
},
|
|
};
|
|
},
|
|
|
|
removeLayer(state: IndexPatternPrivateState, layerId: string) {
|
|
const newLayers = { ...state.layers };
|
|
delete newLayers[layerId];
|
|
|
|
return {
|
|
...state,
|
|
layers: newLayers,
|
|
};
|
|
},
|
|
|
|
clearLayer(state: IndexPatternPrivateState, layerId: string) {
|
|
return {
|
|
...state,
|
|
layers: {
|
|
...state.layers,
|
|
[layerId]: blankLayer(state.currentIndexPatternId),
|
|
},
|
|
};
|
|
},
|
|
|
|
getLayers(state: IndexPatternPrivateState) {
|
|
return Object.keys(state.layers);
|
|
},
|
|
|
|
removeColumn({ prevState, layerId, columnId }) {
|
|
return deleteColumn({
|
|
state: prevState,
|
|
layerId,
|
|
columnId,
|
|
});
|
|
},
|
|
|
|
toExpression,
|
|
|
|
renderDataPanel(
|
|
domElement: Element,
|
|
props: DatasourceDataPanelProps<IndexPatternPrivateState>
|
|
) {
|
|
render(
|
|
<I18nProvider>
|
|
<IndexPatternDataPanel
|
|
changeIndexPattern={(
|
|
id: string,
|
|
state: IndexPatternPrivateState,
|
|
setState: StateSetter<IndexPatternPrivateState>
|
|
) => {
|
|
changeIndexPattern({
|
|
id,
|
|
state,
|
|
setState,
|
|
onError: onIndexPatternLoadError,
|
|
storage,
|
|
indexPatternsService,
|
|
});
|
|
}}
|
|
data={data}
|
|
charts={charts}
|
|
{...props}
|
|
/>
|
|
</I18nProvider>,
|
|
domElement
|
|
);
|
|
},
|
|
|
|
renderDimensionTrigger: (
|
|
domElement: Element,
|
|
props: DatasourceDimensionTriggerProps<IndexPatternPrivateState>
|
|
) => {
|
|
const columnLabelMap = uniqueLabels(props.state.layers);
|
|
|
|
render(
|
|
<I18nProvider>
|
|
<KibanaContextProvider
|
|
services={{
|
|
appName: 'lens',
|
|
storage,
|
|
uiSettings,
|
|
data,
|
|
savedObjects: core.savedObjects,
|
|
docLinks: core.docLinks,
|
|
}}
|
|
>
|
|
<IndexPatternDimensionTrigger uniqueLabel={columnLabelMap[props.columnId]} {...props} />
|
|
</KibanaContextProvider>
|
|
</I18nProvider>,
|
|
domElement
|
|
);
|
|
},
|
|
|
|
renderDimensionEditor: (
|
|
domElement: Element,
|
|
props: DatasourceDimensionEditorProps<IndexPatternPrivateState>
|
|
) => {
|
|
const columnLabelMap = uniqueLabels(props.state.layers);
|
|
|
|
render(
|
|
<I18nProvider>
|
|
<KibanaContextProvider
|
|
services={{
|
|
appName: 'lens',
|
|
storage,
|
|
uiSettings,
|
|
data,
|
|
savedObjects: core.savedObjects,
|
|
docLinks: core.docLinks,
|
|
http: core.http,
|
|
}}
|
|
>
|
|
<IndexPatternDimensionEditor
|
|
uiSettings={uiSettings}
|
|
storage={storage}
|
|
savedObjectsClient={core.savedObjects.client}
|
|
http={core.http}
|
|
data={data}
|
|
uniqueLabel={columnLabelMap[props.columnId]}
|
|
{...props}
|
|
/>
|
|
</KibanaContextProvider>
|
|
</I18nProvider>,
|
|
domElement
|
|
);
|
|
},
|
|
|
|
renderLayerPanel: (
|
|
domElement: Element,
|
|
props: DatasourceLayerPanelProps<IndexPatternPrivateState>
|
|
) => {
|
|
render(
|
|
<LayerPanel
|
|
onChangeIndexPattern={(indexPatternId) => {
|
|
changeLayerIndexPattern({
|
|
indexPatternId,
|
|
setState: props.setState,
|
|
state: props.state,
|
|
layerId: props.layerId,
|
|
onError: onIndexPatternLoadError,
|
|
replaceIfPossible: true,
|
|
storage,
|
|
indexPatternsService,
|
|
});
|
|
}}
|
|
{...props}
|
|
/>,
|
|
domElement
|
|
);
|
|
},
|
|
|
|
canHandleDrop,
|
|
onDrop,
|
|
|
|
getPublicAPI({ state, layerId }: PublicAPIProps<IndexPatternPrivateState>) {
|
|
const columnLabelMap = uniqueLabels(state.layers);
|
|
|
|
return {
|
|
datasourceId: 'indexpattern',
|
|
|
|
getTableSpec: () => {
|
|
return state.layers[layerId].columnOrder.map((colId) => ({ columnId: colId }));
|
|
},
|
|
getOperationForColumnId: (columnId: string) => {
|
|
const layer = state.layers[layerId];
|
|
|
|
if (layer && layer.columns[columnId]) {
|
|
return columnToOperation(layer.columns[columnId], columnLabelMap[columnId]);
|
|
}
|
|
return null;
|
|
},
|
|
};
|
|
},
|
|
getDatasourceSuggestionsForField(state, draggedField) {
|
|
return isDraggedField(draggedField)
|
|
? getDatasourceSuggestionsForField(state, draggedField.indexPatternId, draggedField.field)
|
|
: [];
|
|
},
|
|
getDatasourceSuggestionsFromCurrentState,
|
|
};
|
|
|
|
return indexPatternDatasource;
|
|
}
|
|
|
|
function blankLayer(indexPatternId: string) {
|
|
return {
|
|
indexPatternId,
|
|
columns: {},
|
|
columnOrder: [],
|
|
};
|
|
}
|