Update example plugins to stop using deprecated APIs (#121547)

This commit is contained in:
Anton Dosov 2021-12-20 14:43:16 +01:00 committed by GitHub
parent f1bb5ea96c
commit 2fa5a87a5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 199 additions and 208 deletions

View file

@ -20,23 +20,20 @@ import {
DefaultItemAction,
} from '@elastic/eui';
import { AppMountParameters } from '../../../src/core/public';
import {
DataPublicPluginStart,
IndexPattern,
IndexPatternField,
} from '../../../src/plugins/data/public';
import { DataPublicPluginStart } from '../../../src/plugins/data/public';
import type { DataView, DataViewField } from '../../../src/plugins/data_views/public';
import { IndexPatternFieldEditorStart } from '../../../src/plugins/data_view_field_editor/public';
interface Props {
indexPattern?: IndexPattern;
dataView?: DataView;
dataViewFieldEditor: IndexPatternFieldEditorStart;
}
const IndexPatternFieldEditorExample = ({ indexPattern, dataViewFieldEditor }: Props) => {
const [fields, setFields] = useState<IndexPatternField[]>(
indexPattern?.getNonScriptedFields() || []
const DataViewFieldEditorExample = ({ dataView, dataViewFieldEditor }: Props) => {
const [fields, setFields] = useState<DataViewField[]>(
dataView?.fields.getAll().filter((f) => !f.scripted) || []
);
const refreshFields = () => setFields(indexPattern?.getNonScriptedFields() || []);
const refreshFields = () => setFields(dataView?.fields.getAll().filter((f) => !f.scripted) || []);
const columns = [
{
field: 'name',
@ -51,9 +48,9 @@ const IndexPatternFieldEditorExample = ({ indexPattern, dataViewFieldEditor }: P
icon: 'pencil',
type: 'icon',
'data-test-subj': 'editField',
onClick: (fld: IndexPatternField) =>
onClick: (fld: DataViewField) =>
dataViewFieldEditor.openEditor({
ctx: { dataView: indexPattern! },
ctx: { dataView: dataView! },
fieldName: fld.name,
onSave: refreshFields,
}),
@ -65,27 +62,27 @@ const IndexPatternFieldEditorExample = ({ indexPattern, dataViewFieldEditor }: P
type: 'icon',
'data-test-subj': 'deleteField',
available: (fld) => !!fld.runtimeField,
onClick: (fld: IndexPatternField) =>
onClick: (fld: DataViewField) =>
dataViewFieldEditor.openDeleteModal({
fieldName: fld.name,
ctx: {
dataView: indexPattern!,
dataView: dataView!,
},
onDelete: refreshFields,
}),
},
] as Array<DefaultItemAction<IndexPatternField>>,
] as Array<DefaultItemAction<DataViewField>>,
},
];
const content = indexPattern ? (
const content = dataView ? (
<>
<EuiText data-test-subj="indexPatternTitle">Index pattern: {indexPattern?.title}</EuiText>
<EuiText data-test-subj="dataViewTitle">Data view: {dataView.title}</EuiText>
<div>
<EuiButton
onClick={() =>
dataViewFieldEditor.openEditor({
ctx: { dataView: indexPattern! },
ctx: { dataView },
onSave: refreshFields,
})
}
@ -94,7 +91,7 @@ const IndexPatternFieldEditorExample = ({ indexPattern, dataViewFieldEditor }: P
Add field
</EuiButton>
</div>
<EuiInMemoryTable<IndexPatternField>
<EuiInMemoryTable<DataViewField>
items={fields}
columns={columns}
pagination={true}
@ -108,13 +105,13 @@ const IndexPatternFieldEditorExample = ({ indexPattern, dataViewFieldEditor }: P
/>
</>
) : (
<p>Please create an index pattern</p>
<p>Please create a data view</p>
);
return (
<EuiPage>
<EuiPageBody>
<EuiPageHeader>Index pattern field editor demo</EuiPageHeader>
<EuiPageHeader>Data view field editor demo</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>{content}</EuiPageContentBody>
</EuiPageContent>
@ -132,12 +129,9 @@ export const renderApp = async (
{ data, dataViewFieldEditor }: RenderAppDependencies,
{ element }: AppMountParameters
) => {
const indexPattern = (await data.indexPatterns.getDefault()) || undefined;
const dataView = (await data.dataViews.getDefault()) || undefined;
ReactDOM.render(
<IndexPatternFieldEditorExample
indexPattern={indexPattern}
dataViewFieldEditor={dataViewFieldEditor}
/>,
<DataViewFieldEditorExample dataView={dataView} dataViewFieldEditor={dataViewFieldEditor} />,
element
);

View file

@ -6,6 +6,6 @@
* Side Public License, v 1.
*/
import { IndexPatternFieldEditorPlugin } from './plugin';
import { DataViewFieldEditorPlugin } from './plugin';
export const plugin = () => new IndexPatternFieldEditorPlugin();
export const plugin = () => new DataViewFieldEditorPlugin();

View file

@ -20,11 +20,11 @@ interface SetupDeps {
developerExamples: DeveloperExamplesSetup;
}
export class IndexPatternFieldEditorPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
export class DataViewFieldEditorPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
public setup(core: CoreSetup<StartDeps>, deps: SetupDeps) {
core.application.register({
id: 'indexPatternFieldEditorExample',
title: 'Index pattern field editor example',
id: 'dataViewFieldEditorExample',
title: 'Data view field editor example',
navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
const [, depsStart] = await core.getStartServices();
@ -34,13 +34,13 @@ export class IndexPatternFieldEditorPlugin implements Plugin<void, void, SetupDe
});
deps.developerExamples.register({
appId: 'indexPatternFieldEditorExample',
title: 'Index pattern field editor',
description: `IndexPatternFieldEditor provides a UI for editing index pattern fields directly from Kibana apps. This example plugin demonstrates integration.`,
appId: 'dataViewFieldEditorExample',
title: 'Data view field editor',
description: `DataViewFieldEditor provides a UI for editing data view fields directly from Kibana apps. This example plugin demonstrates integration.`,
links: [
{
label: 'README',
href: 'https://github.com/elastic/kibana/blob/main/src/plugins/index_pattern_field_editor/README.md',
href: 'https://github.com/elastic/kibana/blob/main/src/plugins/data_view_field_editor/README.md',
iconType: 'logoGithub',
size: 's',
target: '_blank',

View file

@ -14,6 +14,7 @@
{ "path": "../../src/core/tsconfig.json" },
{ "path": "../../src/plugins/kibana_react/tsconfig.json" },
{ "path": "../../src/plugins/data/tsconfig.json" },
{ "path": "../../src/plugins/data_views/tsconfig.json" },
{ "path": "../../src/plugins/data_view_field_editor/tsconfig.json" },
{ "path": "../developer_examples/tsconfig.json" },
]

View file

@ -43,7 +43,7 @@ export interface Deps {
/**
* Just for demo purposes
*/
openIndexPatternNumberFieldEditor: () => void;
openDateViewNumberFieldEditor: () => void;
}
const UsingAnExistingFieldFormatExample: React.FC<{ deps: Deps }> = (props) => {
@ -123,15 +123,15 @@ const CreatingCustomFieldFormat: React.FC<{ deps: Deps }> = (props) => {
<EuiSpacer size={'s'} />
<EuiCallOut
title="Seamless integration with index patterns!"
title="Seamless integration with data views!"
color="success"
iconType="indexManagementApp"
>
<p>
Currency formatter that we&apos;ve just created is already integrated with index patterns.
It can be applied to any <EuiCode>numeric</EuiCode> field of any index pattern.{' '}
<EuiLink onClick={() => props.deps.openIndexPatternNumberFieldEditor()}>
Open index pattern field editor to give it a try.
Currency formatter that we&apos;ve just created is already integrated with data views. It
can be applied to any <EuiCode>numeric</EuiCode> field of any data view.{' '}
<EuiLink onClick={() => props.deps.openDateViewNumberFieldEditor()}>
Open data view field editor to give it a try.
</EuiLink>
</p>
</EuiCallOut>
@ -155,15 +155,15 @@ const CreatingCustomFieldFormatEditor: React.FC<{ deps: Deps }> = (props) => {
<EuiSpacer size={'s'} />
<EuiCallOut
title="Check the result in the index pattern field editor!"
title="Check the result in the data view field editor!"
color="primary"
iconType="indexManagementApp"
>
<p>
Currency formatter and its custom editor are integrated with index patterns. It can be
applied to any <EuiCode>numeric</EuiCode> field of any index pattern.{' '}
<EuiLink onClick={() => props.deps.openIndexPatternNumberFieldEditor()}>
Open index pattern field editor to give it a try.
Currency formatter and its custom editor are integrated with data views. It can be applied
to any <EuiCode>numeric</EuiCode> field of any data view.{' '}
<EuiLink onClick={() => props.deps.openDateViewNumberFieldEditor()}>
Open date view field editor to give it a try.
</EuiLink>
</p>
</EuiCallOut>

View file

@ -45,29 +45,29 @@ export class FieldFormatsExamplePlugin implements Plugin<void, void, SetupDeps,
registerExampleFormatEditor(deps.dataViewFieldEditor);
// just for demonstration purposes:
// opens a field editor using default index pattern and first number field
const openIndexPatternNumberFieldEditor = async () => {
// opens a field editor using default data view and first number field
const openDateViewNumberFieldEditor = async () => {
const [, plugins] = await core.getStartServices();
const indexPattern = await plugins.data.indexPatterns.getDefault();
if (!indexPattern) {
alert('Creating at least one index pattern to continue with this example');
const dataView = await plugins.data.dataViews.getDefault();
if (!dataView) {
alert('Create at least one data view to continue with this example');
return;
}
const numberField = indexPattern
.getNonScriptedFields()
.find((f) => !f.name.startsWith('_') && f.type === KBN_FIELD_TYPES.NUMBER);
const numberField = dataView.fields
.getAll()
.find((f) => !f.name.startsWith('_') && f.type === KBN_FIELD_TYPES.NUMBER && !f.scripted);
if (!numberField) {
alert(
'Default index pattern needs at least a single field of type `number` to continue with this example'
'Default data view needs at least a single field of type `number` to continue with this example'
);
return;
}
plugins.dataViewFieldEditor.openEditor({
ctx: {
dataView: indexPattern,
dataView,
},
fieldName: numberField.name,
});
@ -81,7 +81,7 @@ export class FieldFormatsExamplePlugin implements Plugin<void, void, SetupDeps,
async mount({ element }: AppMountParameters) {
const [, plugins] = await core.getStartServices();
ReactDOM.render(
<App deps={{ fieldFormats: plugins.fieldFormats, openIndexPatternNumberFieldEditor }} />,
<App deps={{ fieldFormats: plugins.fieldFormats, openDateViewNumberFieldEditor }} />,
element
);
return () => ReactDOM.unmountComponentAtNode(element);

View file

@ -41,11 +41,10 @@ import { PLUGIN_ID, PLUGIN_NAME, SERVER_SEARCH_ROUTE_PATH } from '../../common';
import {
DataPublicPluginStart,
IKibanaSearchResponse,
IndexPattern,
IndexPatternField,
isCompleteResponse,
isErrorResponse,
} from '../../../../src/plugins/data/public';
import type { DataViewField, DataView } from '../../../../src/plugins/data_views/public';
import { IMyStrategyResponse } from '../../common/types';
import { AbortError } from '../../../../src/plugins/kibana_utils/common';
@ -56,22 +55,22 @@ interface SearchExamplesAppDeps {
data: DataPublicPluginStart;
}
function getNumeric(fields?: IndexPatternField[]) {
function getNumeric(fields?: DataViewField[]) {
if (!fields) return [];
return fields?.filter((f) => f.type === 'number' && f.aggregatable);
}
function getAggregatableStrings(fields?: IndexPatternField[]) {
function getAggregatableStrings(fields?: DataViewField[]) {
if (!fields) return [];
return fields?.filter((f) => f.type === 'string' && f.aggregatable);
}
function formatFieldToComboBox(field?: IndexPatternField | null) {
function formatFieldToComboBox(field?: DataViewField | null) {
if (!field) return [];
return formatFieldsToComboBox([field]);
}
function formatFieldsToComboBox(fields?: IndexPatternField[]) {
function formatFieldsToComboBox(fields?: DataViewField[]) {
if (!fields) return [];
return fields?.map((field) => {
@ -93,14 +92,14 @@ export const SearchExamplesApp = ({
const [timeTook, setTimeTook] = useState<number | undefined>();
const [total, setTotal] = useState<number>(100);
const [loaded, setLoaded] = useState<number>(0);
const [indexPattern, setIndexPattern] = useState<IndexPattern | null>();
const [fields, setFields] = useState<IndexPatternField[]>();
const [selectedFields, setSelectedFields] = useState<IndexPatternField[]>([]);
const [dataView, setDataView] = useState<DataView | null>();
const [fields, setFields] = useState<DataViewField[]>();
const [selectedFields, setSelectedFields] = useState<DataViewField[]>([]);
const [selectedNumericField, setSelectedNumericField] = useState<
IndexPatternField | null | undefined
DataViewField | null | undefined
>();
const [selectedBucketField, setSelectedBucketField] = useState<
IndexPatternField | null | undefined
DataViewField | null | undefined
>();
const [request, setRequest] = useState<Record<string, any>>({});
const [isLoading, setIsLoading] = useState<boolean>(false);
@ -115,20 +114,20 @@ export const SearchExamplesApp = ({
setTimeTook(response.rawResponse.took);
}
// Fetch the default index pattern using the `data.indexPatterns` service, as the component is mounted.
// Fetch the default data view using the `data.dataViews` service, as the component is mounted.
useEffect(() => {
const setDefaultIndexPattern = async () => {
const defaultIndexPattern = await data.indexPatterns.getDefault();
setIndexPattern(defaultIndexPattern);
const setDefaultDataView = async () => {
const defaultDataView = await data.dataViews.getDefault();
setDataView(defaultDataView);
};
setDefaultIndexPattern();
setDefaultDataView();
}, [data]);
// Update the fields list every time the index pattern is modified.
// Update the fields list every time the data view is modified.
useEffect(() => {
setFields(indexPattern?.fields);
}, [indexPattern]);
setFields(dataView?.fields);
}, [dataView]);
useEffect(() => {
setSelectedBucketField(fields?.length ? getAggregatableStrings(fields)[0] : null);
setSelectedNumericField(fields?.length ? getNumeric(fields)[0] : null);
@ -140,10 +139,10 @@ export const SearchExamplesApp = ({
addWarning: boolean = false,
addError: boolean = false
) => {
if (!indexPattern || !selectedNumericField) return;
if (!dataView || !selectedNumericField) return;
// Construct the query portion of the search request
const query = data.query.getEsQuery(indexPattern);
const query = data.query.getEsQuery(dataView);
if (addWarning) {
query.bool.must.push({
@ -151,7 +150,7 @@ export const SearchExamplesApp = ({
error_query: {
indices: [
{
name: indexPattern.title,
name: dataView.title,
error_type: 'warning',
message: 'Watch out!',
},
@ -165,7 +164,7 @@ export const SearchExamplesApp = ({
error_query: {
indices: [
{
name: indexPattern.title,
name: dataView.title,
error_type: 'exception',
message: 'Watch out!',
},
@ -176,11 +175,11 @@ export const SearchExamplesApp = ({
// Construct the aggregations portion of the search request by using the `data.search.aggs` service.
const aggs = [{ type: 'avg', params: { field: selectedNumericField!.name } }];
const aggsDsl = data.search.aggs.createAggConfigs(indexPattern, aggs).toDsl();
const aggsDsl = data.search.aggs.createAggConfigs(dataView, aggs).toDsl();
const req = {
params: {
index: indexPattern.title,
index: dataView.title,
body: {
aggs: aggsDsl,
query,
@ -264,11 +263,11 @@ export const SearchExamplesApp = ({
};
const doSearchSourceSearch = async (otherBucket: boolean) => {
if (!indexPattern) return;
if (!dataView) return;
const query = data.query.queryString.getQuery();
const filters = data.query.filterManager.getFilters();
const timefilter = data.query.timefilter.timefilter.createFilter(indexPattern);
const timefilter = data.query.timefilter.timefilter.createFilter(dataView);
if (timefilter) {
filters.push(timefilter);
}
@ -277,7 +276,7 @@ export const SearchExamplesApp = ({
const searchSource = await data.search.searchSource.create();
searchSource
.setField('index', indexPattern)
.setField('index', dataView)
.setField('filter', filters)
.setField('query', query)
.setField('fields', selectedFields.length ? selectedFields.map((f) => f.name) : [''])
@ -296,7 +295,7 @@ export const SearchExamplesApp = ({
aggDef.push({ type: 'avg', params: { field: selectedNumericField.name } });
}
if (aggDef.length > 0) {
const ac = data.search.aggs.createAggConfigs(indexPattern, aggDef);
const ac = data.search.aggs.createAggConfigs(dataView, aggDef);
searchSource.setField('aggs', ac);
}
@ -407,14 +406,14 @@ export const SearchExamplesApp = ({
};
const onServerClickHandler = async () => {
if (!indexPattern || !selectedNumericField) return;
if (!dataView || !selectedNumericField) return;
const abortController = new AbortController();
setAbortController(abortController);
setIsLoading(true);
try {
const res = await http.get(SERVER_SEARCH_ROUTE_PATH, {
query: {
index: indexPattern.title,
index: dataView.title,
field: selectedNumericField!.name,
},
signal: abortController.signal,
@ -510,22 +509,26 @@ export const SearchExamplesApp = ({
appName={PLUGIN_ID}
showSearchBar={true}
useDefaultBehaviors={true}
indexPatterns={indexPattern ? [indexPattern] : undefined}
indexPatterns={dataView ? [dataView] : undefined}
/>
<EuiFlexGrid columns={4}>
<EuiFlexItem>
<EuiFormLabel>Index Pattern</EuiFormLabel>
<EuiFormLabel>Data view</EuiFormLabel>
<IndexPatternSelect
placeholder={i18n.translate('searchSessionExample.selectIndexPatternPlaceholder', {
defaultMessage: 'Select index pattern',
placeholder={i18n.translate('searchSessionExample.selectDataViewPlaceholder', {
defaultMessage: 'Select data view',
})}
indexPatternId={indexPattern?.id || ''}
onChange={async (newIndexPatternId: any) => {
const newIndexPattern = await data.indexPatterns.get(newIndexPatternId);
setIndexPattern(newIndexPattern);
indexPatternId={dataView?.id || ''}
onChange={async (dataViewId?: string) => {
if (dataViewId) {
const newDataView = await data.dataViews.get(dataViewId);
setDataView(newDataView);
} else {
setDataView(undefined);
}
}}
isClearable={false}
data-test-subj="indexPatternSelector"
data-test-subj="dataViewSelector"
/>
</EuiFlexItem>
<EuiFlexItem>
@ -536,7 +539,7 @@ export const SearchExamplesApp = ({
singleSelection={true}
onChange={(option) => {
if (option.length) {
const fld = indexPattern?.getFieldByName(option[0].label);
const fld = dataView?.getFieldByName(option[0].label);
setSelectedBucketField(fld || null);
} else {
setSelectedBucketField(null);
@ -554,7 +557,7 @@ export const SearchExamplesApp = ({
singleSelection={true}
onChange={(option) => {
if (option.length) {
const fld = indexPattern?.getFieldByName(option[0].label);
const fld = dataView?.getFieldByName(option[0].label);
setSelectedNumericField(fld || null);
} else {
setSelectedNumericField(null);
@ -572,9 +575,9 @@ export const SearchExamplesApp = ({
singleSelection={false}
onChange={(option) => {
const flds = option
.map((opt) => indexPattern?.getFieldByName(opt?.label))
.map((opt) => dataView?.getFieldByName(opt?.label))
.filter((f) => f);
setSelectedFields(flds.length ? (flds as IndexPatternField[]) : []);
setSelectedFields(flds.length ? (flds as DataViewField[]) : []);
}}
sortMatchesBy="startsWith"
/>
@ -590,9 +593,8 @@ export const SearchExamplesApp = ({
</EuiTitle>
<EuiText>
If you want to fetch data from Elasticsearch, you can use the different services
provided by the <EuiCode>data</EuiCode> plugin. These help you get the index pattern
and search bar configuration, format them into a DSL query and send it to
Elasticsearch.
provided by the <EuiCode>data</EuiCode> plugin. These help you get the data view and
search bar configuration, format them into a DSL query and send it to Elasticsearch.
<EuiSpacer />
<EuiButtonEmpty size="xs" onClick={onClickHandler} iconType="play">
<FormattedMessage

View file

@ -43,14 +43,13 @@ import {
DataPublicPluginStart,
IEsSearchRequest,
IEsSearchResponse,
IndexPattern,
IndexPatternField,
isCompleteResponse,
isErrorResponse,
QueryState,
SearchSessionState,
TimeRange,
} from '../../../../src/plugins/data/public';
import type { DataView, DataViewField } from '../../../../src/plugins/data_views/public';
import {
createStateContainer,
useContainerState,
@ -77,7 +76,7 @@ enum DemoStep {
}
interface State extends QueryState {
indexPatternId?: string;
dataViewId?: string;
numericFieldName?: string;
/**
@ -123,10 +122,10 @@ export const SearchSessionsExampleApp = ({
const {
numericFieldName,
indexPattern,
dataView,
selectedField,
fields,
setIndexPattern,
setDataView,
setNumericFieldName,
state,
} = useAppState({ data });
@ -141,14 +140,14 @@ export const SearchSessionsExampleApp = ({
time: data.query.timefilter.timefilter.getTime(),
filters: data.query.filterManager.getFilters(),
query: data.query.queryString.getQuery(),
indexPatternId: indexPattern?.id,
dataViewId: dataView?.id,
numericFieldName,
},
restoreState: {
time: data.query.timefilter.timefilter.getAbsoluteTime(),
filters: data.query.filterManager.getFilters(),
query: data.query.queryString.getQuery(),
indexPatternId: indexPattern?.id,
dataViewId: dataView?.id,
numericFieldName,
searchSessionId: data.search.session.getSessionId(),
},
@ -160,7 +159,7 @@ export const SearchSessionsExampleApp = ({
data.query.queryString,
data.query.timefilter.timefilter,
data.search.session,
indexPattern?.id,
dataView?.id,
numericFieldName,
]);
@ -198,11 +197,11 @@ export const SearchSessionsExampleApp = ({
const search = useCallback(
(restoreSearchSessionId?: string) => {
if (!indexPattern) return;
if (!dataView) return;
if (!numericFieldName) return;
setIsSearching(true);
const requestId = ++nextRequestIdRef.current;
doSearch({ indexPattern, numericFieldName, restoreSearchSessionId }, { data, notifications })
doSearch({ dataView, numericFieldName, restoreSearchSessionId }, { data, notifications })
.then(({ response: res, request: req, tookMs: _tookMs }) => {
if (requestId !== nextRequestIdRef.current) return; // no longer interested in this result
if (restoreSearchSessionId) {
@ -220,7 +219,7 @@ export const SearchSessionsExampleApp = ({
setIsSearching(false);
});
},
[data, notifications, indexPattern, numericFieldName]
[data, notifications, dataView, numericFieldName]
);
useEffect(() => {
@ -243,9 +242,9 @@ export const SearchSessionsExampleApp = ({
<EuiSpacer />
</>
)}
{!indexPattern && (
{!dataView && (
<>
<NoIndexPatternsCallout />
<NoDataViewsCallout />
<EuiSpacer />
</>
)}
@ -280,26 +279,23 @@ export const SearchSessionsExampleApp = ({
appName={PLUGIN_ID}
showSearchBar={true}
useDefaultBehaviors={true}
indexPatterns={indexPattern ? [indexPattern] : undefined}
indexPatterns={dataView ? [dataView] : undefined}
onQuerySubmit={reset}
/>
<EuiFlexGroup justifyContent={'flexStart'}>
<EuiFlexItem grow={false}>
<EuiFormLabel>Index Pattern</EuiFormLabel>
<EuiFormLabel>Data view</EuiFormLabel>
<IndexPatternSelect
placeholder={i18n.translate(
'searchSessionExample.selectIndexPatternPlaceholder',
{
defaultMessage: 'Select index pattern',
}
)}
indexPatternId={indexPattern?.id ?? ''}
placeholder={i18n.translate('searchSessionExample.selectDataViewPlaceholder', {
defaultMessage: 'Select data view',
})}
indexPatternId={dataView?.id ?? ''}
onChange={(id) => {
if (!id) return;
setIndexPattern(id);
setDataView(id);
}}
isClearable={false}
data-test-subj="indexPatternSelector"
data-test-subj="dataViewSelector"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
@ -309,7 +305,7 @@ export const SearchSessionsExampleApp = ({
selectedOptions={formatFieldToComboBox(selectedField)}
singleSelection={true}
onChange={(option) => {
const fld = indexPattern?.getFieldByName(option[0].label);
const fld = dataView?.getFieldByName(option[0].label);
if (!fld) return;
setNumericFieldName(fld?.name);
}}
@ -336,7 +332,7 @@ export const SearchSessionsExampleApp = ({
size="xs"
onClick={() => search()}
iconType="play"
disabled={isSearching || !indexPattern || !numericFieldName}
disabled={isSearching || !dataView || !numericFieldName}
data-test-subj={'startSearch'}
>
Start the search from low-level client (data.search.search)
@ -540,7 +536,7 @@ function SearchInspector({
function useAppState({ data }: { data: DataPublicPluginStart }) {
const stateContainer = useMemo(() => {
const { filters, time, searchSessionId, numericFieldName, indexPatternId, query } =
const { filters, time, searchSessionId, numericFieldName, dataViewId, query } =
getInitialStateFromUrl();
if (filters) {
@ -558,7 +554,7 @@ function useAppState({ data }: { data: DataPublicPluginStart }) {
return createStateContainer<State>({
restoreSessionId: searchSessionId,
numericFieldName,
indexPatternId,
dataViewId,
});
}, [data.query.filterManager, data.query.queryString, data.query.timefilter.timefilter]);
const setState = useCallback(
@ -575,78 +571,78 @@ function useAppState({ data }: { data: DataPublicPluginStart }) {
});
}, [stateContainer, data.query]);
const [fields, setFields] = useState<IndexPatternField[]>();
const [indexPattern, setIndexPattern] = useState<IndexPattern | null>();
const [fields, setFields] = useState<DataViewField[]>();
const [dataView, setDataView] = useState<DataView | null>();
// Fetch the default index pattern using the `data.indexPatterns` service, as the component is mounted.
// Fetch the default data view using the `data.dataViews` service, as the component is mounted.
useEffect(() => {
let canceled = false;
const loadIndexPattern = async () => {
const loadDataView = async () => {
// eslint-disable-next-line no-console
console.warn('Loading default index pattern');
let loadedIndexPattern = state.indexPatternId
? await data.indexPatterns.get(state.indexPatternId)
: await data.indexPatterns.getDefault();
if (!loadedIndexPattern) {
// try to find any available index pattern
const [id] = await data.indexPatterns.getIds(true);
console.warn('Loading default data view');
let loadedDataView = state.dataViewId
? await data.dataViews.get(state.dataViewId)
: await data.dataViews.getDefault();
if (!loadedDataView) {
// try to find any available data view
const [id] = await data.dataViews.getIds(true);
if (id) {
loadedIndexPattern = await data.indexPatterns.get(id);
loadedDataView = await data.dataViews.get(id);
}
}
if (canceled) return;
if (!loadedIndexPattern) {
if (!loadedDataView) {
// eslint-disable-next-line no-console
console.warn('No index patterns to pick from');
console.warn('No data view to pick from');
return;
}
if (!state.indexPatternId) {
if (!state.dataViewId) {
setState({
indexPatternId: loadedIndexPattern.id,
dataViewId: loadedDataView.id,
});
}
setIndexPattern(loadedIndexPattern);
setDataView(loadedDataView);
};
loadIndexPattern();
loadDataView();
return () => {
canceled = true;
};
}, [data, setState, state.indexPatternId]);
}, [data, setState, state.dataViewId]);
// Update the fields list every time the index pattern is modified.
// Update the fields list every time the data view is modified.
useEffect(() => {
setFields(indexPattern?.fields);
}, [indexPattern]);
setFields(dataView?.fields);
}, [dataView]);
useEffect(() => {
if (state.numericFieldName) return;
setState({ numericFieldName: fields?.length ? getNumeric(fields)[0]?.name : undefined });
}, [setState, fields, state.numericFieldName]);
const selectedField: IndexPatternField | undefined = useMemo(
() => indexPattern?.fields.find((field) => field.name === state.numericFieldName),
[indexPattern?.fields, state.numericFieldName]
const selectedField: DataViewField | undefined = useMemo(
() => dataView?.fields.find((field) => field.name === state.numericFieldName),
[dataView?.fields, state.numericFieldName]
);
return {
selectedField,
indexPattern,
dataView,
numericFieldName: state.numericFieldName,
fields,
setNumericFieldName: (field: string) => setState({ numericFieldName: field }),
setIndexPattern: (indexPatternId: string) => setState({ indexPatternId }),
setDataView: (dataViewId: string) => setState({ dataViewId }),
state,
};
}
function doSearch(
{
indexPattern,
dataView,
numericFieldName,
restoreSearchSessionId,
}: {
indexPattern: IndexPattern;
dataView: DataView;
numericFieldName: string;
restoreSearchSessionId?: string;
},
@ -655,7 +651,7 @@ function doSearch(
notifications,
}: { data: DataPublicPluginStart; notifications: CoreStart['notifications'] }
): Promise<{ request: IEsSearchRequest; response: IEsSearchResponse; tookMs?: number }> {
if (!indexPattern) return Promise.reject('Select an index patten');
if (!dataView) return Promise.reject('Select a data view');
if (!numericFieldName) return Promise.reject('Select a field to aggregate on');
// start a new session or restore an existing one
@ -668,7 +664,7 @@ function doSearch(
const sessionId = restoreSearchSessionId ? restoreSearchSessionId : data.search.session.start();
// Construct the query portion of the search request
const query = data.query.getEsQuery(indexPattern, restoreTimeRange);
const query = data.query.getEsQuery(dataView, restoreTimeRange);
// Construct the aggregations portion of the search request by using the `data.search.aggs` service.
@ -679,11 +675,11 @@ function doSearch(
]
: [{ type: 'avg', params: { field: numericFieldName } }];
const aggsDsl = data.search.aggs.createAggConfigs(indexPattern, aggs).toDsl();
const aggsDsl = data.search.aggs.createAggConfigs(dataView, aggs).toDsl();
const req = {
params: {
index: indexPattern.title,
index: dataView.title,
body: {
aggs: aggsDsl,
query,
@ -728,17 +724,17 @@ function doSearch(
.toPromise();
}
function getNumeric(fields?: IndexPatternField[]) {
function getNumeric(fields?: DataViewField[]) {
if (!fields) return [];
return fields?.filter((f) => f.type === 'number' && f.aggregatable);
}
function formatFieldToComboBox(field?: IndexPatternField | null) {
function formatFieldToComboBox(field?: DataViewField | null) {
if (!field) return [];
return formatFieldsToComboBox([field]);
}
function formatFieldsToComboBox(fields?: IndexPatternField[]) {
function formatFieldsToComboBox(fields?: DataViewField[]) {
if (!fields) return [];
return fields?.map((field) => {
@ -781,10 +777,10 @@ function NoShardDelayCallout() {
);
}
function NoIndexPatternsCallout() {
function NoDataViewsCallout() {
return (
<EuiCallOut title={<>Missing index patterns!</>} color="warning" iconType="help">
<p>This demo requires at least one index pattern.</p>
<EuiCallOut title={<>Missing data views!</>} color="warning" iconType="help">
<p>This demo requires at least one data view.</p>
</EuiCallOut>
);
}

View file

@ -7,7 +7,8 @@
*/
import { SerializableRecord } from '@kbn/utility-types';
import { esFilters, Filter, Query, TimeRange } from '../../../../src/plugins/data/public';
import { Filter, Query, isFilterPinned } from '@kbn/es-query';
import type { TimeRange } from '../../../../src/plugins/data/public';
import { getStatesFromKbnUrl, setStateToKbnUrl } from '../../../../src/plugins/kibana_utils/public';
import { LocatorDefinition } from '../../../../src/plugins/share/common';
@ -19,7 +20,7 @@ export const SEARCH_SESSIONS_EXAMPLES_APP_LOCATOR = 'SEARCH_SESSIONS_EXAMPLES_AP
export interface AppUrlState extends SerializableRecord {
filters?: Filter[];
query?: Query;
indexPatternId?: string;
dataViewId?: string;
numericFieldName?: string;
searchSessionId?: string;
}
@ -46,8 +47,8 @@ export class SearchSessionsExamplesAppLocatorDefinition
STATE_STORAGE_KEY,
{
query: params.query,
filters: params.filters?.filter((f) => !esFilters.isFilterPinned(f)),
indexPatternId: params.indexPatternId,
filters: params.filters?.filter((f) => !isFilterPinned(f)),
dataViewId: params.dataViewId,
numericFieldName: params.numericFieldName,
searchSessionId: params.searchSessionId,
} as AppUrlState,
@ -59,7 +60,7 @@ export class SearchSessionsExamplesAppLocatorDefinition
GLOBAL_STATE_STORAGE_KEY,
{
time: params.time,
filters: params.filters?.filter((f) => esFilters.isFilterPinned(f)),
filters: params.filters?.filter((f) => isFilterPinned(f)),
} as GlobalUrlState,
{ useHash: false, storeInHashQuery: false },
url
@ -75,7 +76,7 @@ export class SearchSessionsExamplesAppLocatorDefinition
export function getInitialStateFromUrl(): SearchSessionsExamplesAppLocatorParams {
const {
_a: { numericFieldName, indexPatternId, searchSessionId, filters: aFilters, query } = {},
_a: { numericFieldName, dataViewId, searchSessionId, filters: aFilters, query } = {},
_g: { filters: gFilters, time } = {},
} = getStatesFromKbnUrl<{ _a: AppUrlState; _g: GlobalUrlState }>(
window.location.href,
@ -90,7 +91,7 @@ export function getInitialStateFromUrl(): SearchSessionsExamplesAppLocatorParams
searchSessionId,
time,
filters: [...(gFilters ?? []), ...(aFilters ?? [])],
indexPatternId,
dataViewId,
query,
};
}

View file

@ -15,6 +15,7 @@
"references": [
{ "path": "../../src/core/tsconfig.json" },
{ "path": "../../src/plugins/data/tsconfig.json" },
{ "path": "../../src/plugins/data_views/tsconfig.json" },
{ "path": "../../src/plugins/kibana_utils/tsconfig.json" },
{ "path": "../../src/plugins/kibana_react/tsconfig.json" },
{ "path": "../../src/plugins/navigation/tsconfig.json" },

View file

@ -18,19 +18,18 @@ import {
EuiText,
EuiTitle,
} from '@elastic/eui';
import { Filter, FilterStateStore } from '@kbn/es-query';
import { CoreStart } from 'kibana/public';
import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public';
import {
connectToQueryState,
DataPublicPluginStart,
esFilters,
Filter,
IndexPattern,
Query,
QueryState,
syncQueryStateWithUrl,
} from '../../../../src/plugins/data/public';
import type { DataView } from '../../../../src/plugins/data_views/public';
import {
BaseStateContainer,
createStateContainer,
@ -73,13 +72,9 @@ export const App = ({
useGlobalStateSyncing(data.query, kbnUrlStateStorage);
useAppStateSyncing(appStateContainer, data.query, kbnUrlStateStorage);
const indexPattern = useIndexPattern(data);
if (!indexPattern)
return (
<div>
No index pattern found. Please create an index pattern before trying this example...
</div>
);
const dataView = useDataView(data);
if (!dataView)
return <div>No data view found. Please create a data view before trying this example...</div>;
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
return (
@ -102,7 +97,7 @@ export const App = ({
<navigation.ui.TopNavMenu
appName={'Example'}
showSearchBar={true}
indexPatterns={[indexPattern]}
indexPatterns={[dataView]}
useDefaultBehaviors={true}
showSaveQuery={true}
/>
@ -126,19 +121,19 @@ export const App = ({
);
};
function useIndexPattern(data: DataPublicPluginStart) {
const [indexPattern, setIndexPattern] = useState<IndexPattern>();
function useDataView(data: DataPublicPluginStart) {
const [dataView, setDataView] = useState<DataView>();
useEffect(() => {
const fetchIndexPattern = async () => {
const defaultIndexPattern = await data.indexPatterns.getDefault();
if (defaultIndexPattern) {
setIndexPattern(defaultIndexPattern);
const fetchDataView = async () => {
const defaultDataView = await data.dataViews.getDefault();
if (defaultDataView) {
setDataView(defaultDataView);
}
};
fetchIndexPattern();
}, [data.indexPatterns]);
fetchDataView();
}, [data.dataViews]);
return indexPattern;
return dataView;
}
function useGlobalStateSyncing(
@ -167,7 +162,7 @@ function useAppStateSyncing<AppState extends QueryState>(
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(
query,
appStateContainer,
{ filters: esFilters.FilterStateStore.APP_STATE, query: true }
{ filters: FilterStateStore.APP_STATE, query: true }
);
// sets up syncing app state container with url

View file

@ -18,6 +18,7 @@
{ "path": "../../src/plugins/kibana_react/tsconfig.json" },
{ "path": "../../src/plugins/navigation/tsconfig.json" },
{ "path": "../../src/plugins/data/tsconfig.json" },
{ "path": "../../src/plugins/data_views/tsconfig.json" },
{ "path": "../developer_examples/tsconfig.json" },
]
}

View file

@ -30,7 +30,7 @@ export default async function ({ readConfigFile }) {
require.resolve('./state_sync'),
require.resolve('./routing'),
require.resolve('./expressions_explorer'),
require.resolve('./index_pattern_field_editor_example'),
require.resolve('./data_view_field_editor_example'),
require.resolve('./field_formats'),
require.resolve('./partial_results'),
],

View file

@ -13,8 +13,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
const testSubjects = getService('testSubjects');
describe('', () => {
it('finds an index pattern', async () => {
await testSubjects.existOrFail('indexPatternTitle');
it('finds a data view', async () => {
await testSubjects.existOrFail('dataViewTitle');
});
it('opens the field editor', async () => {
await testSubjects.click('addField');

View file

@ -19,7 +19,7 @@ export default function ({
const es = getService('es');
const PageObjects = getPageObjects(['common', 'header', 'settings']);
describe('index pattern field editor example', function () {
describe('data view field editor example', function () {
this.tags('ciGroup2');
before(async () => {
await esArchiver.emptyKibanaIndex();
@ -32,9 +32,9 @@ export default function ({
await PageObjects.settings.navigateTo();
await PageObjects.settings.createIndexPattern('blogs', null);
await PageObjects.common.navigateToApp('indexPatternFieldEditorExample');
await PageObjects.common.navigateToApp('dataViewFieldEditorExample');
});
loadTestFile(require.resolve('./index_pattern_field_editor_example'));
loadTestFile(require.resolve('./data_view_field_editor_example'));
});
}

View file

@ -21,7 +21,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
before(async function () {
await PageObjects.common.navigateToApp(appId, { insertTimestamp: false });
await comboBox.setCustom('indexPatternSelector', 'logstash-*');
await comboBox.setCustom('dataViewSelector', 'logstash-*');
await comboBox.set('searchBucketField', 'geo.src');
await comboBox.set('searchMetricField', 'memory');
await PageObjects.timePicker.setAbsoluteRange(

View file

@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should start search, save session, restore session using "restore" button', async () => {
await comboBox.setCustom('indexPatternSelector', 'logstash-*');
await comboBox.setCustom('dataViewSelector', 'logstash-*');
await comboBox.setCustom('searchMetricField', 'bytes');
await testSubjects.clickWhenNotDisabled('startSearch');
await testSubjects.find('searchResults-1');

View file

@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
before(async function () {
await PageObjects.common.navigateToApp(appId, { insertTimestamp: false });
await comboBox.setCustom('indexPatternSelector', 'logstash-*');
await comboBox.setCustom('dataViewSelector', 'logstash-*');
await comboBox.set('searchBucketField', 'extension.raw');
await comboBox.set('searchMetricField', 'phpmemory');
});