mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Log Explorer] Implement Data Views tab into selector (#166938)
## 📓 Summary Closes #166848 This work adds a new tab to navigate Data View from the Log Explorer selector. In this first iteration, when the user selects a data view, we move into discovering preselecting and loading the data view of choice. **N.B.**: this recording is made on a setup where I have no installed integrations, so having the no integrations panel is the expected behaviour.e8d1f622
-86fb-4841-b4cc-4a913067d2cc ## Updated selector state machine <img width="1492" alt="Screenshot 2023-09-22 at 12 15 44" src="c563b765
-6c6c-41e8-b8cd-769c518932c3"> ## New DataViews state machine <img width="995" alt="Screenshot 2023-09-22 at 12 39 09" src="e4e43343
-c915-42d8-8660-a2ee89f8d595"> --------- Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
f8dae67d8d
commit
3be21c9e56
28 changed files with 1161 additions and 92 deletions
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import { AllDatasetSelection } from './all_dataset_selection';
|
||||
import { SingleDatasetSelection } from './single_dataset_selection';
|
||||
import { UnresolvedDatasetSelection } from './unresolved_dataset_selection';
|
||||
|
@ -14,6 +15,7 @@ export type DatasetSelection =
|
|||
| SingleDatasetSelection
|
||||
| UnresolvedDatasetSelection;
|
||||
export type DatasetSelectionChange = (datasetSelection: DatasetSelection) => void;
|
||||
export type DataViewSelection = (dataView: DataViewListItem) => void;
|
||||
|
||||
export const isDatasetSelection = (input: any): input is DatasetSelection => {
|
||||
return (
|
||||
|
|
|
@ -12,8 +12,10 @@ export const INTEGRATIONS_PANEL_ID = 'dataset-selector-integrations-panel';
|
|||
export const INTEGRATIONS_TAB_ID = 'dataset-selector-integrations-tab';
|
||||
export const UNCATEGORIZED_PANEL_ID = 'dataset-selector-uncategorized-panel';
|
||||
export const UNCATEGORIZED_TAB_ID = 'dataset-selector-uncategorized-tab';
|
||||
export const DATA_VIEWS_PANEL_ID = 'dataset-selector-data-views-panel';
|
||||
export const DATA_VIEWS_TAB_ID = 'dataset-selector-data-views-tab';
|
||||
|
||||
export const DATA_VIEW_POPOVER_CONTENT_WIDTH = 300;
|
||||
export const DATA_VIEW_POPOVER_CONTENT_WIDTH = 400;
|
||||
|
||||
export const showAllLogsLabel = i18n.translate('xpack.logExplorer.datasetSelector.showAllLogs', {
|
||||
defaultMessage: 'Show all logs',
|
||||
|
@ -28,6 +30,14 @@ export const uncategorizedLabel = i18n.translate(
|
|||
{ defaultMessage: 'Uncategorized' }
|
||||
);
|
||||
|
||||
export const dataViewsLabel = i18n.translate('xpack.logExplorer.datasetSelector.dataViews', {
|
||||
defaultMessage: 'Data Views',
|
||||
});
|
||||
|
||||
export const openDiscoverLabel = i18n.translate('xpack.logExplorer.datasetSelector.openDiscover', {
|
||||
defaultMessage: 'Opens in Discover',
|
||||
});
|
||||
|
||||
export const sortOrdersLabel = i18n.translate('xpack.logExplorer.datasetSelector.sortOrders', {
|
||||
defaultMessage: 'Sort directions',
|
||||
});
|
||||
|
@ -43,6 +53,17 @@ export const noDatasetsDescriptionLabel = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const noDataViewsLabel = i18n.translate('xpack.logExplorer.datasetSelector.noDataViews', {
|
||||
defaultMessage: 'No data views found',
|
||||
});
|
||||
|
||||
export const noDataViewsDescriptionLabel = i18n.translate(
|
||||
'xpack.logExplorer.datasetSelector.noDataViewsDescription',
|
||||
{
|
||||
defaultMessage: 'No data views or search results found.',
|
||||
}
|
||||
);
|
||||
|
||||
export const noIntegrationsLabel = i18n.translate(
|
||||
'xpack.logExplorer.datasetSelector.noIntegrations',
|
||||
{ defaultMessage: 'No integrations found' }
|
||||
|
|
|
@ -11,6 +11,7 @@ import React, { useState } from 'react';
|
|||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import type { Meta, Story } from '@storybook/react';
|
||||
import { IndexPattern } from '@kbn/io-ts-utils';
|
||||
import { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import {
|
||||
AllDatasetSelection,
|
||||
DatasetSelection,
|
||||
|
@ -29,6 +30,10 @@ const meta: Meta<typeof DatasetSelector> = {
|
|||
options: [null, { message: 'Failed to fetch data streams' }],
|
||||
control: { type: 'radio' },
|
||||
},
|
||||
dataViewsError: {
|
||||
options: [null, { message: 'Failed to fetch data data views' }],
|
||||
control: { type: 'radio' },
|
||||
},
|
||||
integrationsError: {
|
||||
options: [null, { message: 'Failed to fetch data integrations' }],
|
||||
control: { type: 'radio' },
|
||||
|
@ -71,20 +76,39 @@ const DatasetSelectorTemplate: Story<DatasetSelectorProps> = (args) => {
|
|||
|
||||
const sortedDatasets = search.sortOrder === 'asc' ? filteredDatasets : filteredDatasets.reverse();
|
||||
|
||||
const filteredDataViews = mockDataViews.filter((dataView) =>
|
||||
dataView.name?.includes(search.name as string)
|
||||
);
|
||||
|
||||
const sortedDataViews =
|
||||
search.sortOrder === 'asc' ? filteredDataViews : filteredDataViews.reverse();
|
||||
|
||||
const {
|
||||
datasetsError,
|
||||
dataViewsError,
|
||||
integrationsError,
|
||||
isLoadingDataViews,
|
||||
isLoadingIntegrations,
|
||||
isLoadingUncategorized,
|
||||
} = args;
|
||||
|
||||
return (
|
||||
<DatasetSelector
|
||||
{...args}
|
||||
datasets={sortedDatasets}
|
||||
datasets={Boolean(datasetsError || isLoadingUncategorized) ? [] : sortedDatasets}
|
||||
dataViews={Boolean(dataViewsError || isLoadingDataViews) ? [] : sortedDataViews}
|
||||
datasetSelection={datasetSelection}
|
||||
integrations={sortedIntegrations}
|
||||
integrations={Boolean(integrationsError || isLoadingIntegrations) ? [] : sortedIntegrations}
|
||||
onDataViewsSearch={setSearch}
|
||||
onDataViewsSort={setSearch}
|
||||
onIntegrationsLoadMore={onIntegrationsLoadMore}
|
||||
onIntegrationsSearch={setSearch}
|
||||
onIntegrationsSort={setSearch}
|
||||
onIntegrationsStreamsSearch={setSearch}
|
||||
onIntegrationsStreamsSort={setSearch}
|
||||
onSelectionChange={onSelectionChange}
|
||||
onUnmanagedStreamsSearch={setSearch}
|
||||
onUnmanagedStreamsSort={setSearch}
|
||||
onUncategorizedSearch={setSearch}
|
||||
onUncategorizedSort={setSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -92,12 +116,18 @@ const DatasetSelectorTemplate: Story<DatasetSelectorProps> = (args) => {
|
|||
export const Basic = DatasetSelectorTemplate.bind({});
|
||||
Basic.args = {
|
||||
datasetsError: null,
|
||||
dataViewsError: null,
|
||||
integrationsError: null,
|
||||
isLoadingDataViews: false,
|
||||
isLoadingIntegrations: false,
|
||||
isLoadingStreams: false,
|
||||
isLoadingUncategorized: false,
|
||||
isSearchingIntegrations: false,
|
||||
onDataViewsReload: () => alert('Reload data views...'),
|
||||
onDataViewSelection: (dataView) => alert(`Navigate to data view "${dataView.name}"`),
|
||||
onDataViewsTabClick: () => console.log('Load data views...'),
|
||||
onIntegrationsReload: () => alert('Reload integrations...'),
|
||||
onStreamsEntryClick: () => console.log('Load uncategorized streams...'),
|
||||
onUnmanagedStreamsReload: () => alert('Reloading streams...'),
|
||||
onUncategorizedTabClick: () => console.log('Load uncategorized streams...'),
|
||||
onUncategorizedReload: () => alert('Reloading streams...'),
|
||||
};
|
||||
|
||||
const mockIntegrations: Integration[] = [
|
||||
|
@ -477,3 +507,25 @@ const mockDatasets: Dataset[] = [
|
|||
{ name: 'data-load-balancing-logs-*' as IndexPattern },
|
||||
{ name: 'data-scaling-logs-*' as IndexPattern },
|
||||
].map((dataset) => Dataset.create(dataset));
|
||||
|
||||
const mockDataViews: DataViewListItem[] = [
|
||||
{
|
||||
id: 'logs-*',
|
||||
namespaces: ['default'],
|
||||
title: 'logs-*',
|
||||
name: 'logs-*',
|
||||
},
|
||||
{
|
||||
id: 'metrics-*',
|
||||
namespaces: ['default'],
|
||||
title: 'metrics-*',
|
||||
name: 'metrics-*',
|
||||
},
|
||||
{
|
||||
id: '7258d186-6430-4b51-bb67-2603cdfb4652',
|
||||
namespaces: ['default'],
|
||||
title: 'synthetics-*',
|
||||
typeMeta: {},
|
||||
name: 'synthetics-dashboard',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -10,6 +10,9 @@ import styled from '@emotion/styled';
|
|||
import { EuiContextMenu, EuiHorizontalRule, EuiTab, EuiTabs } from '@elastic/eui';
|
||||
import { useIntersectionRef } from '../../hooks/use_intersection_ref';
|
||||
import {
|
||||
dataViewsLabel,
|
||||
DATA_VIEWS_PANEL_ID,
|
||||
DATA_VIEWS_TAB_ID,
|
||||
DATA_VIEW_POPOVER_CONTENT_WIDTH,
|
||||
integrationsLabel,
|
||||
INTEGRATIONS_PANEL_ID,
|
||||
|
@ -25,19 +28,30 @@ import { SelectorActions } from './sub_components/selector_actions';
|
|||
import { DatasetSelectorProps } from './types';
|
||||
import {
|
||||
buildIntegrationsTree,
|
||||
createDataViewsStatusItem,
|
||||
createIntegrationStatusItem,
|
||||
createUncategorizedStatusItem,
|
||||
} from './utils';
|
||||
import { getDataViewTestSubj } from '../../utils/get_data_view_test_subj';
|
||||
import { DataViewsPanelTitle } from './sub_components/data_views_panel_title';
|
||||
|
||||
export function DatasetSelector({
|
||||
datasets,
|
||||
datasetsError,
|
||||
datasetSelection,
|
||||
datasetsError,
|
||||
dataViews,
|
||||
dataViewsError,
|
||||
integrations,
|
||||
integrationsError,
|
||||
isLoadingDataViews,
|
||||
isLoadingIntegrations,
|
||||
isLoadingStreams,
|
||||
isLoadingUncategorized,
|
||||
isSearchingIntegrations,
|
||||
onDataViewSelection,
|
||||
onDataViewsReload,
|
||||
onDataViewsSearch,
|
||||
onDataViewsSort,
|
||||
onDataViewsTabClick,
|
||||
onIntegrationsLoadMore,
|
||||
onIntegrationsReload,
|
||||
onIntegrationsSearch,
|
||||
|
@ -45,10 +59,10 @@ export function DatasetSelector({
|
|||
onIntegrationsStreamsSearch,
|
||||
onIntegrationsStreamsSort,
|
||||
onSelectionChange,
|
||||
onStreamsEntryClick,
|
||||
onUnmanagedStreamsReload,
|
||||
onUnmanagedStreamsSearch,
|
||||
onUnmanagedStreamsSort,
|
||||
onUncategorizedReload,
|
||||
onUncategorizedSearch,
|
||||
onUncategorizedSort,
|
||||
onUncategorizedTabClick,
|
||||
}: DatasetSelectorProps) {
|
||||
const {
|
||||
panelId,
|
||||
|
@ -62,21 +76,26 @@ export function DatasetSelector({
|
|||
searchByName,
|
||||
selectAllLogDataset,
|
||||
selectDataset,
|
||||
selectDataView,
|
||||
sortByOrder,
|
||||
switchToIntegrationsTab,
|
||||
switchToUncategorizedTab,
|
||||
switchToDataViewsTab,
|
||||
togglePopover,
|
||||
} = useDatasetSelector({
|
||||
initialContext: { selection: datasetSelection },
|
||||
onDataViewSelection,
|
||||
onDataViewsSearch,
|
||||
onDataViewsSort,
|
||||
onIntegrationsLoadMore,
|
||||
onIntegrationsReload,
|
||||
onIntegrationsSearch,
|
||||
onIntegrationsSort,
|
||||
onIntegrationsStreamsSearch,
|
||||
onIntegrationsStreamsSort,
|
||||
onUnmanagedStreamsSearch,
|
||||
onUnmanagedStreamsSort,
|
||||
onUnmanagedStreamsReload,
|
||||
onUncategorizedSearch,
|
||||
onUncategorizedSort,
|
||||
onUncategorizedReload,
|
||||
onSelectionChange,
|
||||
});
|
||||
|
||||
|
@ -117,8 +136,8 @@ export function DatasetSelector({
|
|||
createUncategorizedStatusItem({
|
||||
data: datasets,
|
||||
error: datasetsError,
|
||||
isLoading: isLoadingStreams,
|
||||
onRetry: onUnmanagedStreamsReload,
|
||||
isLoading: isLoadingUncategorized,
|
||||
onRetry: onUncategorizedReload,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
@ -127,7 +146,26 @@ export function DatasetSelector({
|
|||
name: dataset.title,
|
||||
onClick: () => selectDataset(dataset),
|
||||
}));
|
||||
}, [datasets, datasetsError, isLoadingStreams, selectDataset, onUnmanagedStreamsReload]);
|
||||
}, [datasets, datasetsError, isLoadingUncategorized, selectDataset, onUncategorizedReload]);
|
||||
|
||||
const dataViewsItems = useMemo(() => {
|
||||
if (!dataViews || dataViews.length === 0) {
|
||||
return [
|
||||
createDataViewsStatusItem({
|
||||
data: dataViews,
|
||||
error: dataViewsError,
|
||||
isLoading: isLoadingDataViews,
|
||||
onRetry: onDataViewsReload,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
return dataViews.map((dataView) => ({
|
||||
'data-test-subj': getDataViewTestSubj(dataView.title),
|
||||
name: dataView.name,
|
||||
onClick: () => selectDataView(dataView),
|
||||
}));
|
||||
}, [dataViews, dataViewsError, isLoadingDataViews, selectDataView, onDataViewsReload]);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
|
@ -140,11 +178,20 @@ export function DatasetSelector({
|
|||
id: UNCATEGORIZED_TAB_ID,
|
||||
name: uncategorizedLabel,
|
||||
onClick: () => {
|
||||
onStreamsEntryClick(); // Lazy-load uncategorized datasets only when accessing the Uncategorized tab
|
||||
onUncategorizedTabClick(); // Lazy-load uncategorized datasets only when accessing the Uncategorized tab
|
||||
switchToUncategorizedTab();
|
||||
},
|
||||
'data-test-subj': 'datasetSelectorUncategorizedTab',
|
||||
},
|
||||
{
|
||||
id: DATA_VIEWS_TAB_ID,
|
||||
name: dataViewsLabel,
|
||||
onClick: () => {
|
||||
onDataViewsTabClick(); // Lazy-load data views only when accessing the Data Views tab
|
||||
switchToDataViewsTab();
|
||||
},
|
||||
'data-test-subj': 'datasetSelectorDataViewsTab',
|
||||
},
|
||||
];
|
||||
|
||||
const tabEntries = tabs.map((tab) => (
|
||||
|
@ -175,7 +222,7 @@ export function DatasetSelector({
|
|||
search={search}
|
||||
onSearch={searchByName}
|
||||
onSort={sortByOrder}
|
||||
isLoading={isSearchingIntegrations || isLoadingStreams}
|
||||
isLoading={isSearchingIntegrations || isLoadingUncategorized}
|
||||
/>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
{/* For a smoother user experience, we keep each tab content mount and we only show the select one
|
||||
|
@ -215,6 +262,22 @@ export function DatasetSelector({
|
|||
data-test-subj="uncategorizedContextMenu"
|
||||
size="s"
|
||||
/>
|
||||
{/* Data views tab content */}
|
||||
<ContextMenu
|
||||
hidden={tabId !== DATA_VIEWS_TAB_ID}
|
||||
initialPanelId={DATA_VIEWS_PANEL_ID}
|
||||
panels={[
|
||||
{
|
||||
id: DATA_VIEWS_PANEL_ID,
|
||||
title: <DataViewsPanelTitle />,
|
||||
width: DATA_VIEW_POPOVER_CONTENT_WIDTH,
|
||||
items: dataViewsItems,
|
||||
},
|
||||
]}
|
||||
className="eui-yScroll"
|
||||
data-test-subj="dataViewsContextMenu"
|
||||
size="s"
|
||||
/>
|
||||
</DatasetsPopover>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { actions, assign, createMachine, raise } from 'xstate';
|
||||
import { AllDatasetSelection, SingleDatasetSelection } from '../../../../common/dataset_selection';
|
||||
import { INTEGRATIONS_TAB_ID, UNCATEGORIZED_TAB_ID } from '../constants';
|
||||
import { DATA_VIEWS_TAB_ID, INTEGRATIONS_TAB_ID, UNCATEGORIZED_TAB_ID } from '../constants';
|
||||
import { defaultSearch, DEFAULT_CONTEXT } from './defaults';
|
||||
import {
|
||||
DatasetsSelectorContext,
|
||||
|
@ -20,7 +20,7 @@ import {
|
|||
export const createPureDatasetsSelectorStateMachine = (
|
||||
initialContext: Partial<DefaultDatasetsSelectorContext> = DEFAULT_CONTEXT
|
||||
) =>
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVsztgZTABswBjdAewCcA6AB3PoDcwaTDzsIBiAFQHkA4gIAyAUQDaABgC6iUPVgBLdIvIA7OSAAeiAJwBmXdUkAWSZIAcAJgBs+gIy6A7PpsAaEAE9E+q-eoW+k5OVuY24ZJONgC+0R5omNi4BMRkVHQM5Mw0DGBqXADCwnx4EjKaCsqqGkjaen7UAKySjVYONoEmFk66Ht4IjU4m1B36Y4G6k7omVrHxGFg4+ESkFDT0TCzUufn8QmJSsrWVKuqaOgi69o3UTpZWznaSuoONfYitN5K29k6tNj1nvY5iAEotkis0utMtltrQ8lxSmICjwAPoAQWEwlRxQEeFRyHRPHRpR4hwqHCqZ1qFws3Wo+kkNkaFnsJhs90a+neCBs7OoVgeJl0NnsdmuJhBYKSy1Sawymxy8LU1EUanQYCglAw1VgPFQACNEQB1ACSPAKAAlUfxUQBVAByBSJogEfAASqaAFqiZA29EAIXJx0ppxqoAu9meDMawqszUsjRFvS8iHsUf8kRMUVCjXsrn0jSlCxlKVW6Q2WS2O1V6s12rDesN1EIilgKjUUFNda1OvUsEKlvRDoEolRAAVh6JhMH5KHquc09HbPGuq4bLoLEyLDy-v4XoY41FvvYLMXEksy1CFVWlXlaxre439QaW22O12ew3dYiCu6+FiNp8KipoOjwrrukSpp8A6+IBnwPD8AAsrOIAnAuNKIAAtIKNyDPoJgOOyhanim-SDFYIxXAY+F2H4MRxKCJaXpC8qVrCNZqo+379i+b7tmqn7cX2agDqU6LulaqIBgAmqiDrokhZRHHOShhouCBYYRXw2H4FgmAmQQGSYPJ8kY8YdFyTgWACTjAox0osXKFYwtWyoPvWIlNq+rYCZ23bCY2iIemismoh6yCiO6qHodSEbYQR+hNFY7IhD0fgSjyFiNHh3yRCKorOEE57grK5bQoqcL3lxnnPs2vkfgFtXVHg6CUGAqAALYDlaw6jhOU4zuUIZqRh8UIFG5m6bGgThJuTJvKmCB3Jmop-FZoRBAx8wXhCzkVbeVUqjVT66nxDWCU1p3qK17VdWJogSVJYUKUpMXznFdQDKeIzNPo+khLZrQ8jl5nXARG5BNMhglaWrEuZVnFfl553vpdyNhrdHXdcF7qhXJEVRe9o2fRcbROMYFiTA8jgivpVgg601DTOmfhtHSfKzA5zF7eVN4ce5J08aJqN+UJzU3W12MPciaKEsSpLE1S4ZfUm-ispMfJjJIjLTLu-3UIRALfM06a6dtTG7WV17sW594AK5qCQGCalQigAF6QC+JrmlJtqgeBAiQTw0Gwf6QbDapysaVpki3ARYRRi4KV8u4S1RgZxhCs03QuACsNOXztt3iqjvO4+buexA3viZJ1ovYpykUiTKsXDhQSG40zIhOTOV0Ty9hWNZ1AZ78035nmBe8zbrkl9QZcu1Alde4auP4+F7qRdFkdoR9rfYYKRjMqegLZmKXI7unzw3FYVMvDZOX5n8U-W2xs9HfPTuL8v1er0iogogJESEkogyQ71ivvBAVM46xlGBMW+LQyJphSkYYIhFBQpUFAZF+V55TYDlNUagShOzEERNOABaJMTYlxPieWICwEqV3i3GOjgkqrkwWMOwhE6QDxcMMdkJgJ6xlsOEHB8MaD4NWIQ4hUBSH-0AXQxW4C94xy5HHUwkQOh0QeGnfoYp+TZWzMbVcLIzzcytrg9IkiyCENQIQQgZDZZAIVqApW6lMKaXPgEfSSYuh+GcLGAeHJhgckmCYcJrgZimFiIxNQ5AIBwE0I5aeaxm7Rw8VhXSFMmR6QMuYIyi1+guGZmMKyx5HC-H0GI-a-MWBpPceNLCgwjA5NPHknWQxClplFIbUp-0hiMhSuyapRd35sA4JAepY0vqDyjDGeM0NdA00mDyXw6sgghDCBEKIIyZ6I2VFM0m2EuSrVyYZTpPIUoWBKYWQwIQWSDF+Lst++z7wAAt3yHMgQCa5m54y-EqSlEIqzXC9MLGYO4Nk77PIRodJGgUzqGi+THNcxhdJtPOcZHkgjrnhPTKeawe4+QwoOgLaqGNEWvg+e2ZFHibJotvnYXS24dYMyWkspKXRB4GXFIRHWJLalzyFijeqaN-IUv7LSxpoQbDGCjFTQRllsrxlMkMEeeTrKGE3GfAVxcP7Crqj5MV4trpqCxvdKVX0HiUR1kmEIXRbUslMnSEpAJu4cj+roXV78awLwrpQD2K8DSWouIIgUkgxQihcCyc2TgB6hDjkPQi-1mh2QMlU8xpVLESNYtMiBqjh4aKiDZXwOiB55gppNaG6aWTBAFdYsMRDBLEBDQlYUAoNxUyiDlQI3J05RCSmMQwZsDCuFsPW3N6hqB2MIK2ia1wR6xh6FGHt4MTL9ruMzX48YHhEucDE6IQA */
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVsztgZTABswBjdAewCcA6AB3PoDcwaTDzsIBiAFQHkA4gIAyAUQDaABgC6iUPVgBLdIvIA7OSAAeiAIy6ATAGZqBgBwAWMwHYzdgwE4zD6wBoQAT0RGj16gDYzIwBWKwdJC2CDawMAX1j3NExsXAJiMio6BnJmGgYwNS4AYWE+PAkZTQVlVQ0kbUQbP0kDf0l-IwddSUkXYPcvBH99ah6jNqMzFrMDXSN4xIwsHHwiUgoaeiYWanzC-iExKVl66pV1TR0EO11qI0MLDosLXWsHWYHEUOCAmJ6u3QWBxWawLEBJZapNYZTbZXK7WgFLjlMRFHgAfQAgsJhOjSgI8OjkJieJjyjxjlUODULvUrmZ-H5rLopp0jO1rMEHP5PkMZndOT5JJ1ggy4glwUsUqt0hssts8oi1NRFGp0GAoJQMLVYDxUAAjZEAdQAkjwigAJdH8dEAVQAckUSaIBHwAEomgBaomQ1sxACFKadqec6qArgBaXT+H4GZ4RSS6YIxfwWSRuTx6OaSajWfwGVrRcIRIzixbJFZpdaZLY5HZ7FVqjVa0O6g3Gs2W618IkkzHogBqJtERsJpMDlWDSlDl0QswM1AsrTTDnuSZFFl5+nZufzxmeMSTorBEOlVZh8rrioKjfVmu16jb+uohEUsBUaigJqb99bxQtmL2gIojogACoBojCEG8ghrUs4IN0DimPmoRBP4-hOO0Zi8pytwOMEnRAtEbSzGYJ5SpW0JyrW8INqqd4tjqerPq+76ql+P6MY+yJFG6fA4t26ImvaPAum6JImnw9qEv6fA8PwACy0EgGccF0ogEYFj8yZGBY9yPCELIOLyyYLhhuirjpHSzP45EVlCso1nC9ZKrezYPmoT4vm+H4cQxHmwMioiYm6Xb+gAmui9qYgpFQnDB05qeGGl6T87SkZEYzWJEm6ZkMQKmMEgQEbYjLMnZkIytWsIKgiN70e5rbMd5bGft+-l-ng7oYhF6Lusgohuspqm0slCARrpJhRI8MRvLMSa5YMZjBNpLTptywwuL4FVnlRTm1XRnEBc1rG+e1jW1Hg6CUGAqAALaBZagHAWBEFQZOCU0mGDQIb0yEGKh4wYVMMY4YmozDJyJUGMKeY7ZRjk1VedXKg1v5MQaLVnUdoZXTd92BeUIVhZF0WxcNsGjT9wQsgEwTCpYKbWJyBgmVypjro8q5vHpDjww51WXrRrlo1xnknT57Hnej6h47dD3It16K9f1g0U4lVNXMYzTOO8XQONylis3lK0LsC+jzpMDJLvzVUXjRLn1TjGMsZLbXO7L13y4TkGiGivakuS6tffBXK3GYFncnp7LssCOGTIu4zWC09P6PmtkSqeCOCw717KgArmoJAYBqVCKAAXpAzEduaVo2sJokCOJPCSdJfoTvFKmU99kaOEhpGTIKdhdHmW7tCYJHjFM7TRrMtvntRzl59QhfF3eZeVxA1d4KateCcSpKDsOo7t8HM7qQgcYBOncYMxZRXWEYW6OD8BszEuq4uBYtjz3tSPCzeVeJcoAbyru2ImoUrS9TJnFKkGse4pSmAEKYBF0IFmyvhLcpYLDUFeMnYE7ReaMl-ojIWjsC5F2AaAre4ClYqzdANIaH0u7wPghGb+Zk8z5lmrHem-Q8rbn8NQIq612TrhjPMTOFEBb2yXijFelD16UArmAw0KI-YYgPmSUQFJmEjQQeNOMOC5hxnpq0IqrQtx6SEamF4S5SKOAsCQnOciGwQCWAORQYAADuT4a5dnriJMSEkpJjgDGfJKP1xhm3vn0bknRWRj2TMImGQQ0yhEMLoZxsiDquXcZgTxPi-E707HXHsDonSN3dF6H0p89Hd3goCO4bRIjJ2TjTFakhsICNMYuQIQ8lyzwzuWSqC99rIzcR4rxvjt7BUgcrUmMVYFThDhfYYOZ3hWEyT0FanIsGODuFMecycZhRDLJKeydtF65JvPk1AhSZm0LdD1SKqsmGd30fBdZdxHiBANsCTk7wtxzFuC8NBBskwuEcNk65Ey8lTKKbM1Emi+xHxHBEzWiAR4cycARLky0IiLT0HGBcekZiWFCOyGYMLMjYFlLUagShPzECCsirEAl8SEi0UHeprCL5RlXKMdk5hOhf0mO0LczMmR8OBPhdkjwnFSMuWMmgdL1gMqZVAFl6j-bcp0RigxEYohIQjoEbojgfDhGNoMaM4RcEvALKmEIQQug0tVVRBlqBCCEFZRogO2jdEfIafy6MPx0Lj1TEEdo3Itypj8M4VMDNHjT1BGCNQ5AIBwE0FnGRGw4GrLGhGbhEMMr01hpEeO1BVwhF8HmRMI9ghurIZQfN59C3JiQulFkmVy38JtcMRONabB6RaM8PmSrRl-2bdQNgHBICtsiVcNOJhojpheJZHwHw8pLhweMXwqZzB6Qtk23OKMF2YvGgRW4XaKVZQrXlOMZgq0+AIlC0UpkT2uNcgACx8uegxjIn1OABq8V42CYi8h8EI6OoR0xdL+eOkZu1SGnsOh1F2-62FWBMDentvgcq8heE+543RcLjEkFEVNSHs45LhU7dDj5mq-vfJhi+zNcE03Se0IqcwgS8neBPRMsMxHpjhhO5DLibmow9uLTGp0pYyfgCsttP1NLtFGN0ZwYLlrLQBryRkxjMq2FFYzLJ4maOwoAdJhjsnXatT8hdT2+MHqsbGo4BcwouQxCsJ50U+m7DPsZDGOt9NOifqkwotepdlGb2Yq51T5ghERzZAe5MGFME9NpuYA2HR8KwcfuFujyo7kPKfPFq4pZbjDCBCyZaPn8zPyTFWwE+EpjW2FE2tVZBImfP5cmeN0YI4tGrVarcIi7jxJBN-EFVGLmTtIV10MjL2LEHKxpXSnbIif3uBhaI0YtwgwCDDXw+hy3QvM7m2lHr1DUC9YQNbCEmu+GHZ-Dh+g+16Ajjg47zJjDLU6ZI+IQA */
|
||||
createMachine<DatasetsSelectorContext, DatasetsSelectorEvent, DatasetsSelectorTypestate>(
|
||||
{
|
||||
context: { ...DEFAULT_CONTEXT, ...initialContext },
|
||||
|
@ -55,6 +55,7 @@ export const createPureDatasetsSelectorStateMachine = (
|
|||
entry: ['storeIntegrationsTabId'],
|
||||
on: {
|
||||
SWITCH_TO_UNCATEGORIZED_TAB: 'uncategorizedTab',
|
||||
SWITCH_TO_DATA_VIEWS_TAB: 'dataViewsTab',
|
||||
},
|
||||
states: {
|
||||
hist: {
|
||||
|
@ -106,15 +107,37 @@ export const createPureDatasetsSelectorStateMachine = (
|
|||
],
|
||||
on: {
|
||||
SWITCH_TO_INTEGRATIONS_TAB: 'integrationsTab.hist',
|
||||
SWITCH_TO_DATA_VIEWS_TAB: 'dataViewsTab',
|
||||
SEARCH_BY_NAME: {
|
||||
actions: ['storeSearch', 'searchUnmanagedStreams'],
|
||||
actions: ['storeSearch', 'searchUncategorized'],
|
||||
},
|
||||
SORT_BY_ORDER: {
|
||||
actions: ['storeSearch', 'sortUnmanagedStreams'],
|
||||
actions: ['storeSearch', 'sortUncategorized'],
|
||||
},
|
||||
SELECT_DATASET: '#closed',
|
||||
},
|
||||
},
|
||||
dataViewsTab: {
|
||||
entry: [
|
||||
'storeDataViewsTabId',
|
||||
'retrieveSearchFromCache',
|
||||
'maybeRestoreSearchResult',
|
||||
],
|
||||
on: {
|
||||
SWITCH_TO_INTEGRATIONS_TAB: 'integrationsTab.hist',
|
||||
SWITCH_TO_UNCATEGORIZED_TAB: 'uncategorizedTab',
|
||||
SEARCH_BY_NAME: {
|
||||
actions: ['storeSearch', 'searchDataViews'],
|
||||
},
|
||||
SORT_BY_ORDER: {
|
||||
actions: ['storeSearch', 'sortDataViews'],
|
||||
},
|
||||
SELECT_DATA_VIEW: {
|
||||
target: '#closed',
|
||||
actions: ['selectDataView'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -149,12 +172,13 @@ export const createPureDatasetsSelectorStateMachine = (
|
|||
actions: {
|
||||
storeIntegrationsTabId: assign((_context) => ({ tabId: INTEGRATIONS_TAB_ID })),
|
||||
storeUncategorizedTabId: assign((_context) => ({ tabId: UNCATEGORIZED_TAB_ID })),
|
||||
storeDataViewsTabId: assign((_context) => ({ tabId: DATA_VIEWS_TAB_ID })),
|
||||
storePanelId: assign((_context, event) =>
|
||||
'panelId' in event ? { panelId: event.panelId } : {}
|
||||
),
|
||||
storeSearch: assign((context, event) => {
|
||||
if ('search' in event) {
|
||||
const id = context.tabId === UNCATEGORIZED_TAB_ID ? context.tabId : context.panelId;
|
||||
const id = context.tabId === INTEGRATIONS_TAB_ID ? context.panelId : context.tabId;
|
||||
context.searchCache.set(id, event.search);
|
||||
|
||||
return {
|
||||
|
@ -179,6 +203,9 @@ export const createPureDatasetsSelectorStateMachine = (
|
|||
if (event.type === 'SWITCH_TO_UNCATEGORIZED_TAB' && 'tabId' in context) {
|
||||
return { search: context.searchCache.get(context.tabId) ?? defaultSearch };
|
||||
}
|
||||
if (event.type === 'SWITCH_TO_DATA_VIEWS_TAB' && 'tabId' in context) {
|
||||
return { search: context.searchCache.get(context.tabId) ?? defaultSearch };
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
maybeRestoreSearchResult: actions.pure((context, event) => {
|
||||
|
@ -188,8 +215,15 @@ export const createPureDatasetsSelectorStateMachine = (
|
|||
event.type === 'SWITCH_TO_INTEGRATIONS_TAB' && context.searchCache.has(context.panelId);
|
||||
const hasSearchOnUncategorizedTab =
|
||||
event.type === 'SWITCH_TO_UNCATEGORIZED_TAB' && context.searchCache.has(context.tabId);
|
||||
const hasSearchOnDataViewsTab =
|
||||
event.type === 'SWITCH_TO_DATA_VIEWS_TAB' && context.searchCache.has(context.tabId);
|
||||
|
||||
if (hasSearchOnChangePanel || hasSearchOnIntegrationsTab || hasSearchOnUncategorizedTab) {
|
||||
if (
|
||||
hasSearchOnChangePanel ||
|
||||
hasSearchOnIntegrationsTab ||
|
||||
hasSearchOnUncategorizedTab ||
|
||||
hasSearchOnDataViewsTab
|
||||
) {
|
||||
return raise({ type: 'SORT_BY_ORDER', search: context.search });
|
||||
}
|
||||
}),
|
||||
|
@ -199,16 +233,19 @@ export const createPureDatasetsSelectorStateMachine = (
|
|||
|
||||
export const createDatasetsSelectorStateMachine = ({
|
||||
initialContext,
|
||||
onDataViewSelection,
|
||||
onDataViewsSearch,
|
||||
onDataViewsSort,
|
||||
onIntegrationsLoadMore,
|
||||
onIntegrationsReload,
|
||||
onIntegrationsSearch,
|
||||
onIntegrationsSort,
|
||||
onIntegrationsStreamsSearch,
|
||||
onIntegrationsStreamsSort,
|
||||
onUnmanagedStreamsSearch,
|
||||
onUnmanagedStreamsSort,
|
||||
onUncategorizedSearch,
|
||||
onUncategorizedSort,
|
||||
onSelectionChange,
|
||||
onUnmanagedStreamsReload,
|
||||
onUncategorizedReload,
|
||||
}: DatasetsSelectorStateMachineDependencies) =>
|
||||
createPureDatasetsSelectorStateMachine(initialContext).withConfig({
|
||||
actions: {
|
||||
|
@ -217,7 +254,12 @@ export const createDatasetsSelectorStateMachine = ({
|
|||
},
|
||||
loadMoreIntegrations: onIntegrationsLoadMore,
|
||||
relaodIntegrations: onIntegrationsReload,
|
||||
reloadUnmanagedStreams: onUnmanagedStreamsReload,
|
||||
reloadUncategorized: onUncategorizedReload,
|
||||
selectDataView: (_context, event) => {
|
||||
if (event.type === 'SELECT_DATA_VIEW' && 'dataView' in event) {
|
||||
return onDataViewSelection(event.dataView);
|
||||
}
|
||||
},
|
||||
// Search actions
|
||||
searchIntegrations: (_context, event) => {
|
||||
if ('search' in event) {
|
||||
|
@ -229,6 +271,16 @@ export const createDatasetsSelectorStateMachine = ({
|
|||
onIntegrationsSort(event.search);
|
||||
}
|
||||
},
|
||||
searchDataViews: (context, event) => {
|
||||
if ('search' in event) {
|
||||
onDataViewsSearch(event.search);
|
||||
}
|
||||
},
|
||||
sortDataViews: (context, event) => {
|
||||
if ('search' in event) {
|
||||
onDataViewsSort(event.search);
|
||||
}
|
||||
},
|
||||
searchIntegrationsStreams: (context, event) => {
|
||||
if ('search' in event) {
|
||||
onIntegrationsStreamsSearch({ ...event.search, integrationId: context.panelId });
|
||||
|
@ -239,14 +291,14 @@ export const createDatasetsSelectorStateMachine = ({
|
|||
onIntegrationsStreamsSort({ ...event.search, integrationId: context.panelId });
|
||||
}
|
||||
},
|
||||
searchUnmanagedStreams: (_context, event) => {
|
||||
searchUncategorized: (_context, event) => {
|
||||
if ('search' in event) {
|
||||
onUnmanagedStreamsSearch(event.search);
|
||||
onUncategorizedSearch(event.search);
|
||||
}
|
||||
},
|
||||
sortUnmanagedStreams: (_context, event) => {
|
||||
sortUncategorized: (_context, event) => {
|
||||
if ('search' in event) {
|
||||
onUnmanagedStreamsSort(event.search);
|
||||
onUncategorizedSort(event.search);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,7 +4,13 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { DatasetSelection, DatasetSelectionChange } from '../../../../common/dataset_selection';
|
||||
import { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import { SearchDataViews } from '../../../hooks/use_data_views';
|
||||
import {
|
||||
DatasetSelection,
|
||||
DatasetSelectionChange,
|
||||
DataViewSelection,
|
||||
} from '../../../../common/dataset_selection';
|
||||
import { Dataset } from '../../../../common/datasets/models/dataset';
|
||||
import { ReloadDatasets, SearchDatasets } from '../../../hooks/use_datasets';
|
||||
import {
|
||||
|
@ -56,6 +62,10 @@ export type DatasetsSelectorTypestate =
|
|||
value: 'popover.open.uncategorizedTab';
|
||||
context: DefaultDatasetsSelectorContext;
|
||||
}
|
||||
| {
|
||||
value: 'popover.open.dataViewsTab';
|
||||
context: DefaultDatasetsSelectorContext;
|
||||
}
|
||||
| {
|
||||
value: 'selection';
|
||||
context: DefaultDatasetsSelectorContext;
|
||||
|
@ -84,6 +94,9 @@ export type DatasetsSelectorEvent =
|
|||
| {
|
||||
type: 'SWITCH_TO_UNCATEGORIZED_TAB';
|
||||
}
|
||||
| {
|
||||
type: 'SWITCH_TO_DATA_VIEWS_TAB';
|
||||
}
|
||||
| {
|
||||
type: 'CHANGE_PANEL';
|
||||
panelId: PanelId;
|
||||
|
@ -92,6 +105,10 @@ export type DatasetsSelectorEvent =
|
|||
type: 'SELECT_DATASET';
|
||||
dataset: Dataset;
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_DATA_VIEW';
|
||||
dataView: DataViewListItem;
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_ALL_LOGS_DATASET';
|
||||
}
|
||||
|
@ -109,6 +126,9 @@ export type DatasetsSelectorEvent =
|
|||
|
||||
export interface DatasetsSelectorStateMachineDependencies {
|
||||
initialContext?: Partial<DefaultDatasetsSelectorContext>;
|
||||
onDataViewSelection: DataViewSelection;
|
||||
onDataViewsSearch: SearchDataViews;
|
||||
onDataViewsSort: SearchDataViews;
|
||||
onIntegrationsLoadMore: LoadMoreIntegrations;
|
||||
onIntegrationsReload: ReloadIntegrations;
|
||||
onIntegrationsSearch: SearchIntegrations;
|
||||
|
@ -116,7 +136,7 @@ export interface DatasetsSelectorStateMachineDependencies {
|
|||
onIntegrationsStreamsSearch: SearchIntegrations;
|
||||
onIntegrationsStreamsSort: SearchIntegrations;
|
||||
onSelectionChange: DatasetSelectionChange;
|
||||
onUnmanagedStreamsReload: ReloadDatasets;
|
||||
onUnmanagedStreamsSearch: SearchDatasets;
|
||||
onUnmanagedStreamsSort: SearchDatasets;
|
||||
onUncategorizedReload: ReloadDatasets;
|
||||
onUncategorizedSearch: SearchDatasets;
|
||||
onUncategorizedSort: SearchDatasets;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
ChangePanelHandler,
|
||||
DatasetSelectionHandler,
|
||||
DatasetsSelectorSearchHandler,
|
||||
DataViewSelectionHandler,
|
||||
PanelId,
|
||||
} from '../types';
|
||||
import { createDatasetsSelectorStateMachine } from './state_machine';
|
||||
|
@ -18,6 +19,9 @@ import { DatasetsSelectorStateMachineDependencies } from './types';
|
|||
|
||||
export const useDatasetSelector = ({
|
||||
initialContext,
|
||||
onDataViewSelection,
|
||||
onDataViewsSearch,
|
||||
onDataViewsSort,
|
||||
onIntegrationsLoadMore,
|
||||
onIntegrationsReload,
|
||||
onIntegrationsSearch,
|
||||
|
@ -25,13 +29,16 @@ export const useDatasetSelector = ({
|
|||
onIntegrationsStreamsSearch,
|
||||
onIntegrationsStreamsSort,
|
||||
onSelectionChange,
|
||||
onUnmanagedStreamsSearch,
|
||||
onUnmanagedStreamsSort,
|
||||
onUnmanagedStreamsReload,
|
||||
onUncategorizedSearch,
|
||||
onUncategorizedSort,
|
||||
onUncategorizedReload,
|
||||
}: DatasetsSelectorStateMachineDependencies) => {
|
||||
const datasetsSelectorStateService = useInterpret(() =>
|
||||
createDatasetsSelectorStateMachine({
|
||||
initialContext,
|
||||
onDataViewSelection,
|
||||
onDataViewsSearch,
|
||||
onDataViewsSort,
|
||||
onIntegrationsLoadMore,
|
||||
onIntegrationsReload,
|
||||
onIntegrationsSearch,
|
||||
|
@ -39,9 +46,9 @@ export const useDatasetSelector = ({
|
|||
onIntegrationsStreamsSearch,
|
||||
onIntegrationsStreamsSort,
|
||||
onSelectionChange,
|
||||
onUnmanagedStreamsSearch,
|
||||
onUnmanagedStreamsSort,
|
||||
onUnmanagedStreamsReload,
|
||||
onUncategorizedSearch,
|
||||
onUncategorizedSort,
|
||||
onUncategorizedReload,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -64,6 +71,11 @@ export const useDatasetSelector = ({
|
|||
[datasetsSelectorStateService]
|
||||
);
|
||||
|
||||
const switchToDataViewsTab = useCallback(
|
||||
() => datasetsSelectorStateService.send({ type: 'SWITCH_TO_DATA_VIEWS_TAB' }),
|
||||
[datasetsSelectorStateService]
|
||||
);
|
||||
|
||||
const changePanel = useCallback<ChangePanelHandler>(
|
||||
(panelDetails) =>
|
||||
datasetsSelectorStateService.send({
|
||||
|
@ -93,6 +105,11 @@ export const useDatasetSelector = ({
|
|||
[datasetsSelectorStateService]
|
||||
);
|
||||
|
||||
const selectDataView = useCallback<DataViewSelectionHandler>(
|
||||
(dataView) => datasetsSelectorStateService.send({ type: 'SELECT_DATA_VIEW', dataView }),
|
||||
[datasetsSelectorStateService]
|
||||
);
|
||||
|
||||
const sortByOrder = useCallback<DatasetsSelectorSearchHandler>(
|
||||
(params) => datasetsSelectorStateService.send({ type: 'SORT_BY_ORDER', search: params }),
|
||||
[datasetsSelectorStateService]
|
||||
|
@ -124,9 +141,11 @@ export const useDatasetSelector = ({
|
|||
searchByName,
|
||||
selectAllLogDataset,
|
||||
selectDataset,
|
||||
selectDataView,
|
||||
sortByOrder,
|
||||
switchToIntegrationsTab,
|
||||
switchToUncategorizedTab,
|
||||
switchToDataViewsTab,
|
||||
togglePopover,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 React from 'react';
|
||||
import { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { dataViewsLabel, openDiscoverLabel } from '../constants';
|
||||
|
||||
export const DataViewsPanelTitle = () => {
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem>{dataViewsLabel}</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge iconType="discoverApp">{openDiscoverLabel}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -8,13 +8,14 @@
|
|||
import React from 'react';
|
||||
import { EuiButton, EuiEmptyPrompt, EuiText, EuiToolTip } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import { ReloadDatasets } from '../../../hooks/use_datasets';
|
||||
import { errorLabel, noDataRetryLabel } from '../constants';
|
||||
import type { Dataset, Integration } from '../../../../common/datasets';
|
||||
import { DatasetSkeleton } from './datasets_skeleton';
|
||||
|
||||
export interface ListStatusProps {
|
||||
data: Dataset[] | Integration[] | null;
|
||||
data: Dataset[] | Integration[] | DataViewListItem[] | null;
|
||||
description: string;
|
||||
error: Error | null;
|
||||
isLoading: boolean;
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
|
||||
import { EuiContextMenuPanelId } from '@elastic/eui/src/components/context_menu/context_menu';
|
||||
import type { DatasetSelection, DatasetSelectionChange } from '../../../common/dataset_selection';
|
||||
import { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import type {
|
||||
DatasetSelection,
|
||||
DatasetSelectionChange,
|
||||
DataViewSelection,
|
||||
} from '../../../common/dataset_selection';
|
||||
import { SortOrder } from '../../../common/latest';
|
||||
import { Dataset, Integration, IntegrationId } from '../../../common/datasets';
|
||||
import { LoadDatasets, ReloadDatasets, SearchDatasets } from '../../hooks/use_datasets';
|
||||
|
@ -15,7 +20,13 @@ import {
|
|||
ReloadIntegrations,
|
||||
SearchIntegrations,
|
||||
} from '../../hooks/use_integrations';
|
||||
import { INTEGRATIONS_PANEL_ID, INTEGRATIONS_TAB_ID, UNCATEGORIZED_TAB_ID } from './constants';
|
||||
import {
|
||||
DATA_VIEWS_TAB_ID,
|
||||
INTEGRATIONS_PANEL_ID,
|
||||
INTEGRATIONS_TAB_ID,
|
||||
UNCATEGORIZED_TAB_ID,
|
||||
} from './constants';
|
||||
import { LoadDataViews, ReloadDataViews, SearchDataViews } from '../../hooks/use_data_views';
|
||||
|
||||
export interface DatasetSelectorProps {
|
||||
/* The generic data stream list */
|
||||
|
@ -24,36 +35,52 @@ export interface DatasetSelectorProps {
|
|||
datasetsError: Error | null;
|
||||
/* The current selection instance */
|
||||
datasetSelection: DatasetSelection;
|
||||
/* The available data views list */
|
||||
dataViews: DataViewListItem[] | null;
|
||||
/* Any error occurred to show when the user preview the data views */
|
||||
dataViewsError: Error | null;
|
||||
/* The integrations list, each integration includes its data streams */
|
||||
integrations: Integration[] | null;
|
||||
/* Any error occurred to show when the user preview the integrations */
|
||||
integrationsError: Error | null;
|
||||
/* Flags for loading/searching integrations or data streams*/
|
||||
/* Flags for loading/searching integrations, data streams or data views*/
|
||||
isLoadingDataViews: boolean;
|
||||
isLoadingIntegrations: boolean;
|
||||
isLoadingStreams: boolean;
|
||||
isLoadingUncategorized: boolean;
|
||||
isSearchingIntegrations: boolean;
|
||||
/* Triggered when retrying to load the data views */
|
||||
onDataViewsReload: ReloadDataViews;
|
||||
/* Triggered when selecting a data view */
|
||||
onDataViewSelection: DataViewSelection;
|
||||
/* Triggered when the data views tab is selected */
|
||||
onDataViewsTabClick: LoadDataViews;
|
||||
/* Triggered when we reach the bottom of the integration list and want to load more */
|
||||
onIntegrationsLoadMore: LoadMoreIntegrations;
|
||||
/* Triggered when the user reload the list after an error */
|
||||
onIntegrationsReload: ReloadIntegrations;
|
||||
/* Triggered when a search or sorting is performed */
|
||||
onDataViewsSearch: SearchDataViews;
|
||||
onDataViewsSort: SearchDataViews;
|
||||
onIntegrationsSearch: SearchIntegrations;
|
||||
onIntegrationsSort: SearchIntegrations;
|
||||
onIntegrationsStreamsSearch: SearchIntegrations;
|
||||
onIntegrationsStreamsSort: SearchIntegrations;
|
||||
onUnmanagedStreamsSearch: SearchDatasets;
|
||||
onUnmanagedStreamsSort: SearchDatasets;
|
||||
onUncategorizedSearch: SearchDatasets;
|
||||
onUncategorizedSort: SearchDatasets;
|
||||
/* Triggered when retrying to load the data streams */
|
||||
onUnmanagedStreamsReload: ReloadDatasets;
|
||||
/* Triggered when the uncategorized streams entry is selected */
|
||||
onStreamsEntryClick: LoadDatasets;
|
||||
onUncategorizedReload: ReloadDatasets;
|
||||
/* Triggered when the uncategorized tab is selected */
|
||||
onUncategorizedTabClick: LoadDatasets;
|
||||
/* Triggered when the selection is updated */
|
||||
onSelectionChange: DatasetSelectionChange;
|
||||
}
|
||||
|
||||
export type PanelId = typeof INTEGRATIONS_PANEL_ID | IntegrationId;
|
||||
|
||||
export type TabId = typeof INTEGRATIONS_TAB_ID | typeof UNCATEGORIZED_TAB_ID;
|
||||
export type TabId =
|
||||
| typeof INTEGRATIONS_TAB_ID
|
||||
| typeof UNCATEGORIZED_TAB_ID
|
||||
| typeof DATA_VIEWS_TAB_ID;
|
||||
|
||||
export interface SearchParams {
|
||||
integrationId?: PanelId;
|
||||
|
@ -68,3 +95,5 @@ export type DatasetsSelectorSearchHandler = (params: DatasetsSelectorSearchParam
|
|||
export type ChangePanelHandler = ({ panelId }: { panelId: EuiContextMenuPanelId }) => void;
|
||||
|
||||
export type DatasetSelectionHandler = (dataset: Dataset) => void;
|
||||
|
||||
export type DataViewSelectionHandler = (dataView: DataViewListItem) => void;
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
DATA_VIEW_POPOVER_CONTENT_WIDTH,
|
||||
noDatasetsDescriptionLabel,
|
||||
noDatasetsLabel,
|
||||
noDataViewsDescriptionLabel,
|
||||
noDataViewsLabel,
|
||||
noIntegrationsDescriptionLabel,
|
||||
noIntegrationsLabel,
|
||||
} from './constants';
|
||||
|
@ -116,3 +118,19 @@ export const createUncategorizedStatusItem = (
|
|||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const createDataViewsStatusItem = (
|
||||
props: Omit<ListStatusProps, 'description' | 'title'>
|
||||
) => {
|
||||
return {
|
||||
disabled: true,
|
||||
name: (
|
||||
<ListStatus
|
||||
key="dataViewsStatusItem"
|
||||
description={noDataViewsDescriptionLabel}
|
||||
title={noDataViewsLabel}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,19 +8,19 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { ScopedHistory } from '@kbn/core-application-browser';
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { DiscoverAppState, DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import { DiscoverAppState } from '@kbn/discover-plugin/public';
|
||||
import type { BehaviorSubject } from 'rxjs';
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
|
||||
import { HIDE_ANNOUNCEMENTS } from '@kbn/discover-utils';
|
||||
import {
|
||||
createLogExplorerProfileCustomizations,
|
||||
CreateLogExplorerProfileCustomizationsDeps,
|
||||
} from '../../customizations/log_explorer_profile';
|
||||
import { createLogExplorerProfileCustomizations } from '../../customizations/log_explorer_profile';
|
||||
import { createPropertyGetProxy } from '../../utils/proxies';
|
||||
import { LogExplorerProfileContext } from '../../state_machines/log_explorer_profile';
|
||||
import { LogExplorerStartDeps } from '../../types';
|
||||
|
||||
export interface CreateLogExplorerArgs extends CreateLogExplorerProfileCustomizationsDeps {
|
||||
discover: DiscoverStart;
|
||||
export interface CreateLogExplorerArgs {
|
||||
core: CoreStart;
|
||||
plugins: LogExplorerStartDeps;
|
||||
}
|
||||
|
||||
export interface LogExplorerStateContainer {
|
||||
|
@ -33,11 +33,12 @@ export interface LogExplorerProps {
|
|||
state$?: BehaviorSubject<LogExplorerStateContainer>;
|
||||
}
|
||||
|
||||
export const createLogExplorer = ({
|
||||
core,
|
||||
data,
|
||||
discover: { DiscoverContainer },
|
||||
}: CreateLogExplorerArgs) => {
|
||||
export const createLogExplorer = ({ core, plugins }: CreateLogExplorerArgs) => {
|
||||
const {
|
||||
data,
|
||||
discover: { DiscoverContainer },
|
||||
} = plugins;
|
||||
|
||||
const overrideServices = {
|
||||
data: createDataServiceProxy(data),
|
||||
uiSettings: createUiSettingsServiceProxy(core.uiSettings),
|
||||
|
@ -45,7 +46,7 @@ export const createLogExplorer = ({
|
|||
|
||||
return ({ scopedHistory, state$ }: LogExplorerProps) => {
|
||||
const logExplorerCustomizations = useMemo(
|
||||
() => [createLogExplorerProfileCustomizations({ core, data, state$ })],
|
||||
() => [createLogExplorerProfileCustomizations({ core, plugins, state$ })],
|
||||
[state$]
|
||||
);
|
||||
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import { DatasetSelector } from '../components/dataset_selector';
|
||||
import { DatasetsProvider, useDatasetsContext } from '../hooks/use_datasets';
|
||||
import { IntegrationsProvider, useIntegrationsContext } from '../hooks/use_integrations';
|
||||
import { IDatasetsClient } from '../services/datasets';
|
||||
import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile';
|
||||
import { useDatasetSelection } from '../hooks/use_dataset_selection';
|
||||
import { DataViewsProvider, useDataViewsContext } from '../hooks/use_data_views';
|
||||
|
||||
interface CustomDatasetSelectorProps {
|
||||
logExplorerProfileStateService: LogExplorerProfileStateService;
|
||||
|
@ -38,23 +41,42 @@ export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateSer
|
|||
const {
|
||||
datasets,
|
||||
error: datasetsError,
|
||||
isLoading: isLoadingStreams,
|
||||
isLoading: isLoadingUncategorized,
|
||||
loadDatasets,
|
||||
reloadDatasets,
|
||||
searchDatasets,
|
||||
sortDatasets,
|
||||
} = useDatasetsContext();
|
||||
|
||||
const {
|
||||
dataViews,
|
||||
error: dataViewsError,
|
||||
isLoading: isLoadingDataViews,
|
||||
loadDataViews,
|
||||
reloadDataViews,
|
||||
selectDataView,
|
||||
searchDataViews,
|
||||
sortDataViews,
|
||||
} = useDataViewsContext();
|
||||
|
||||
return (
|
||||
<DatasetSelector
|
||||
datasets={datasets}
|
||||
datasetSelection={datasetSelection}
|
||||
datasetsError={datasetsError}
|
||||
dataViews={dataViews}
|
||||
dataViewsError={dataViewsError}
|
||||
integrations={integrations}
|
||||
integrationsError={integrationsError}
|
||||
isLoadingDataViews={isLoadingDataViews}
|
||||
isLoadingIntegrations={isLoadingIntegrations}
|
||||
isLoadingStreams={isLoadingStreams}
|
||||
isLoadingUncategorized={isLoadingUncategorized}
|
||||
isSearchingIntegrations={isSearchingIntegrations}
|
||||
onDataViewsReload={reloadDataViews}
|
||||
onDataViewsSearch={searchDataViews}
|
||||
onDataViewSelection={selectDataView}
|
||||
onDataViewsSort={sortDataViews}
|
||||
onDataViewsTabClick={loadDataViews}
|
||||
onIntegrationsLoadMore={loadMore}
|
||||
onIntegrationsReload={reloadIntegrations}
|
||||
onIntegrationsSearch={searchIntegrations}
|
||||
|
@ -62,10 +84,10 @@ export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateSer
|
|||
onIntegrationsStreamsSearch={searchIntegrationsStreams}
|
||||
onIntegrationsStreamsSort={sortIntegrationsStreams}
|
||||
onSelectionChange={handleDatasetSelectionChange}
|
||||
onStreamsEntryClick={loadDatasets}
|
||||
onUnmanagedStreamsReload={reloadDatasets}
|
||||
onUnmanagedStreamsSearch={searchDatasets}
|
||||
onUnmanagedStreamsSort={sortDatasets}
|
||||
onUncategorizedReload={reloadDatasets}
|
||||
onUncategorizedSearch={searchDatasets}
|
||||
onUncategorizedSort={sortDatasets}
|
||||
onUncategorizedTabClick={loadDatasets}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -75,17 +97,23 @@ export default CustomDatasetSelector;
|
|||
|
||||
export type CustomDatasetSelectorBuilderProps = CustomDatasetSelectorProps & {
|
||||
datasetsClient: IDatasetsClient;
|
||||
dataViews: DataViewsPublicPluginStart;
|
||||
discover: DiscoverStart;
|
||||
};
|
||||
|
||||
function withProviders(Component: React.FunctionComponent<CustomDatasetSelectorProps>) {
|
||||
return function ComponentWithProviders({
|
||||
logExplorerProfileStateService,
|
||||
datasetsClient,
|
||||
dataViews,
|
||||
discover,
|
||||
logExplorerProfileStateService,
|
||||
}: CustomDatasetSelectorBuilderProps) {
|
||||
return (
|
||||
<IntegrationsProvider datasetsClient={datasetsClient}>
|
||||
<DatasetsProvider datasetsClient={datasetsClient}>
|
||||
<Component logExplorerProfileStateService={logExplorerProfileStateService} />
|
||||
<DataViewsProvider dataViewsService={dataViews} discoverService={discover}>
|
||||
<Component logExplorerProfileStateService={logExplorerProfileStateService} />
|
||||
</DataViewsProvider>
|
||||
</DatasetsProvider>
|
||||
</IntegrationsProvider>
|
||||
);
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { CustomizationCallback, DiscoverStateContainer } from '@kbn/discover-plugin/public';
|
||||
import React from 'react';
|
||||
|
@ -13,19 +11,21 @@ import { type BehaviorSubject, combineLatest, from, map, Subscription } from 'rx
|
|||
import { dynamic } from '../utils/dynamic';
|
||||
import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile';
|
||||
import { LogExplorerStateContainer } from '../components/log_explorer';
|
||||
import { LogExplorerStartDeps } from '../types';
|
||||
|
||||
const LazyCustomDatasetSelector = dynamic(() => import('./custom_dataset_selector'));
|
||||
const LazyCustomDatasetFilters = dynamic(() => import('./custom_dataset_filters'));
|
||||
|
||||
export interface CreateLogExplorerProfileCustomizationsDeps {
|
||||
core: CoreStart;
|
||||
data: DataPublicPluginStart;
|
||||
plugins: LogExplorerStartDeps;
|
||||
state$?: BehaviorSubject<LogExplorerStateContainer>;
|
||||
}
|
||||
|
||||
export const createLogExplorerProfileCustomizations =
|
||||
({ core, data, state$ }: CreateLogExplorerProfileCustomizationsDeps): CustomizationCallback =>
|
||||
({ core, plugins, state$ }: CreateLogExplorerProfileCustomizationsDeps): CustomizationCallback =>
|
||||
async ({ customizations, stateContainer }) => {
|
||||
const { data, dataViews, discover } = plugins;
|
||||
// Lazy load dependencies
|
||||
const datasetServiceModuleLoadable = import('../services/datasets');
|
||||
const logExplorerMachineModuleLoadable = import('../state_machines/log_explorer_profile');
|
||||
|
@ -72,6 +72,8 @@ export const createLogExplorerProfileCustomizations =
|
|||
CustomDataViewPicker: () => (
|
||||
<LazyCustomDatasetSelector
|
||||
datasetsClient={datasetsClient}
|
||||
dataViews={dataViews}
|
||||
discover={discover}
|
||||
logExplorerProfileStateService={logExplorerProfileStateService}
|
||||
/>
|
||||
),
|
||||
|
|
104
x-pack/plugins/log_explorer/public/hooks/use_data_views.tsx
Normal file
104
x-pack/plugins/log_explorer/public/hooks/use_data_views.tsx
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 { useCallback } from 'react';
|
||||
import createContainer from 'constate';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { DataViewListItem, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import { SortOrder } from '../../common/latest';
|
||||
import { createDataViewsStateMachine } from '../state_machines/data_views';
|
||||
|
||||
interface DataViewsContextDeps {
|
||||
dataViewsService: DataViewsPublicPluginStart;
|
||||
discoverService: DiscoverStart;
|
||||
}
|
||||
|
||||
export interface SearchDataViewsParams {
|
||||
name: string;
|
||||
sortOrder: SortOrder;
|
||||
}
|
||||
|
||||
export type DataViewSelectionHandler = (dataView: DataViewListItem) => void;
|
||||
export type SearchDataViews = (params: SearchDataViewsParams) => void;
|
||||
export type LoadDataViews = () => void;
|
||||
export type ReloadDataViews = () => void;
|
||||
|
||||
const useDataViews = ({ dataViewsService, discoverService }: DataViewsContextDeps) => {
|
||||
const dataViewsStateService = useInterpret(() =>
|
||||
createDataViewsStateMachine({
|
||||
dataViews: dataViewsService,
|
||||
discover: discoverService,
|
||||
})
|
||||
);
|
||||
|
||||
const dataViews = useSelector(dataViewsStateService, (state) => state.context.dataViews);
|
||||
|
||||
const error = useSelector(dataViewsStateService, (state) => state.context.error);
|
||||
|
||||
const isLoading = useSelector(dataViewsStateService, (state) => state.matches('loading'));
|
||||
|
||||
const loadDataViews = useCallback(
|
||||
() => dataViewsStateService.send({ type: 'LOAD_DATA_VIEWS' }),
|
||||
[dataViewsStateService]
|
||||
);
|
||||
|
||||
const reloadDataViews = useCallback(
|
||||
() => dataViewsStateService.send({ type: 'RELOAD_DATA_VIEWS' }),
|
||||
[dataViewsStateService]
|
||||
);
|
||||
|
||||
const searchDataViews: SearchDataViews = useCallback(
|
||||
(searchParams) =>
|
||||
dataViewsStateService.send({
|
||||
type: 'SEARCH_DATA_VIEWS',
|
||||
search: searchParams,
|
||||
}),
|
||||
[dataViewsStateService]
|
||||
);
|
||||
|
||||
const selectDataView: DataViewSelectionHandler = useCallback(
|
||||
(dataView) =>
|
||||
dataViewsStateService.send({
|
||||
type: 'SELECT_DATA_VIEW',
|
||||
dataView,
|
||||
}),
|
||||
[dataViewsStateService]
|
||||
);
|
||||
|
||||
const sortDataViews: SearchDataViews = useCallback(
|
||||
(searchParams) =>
|
||||
dataViewsStateService.send({
|
||||
type: 'SORT_DATA_VIEWS',
|
||||
search: searchParams,
|
||||
}),
|
||||
[dataViewsStateService]
|
||||
);
|
||||
|
||||
return {
|
||||
// Underlying state machine
|
||||
dataViewsStateService,
|
||||
|
||||
// Failure states
|
||||
error,
|
||||
|
||||
// Loading states
|
||||
isLoading,
|
||||
|
||||
// Data
|
||||
dataViews,
|
||||
|
||||
// Actions
|
||||
loadDataViews,
|
||||
reloadDataViews,
|
||||
searchDataViews,
|
||||
selectDataView,
|
||||
sortDataViews,
|
||||
};
|
||||
};
|
||||
|
||||
export const [DataViewsProvider, useDataViewsContext] = createContainer(useDataViews);
|
|
@ -40,12 +40,9 @@ export class LogExplorerPlugin implements Plugin<LogExplorerPluginSetup, LogExpl
|
|||
}
|
||||
|
||||
public start(core: CoreStart, plugins: LogExplorerStartDeps) {
|
||||
const { data, discover } = plugins;
|
||||
|
||||
const LogExplorer = createLogExplorer({
|
||||
core,
|
||||
data,
|
||||
discover,
|
||||
plugins,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -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 * from './src';
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { HashedCache } from '../../../../common/hashed_cache';
|
||||
import { DefaultDataViewsContext } from './types';
|
||||
|
||||
export const DEFAULT_CONTEXT: DefaultDataViewsContext = {
|
||||
cache: new HashedCache(),
|
||||
dataViewsSource: null,
|
||||
dataViews: null,
|
||||
error: null,
|
||||
search: {
|
||||
name: '',
|
||||
sortOrder: 'asc',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 * from './state_machine';
|
||||
export * from './types';
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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 { DataViewListItem, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { isError } from 'lodash';
|
||||
import { assign, createMachine } from 'xstate';
|
||||
import { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import { parseDataViewListItem } from '../../../utils/parse_data_view_list_item';
|
||||
import { createComparatorByField } from '../../../utils/comparator_by_field';
|
||||
import { DEFAULT_CONTEXT } from './defaults';
|
||||
import type {
|
||||
DataViewsContext,
|
||||
DataViewsEvent,
|
||||
DataViewsSearchParams,
|
||||
DataViewsTypestate,
|
||||
DefaultDataViewsContext,
|
||||
} from './types';
|
||||
|
||||
export const createPureDataViewsStateMachine = (
|
||||
initialContext: DefaultDataViewsContext = DEFAULT_CONTEXT
|
||||
) =>
|
||||
createMachine<DataViewsContext, DataViewsEvent, DataViewsTypestate>(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVA1AlmA7rAHQCuAdjhejqgDY4BekAxADIDyAgsgPrKcAVTjywBJAKIB1AMoBtAAwBdRKAAOAe1g5q6sipAAPRABYAzKaIB2a6bOWArABoQAT0QBaAJzyi9y8YAmAEYADgcAX3DnNExcAmJadVQISihmCF0wIkoAN3UAayyY7DxCIkTk1IRc9QBjDBxdBUVm-Q0tHT0kQxNjSyJPP3lTIICnV0RLTyJ5eUsQ0ftI6IwS+PKklLI0sAAnXfVdolVaDAAzQ4BbImK4soqtqGqyPPrO5tbu9u1GrtAjBAANgcRFMDmcbgQIPsIQCw0WyxAt1KCU2kGyEFoYGY0nEnAASgBhAASfEEwjEUjkSjamh+un0AOsgN8nksgPsgLGEJMHKIgNCgRC9mMC2siOR6we6JwmOx0nY+IEZKEIgkMk+ajpnUZJk8ISIAQCHK540hpgCBoWgOM8lNEtWd1RyRlcpx4lY4kJyv4qspkk1IG+Ou6AMB8gNfkCoXBEwQ9gtM1GgzZ+qC4qiSMdKI2LogRAgYAARupyLUwNIwKhdrUABapSWEd0Ekkqinq6nKL7a366hBBeT2ez8zncuMJ6ZzEX2VNiywO2I56X5wslssVqs1+vbRuwZgGWCYdBZVBnY+7AAUplmAEpmLvc4WV8XS2Ry5Xq3WG9n4oHg73Q0mEIDXDYwwLMexLUBMEeQQcwLE8II0wCYxARCcMpwXNZ7k2VIADFUBwLEIGYfEPS4XhfXbKk-x7BlAKBaDfACSxbDBM0PACUFPGMBMEURMh1ELeBul3WkOgA-5EFMYUrBsOwOIQdwUK4oYRjGLCnVICgqBoegmAgcT6T+HoEDAlkxnmRZYPcIIhxmSx4Q0zMHweVIjJDKSzNtIgQlmKyx0hZThxCHi+OclZFylNFDO7CT6K83ifCNE1AsQFCglBIIbTtCKsyinC8wxLEPMk0zARtUF7Ds01YN44dAj8OFglFBTNKXGKCxfdcPy3b8CpErV4pMgEBztUEVJjRSzEyzxLOaoJWvY9rosqbYCKIyBSoS0y5q4nLarjZT+j8BZnMiIA */
|
||||
context: initialContext,
|
||||
preserveActionOrder: true,
|
||||
predictableActionArguments: true,
|
||||
id: 'DataViews',
|
||||
initial: 'uninitialized',
|
||||
states: {
|
||||
uninitialized: {
|
||||
on: {
|
||||
LOAD_DATA_VIEWS: 'loading',
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
id: 'loading',
|
||||
invoke: {
|
||||
src: 'loadDataViews',
|
||||
onDone: {
|
||||
target: 'loaded',
|
||||
actions: ['storeInCache', 'storeDataViews', 'storeSearch'],
|
||||
},
|
||||
onError: 'loadingFailed',
|
||||
},
|
||||
},
|
||||
loaded: {
|
||||
id: 'loaded',
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: {
|
||||
on: {
|
||||
SEARCH_DATA_VIEWS: 'debounceSearchingDataViews',
|
||||
SORT_DATA_VIEWS: {
|
||||
actions: ['storeSearch', 'searchDataViews'],
|
||||
},
|
||||
SELECT_DATA_VIEW: {
|
||||
actions: ['navigateToDiscoverDataView'],
|
||||
},
|
||||
},
|
||||
},
|
||||
debounceSearchingDataViews: {
|
||||
entry: 'storeSearch',
|
||||
on: {
|
||||
SEARCH_DATA_VIEWS: 'debounceSearchingDataViews',
|
||||
},
|
||||
after: {
|
||||
300: {
|
||||
target: 'idle',
|
||||
actions: 'searchDataViews',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
loadingFailed: {
|
||||
entry: ['clearCache', 'clearData', 'storeError'],
|
||||
exit: 'clearError',
|
||||
on: {
|
||||
RELOAD_DATA_VIEWS: 'loading',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
storeSearch: assign((_context, event) => ({
|
||||
// Store search from search event
|
||||
...('search' in event && { search: event.search }),
|
||||
})),
|
||||
storeDataViews: assign((_context, event) =>
|
||||
'data' in event && !isError(event.data)
|
||||
? { dataViewsSource: event.data, dataViews: event.data }
|
||||
: {}
|
||||
),
|
||||
searchDataViews: assign((context) => {
|
||||
if (context.dataViewsSource !== null) {
|
||||
return {
|
||||
dataViews: searchDataViews(context.dataViewsSource, context.search),
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
storeInCache: (context, event) => {
|
||||
if ('data' in event && !isError(event.data)) {
|
||||
context.cache.set(context.search, event.data);
|
||||
}
|
||||
},
|
||||
storeError: assign((_context, event) =>
|
||||
'data' in event && isError(event.data) ? { error: event.data } : {}
|
||||
),
|
||||
clearCache: (context) => {
|
||||
context.cache.reset();
|
||||
},
|
||||
clearData: assign((_context) => ({ dataViews: null })),
|
||||
clearError: assign((_context) => ({ error: null })),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface DataViewsStateMachineDependencies {
|
||||
initialContext?: DefaultDataViewsContext;
|
||||
dataViews: DataViewsPublicPluginStart;
|
||||
discover: DiscoverStart;
|
||||
}
|
||||
|
||||
export const createDataViewsStateMachine = ({
|
||||
initialContext,
|
||||
dataViews,
|
||||
discover,
|
||||
}: DataViewsStateMachineDependencies) =>
|
||||
createPureDataViewsStateMachine(initialContext).withConfig({
|
||||
actions: {
|
||||
navigateToDiscoverDataView: (_context, event) => {
|
||||
if (event.type === 'SELECT_DATA_VIEW' && 'dataView' in event) {
|
||||
discover.locator?.navigate({ dataViewId: event.dataView.id });
|
||||
}
|
||||
},
|
||||
},
|
||||
services: {
|
||||
loadDataViews: (context) => {
|
||||
const searchParams = context.search;
|
||||
return context.cache.has(searchParams)
|
||||
? Promise.resolve(context.cache.get(searchParams))
|
||||
: dataViews.getIdsWithTitle().then((views) => views.map(parseDataViewListItem));
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const searchDataViews = (dataViews: DataViewListItem[], search: DataViewsSearchParams) => {
|
||||
const { name, sortOrder } = search;
|
||||
|
||||
return dataViews
|
||||
.filter((dataView) => Boolean(dataView.name?.includes(name ?? '')))
|
||||
.sort(createComparatorByField<DataViewListItem>('name', sortOrder));
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 { DoneInvokeEvent } from 'xstate';
|
||||
import type { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
import type { IHashedCache } from '../../../../common/hashed_cache';
|
||||
import { SortOrder } from '../../../../common/latest';
|
||||
|
||||
export interface DataViewsSearchParams {
|
||||
name?: string;
|
||||
sortOrder?: SortOrder;
|
||||
}
|
||||
|
||||
export interface WithCache {
|
||||
cache: IHashedCache<DataViewsSearchParams, DataViewListItem[]>;
|
||||
}
|
||||
|
||||
export interface WithSearch {
|
||||
search: DataViewsSearchParams;
|
||||
}
|
||||
|
||||
export interface WithDataViews {
|
||||
dataViewsSource: DataViewListItem[];
|
||||
dataViews: DataViewListItem[];
|
||||
}
|
||||
|
||||
export interface WithNullishDataViews {
|
||||
dataViewsSource: null;
|
||||
dataViews: null;
|
||||
}
|
||||
|
||||
export interface WithError {
|
||||
error: Error;
|
||||
}
|
||||
|
||||
export interface WithNullishError {
|
||||
error: null;
|
||||
}
|
||||
|
||||
export type DefaultDataViewsContext = WithCache &
|
||||
WithNullishDataViews &
|
||||
WithSearch &
|
||||
WithNullishError;
|
||||
|
||||
type LoadingDataViewsContext = DefaultDataViewsContext;
|
||||
|
||||
type LoadedDataViewsContext = WithCache & WithDataViews & WithSearch & WithNullishError;
|
||||
|
||||
type LoadingFailedDataViewsContext = WithCache & WithNullishDataViews & WithSearch & WithError;
|
||||
|
||||
export type DataViewsTypestate =
|
||||
| {
|
||||
value: 'uninitialized';
|
||||
context: DefaultDataViewsContext;
|
||||
}
|
||||
| {
|
||||
value: 'loading';
|
||||
context: LoadingDataViewsContext;
|
||||
}
|
||||
| {
|
||||
value: 'loaded';
|
||||
context: LoadedDataViewsContext;
|
||||
}
|
||||
| {
|
||||
value: 'loaded.idle';
|
||||
context: LoadedDataViewsContext;
|
||||
}
|
||||
| {
|
||||
value: 'loaded.debounceSearchingDataViews';
|
||||
context: LoadedDataViewsContext;
|
||||
}
|
||||
| {
|
||||
value: 'loadingFailed';
|
||||
context: LoadingFailedDataViewsContext;
|
||||
};
|
||||
|
||||
export type DataViewsContext = DataViewsTypestate['context'];
|
||||
|
||||
export type DataViewsEvent =
|
||||
| {
|
||||
type: 'LOAD_DATA_VIEWS';
|
||||
}
|
||||
| {
|
||||
type: 'RELOAD_DATA_VIEWS';
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_DATA_VIEW';
|
||||
dataView: DataViewListItem;
|
||||
}
|
||||
| {
|
||||
type: 'SEARCH_DATA_VIEWS';
|
||||
search: DataViewsSearchParams;
|
||||
}
|
||||
| {
|
||||
type: 'SORT_DATA_VIEWS';
|
||||
search: DataViewsSearchParams;
|
||||
}
|
||||
| DoneInvokeEvent<DataViewListItem[] | Error>;
|
|
@ -4,10 +4,11 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ComponentType } from 'react';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import { SharePluginSetup } from '@kbn/share-plugin/public';
|
||||
import type { ComponentType } from 'react';
|
||||
import { LogExplorerLocators } from '../common/locators';
|
||||
import type { LogExplorerProps } from './components/log_explorer';
|
||||
|
||||
|
@ -25,5 +26,6 @@ export interface LogExplorerSetupDeps {
|
|||
|
||||
export interface LogExplorerStartDeps {
|
||||
data: DataPublicPluginStart;
|
||||
dataViews: DataViewsPublicPluginStart;
|
||||
discover: DiscoverStart;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const BASE_DATA_VIEW_TEST_SUBJ = 'logExplorerDataView';
|
||||
|
||||
const publicDataViewPatternsSet = new Set(['logs-*', 'logstash-*', 'filebeat-*']);
|
||||
|
||||
export const getDataViewTestSubj = (title: string) => {
|
||||
if (publicDataViewPatternsSet.has(title)) {
|
||||
return [BASE_DATA_VIEW_TEST_SUBJ, cleanTitle(title)].join('_');
|
||||
}
|
||||
|
||||
return BASE_DATA_VIEW_TEST_SUBJ;
|
||||
};
|
||||
|
||||
const cleanTitle = (title: string) => title.slice(0, -2);
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { DataViewListItem } from '@kbn/data-views-plugin/common';
|
||||
|
||||
export const parseDataViewListItem = (dataViewListItem: DataViewListItem) => {
|
||||
return {
|
||||
...dataViewListItem,
|
||||
name: dataViewListItem.name ?? dataViewListItem.title,
|
||||
};
|
||||
};
|
|
@ -25,7 +25,7 @@ export const betaBadgeDescription = i18n.translate(
|
|||
export const discoverLinkTitle = i18n.translate(
|
||||
'xpack.observabilityLogExplorer.discoverLinkTitle',
|
||||
{
|
||||
defaultMessage: 'Discover',
|
||||
defaultMessage: 'Open in Discover',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -14,23 +14,32 @@ const initialPackageMap = {
|
|||
};
|
||||
const initialPackagesTexts = Object.values(initialPackageMap);
|
||||
|
||||
const expectedDataViews = ['logstash-*', 'logs-*', 'metrics-*'];
|
||||
const sortedExpectedDataViews = expectedDataViews.slice().sort();
|
||||
|
||||
const uncategorized = ['logs-gaming-*', 'logs-manufacturing-*', 'logs-retail-*'];
|
||||
const expectedUncategorized = uncategorized.map((dataset) => dataset.split('-')[1]);
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const browser = getService('browser');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['common', 'observabilityLogExplorer']);
|
||||
const PageObjects = getPageObjects(['common', 'discover', 'observabilityLogExplorer']);
|
||||
|
||||
const noIntegrationsTitle = 'No integrations found';
|
||||
const noUncategorizedTitle = 'No data streams found';
|
||||
|
||||
describe('Dataset Selector', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
|
||||
await PageObjects.observabilityLogExplorer.removeInstalledPackages();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
|
||||
});
|
||||
|
||||
describe('as consistent behavior', () => {
|
||||
before(async () => {
|
||||
await PageObjects.observabilityLogExplorer.navigateTo();
|
||||
|
@ -41,14 +50,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.observabilityLogExplorer.openDatasetSelector();
|
||||
});
|
||||
|
||||
it('should always display the Integrations and Uncategorized top level tabs', async () => {
|
||||
it('should always display the Integrations Uncategorized and Data Views top level tabs', async () => {
|
||||
const integrationsTab = await PageObjects.observabilityLogExplorer.getIntegrationsTab();
|
||||
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
|
||||
const dataViewsTab = await PageObjects.observabilityLogExplorer.getDataViewsTab();
|
||||
|
||||
expect(await integrationsTab.isDisplayed()).to.be(true);
|
||||
expect(await integrationsTab.getVisibleText()).to.be('Integrations');
|
||||
expect(await uncategorizedTab.isDisplayed()).to.be(true);
|
||||
expect(await uncategorizedTab.getVisibleText()).to.be('Uncategorized');
|
||||
expect(await dataViewsTab.isDisplayed()).to.be(true);
|
||||
expect(await dataViewsTab.getVisibleText()).to.be('Data Views');
|
||||
});
|
||||
|
||||
it('should always display the "Show all logs" action', async () => {
|
||||
|
@ -565,6 +577,143 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when open on the data views tab', () => {
|
||||
before(async () => {
|
||||
await PageObjects.observabilityLogExplorer.navigateTo();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.refresh();
|
||||
await PageObjects.observabilityLogExplorer.openDatasetSelector();
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsTab().then((tab) => tab.click());
|
||||
});
|
||||
|
||||
it('should display a list of available data views', async () => {
|
||||
await retry.try(async () => {
|
||||
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) =>
|
||||
Promise.all([
|
||||
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
|
||||
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
|
||||
])
|
||||
);
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(expectedDataViews[0]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(expectedDataViews[1]);
|
||||
expect(await menuEntries[2].getVisibleText()).to.be(expectedDataViews[2]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort the data views list by the clicked sorting option', async () => {
|
||||
await retry.try(async () => {
|
||||
const panelTitleNode = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
});
|
||||
|
||||
// Test descending order
|
||||
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(sortedExpectedDataViews[2]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(sortedExpectedDataViews[1]);
|
||||
expect(await menuEntries[2].getVisibleText()).to.be(sortedExpectedDataViews[0]);
|
||||
});
|
||||
|
||||
// Test back ascending order
|
||||
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(sortedExpectedDataViews[0]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(sortedExpectedDataViews[1]);
|
||||
expect(await menuEntries[2].getVisibleText()).to.be(sortedExpectedDataViews[2]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter the datasets list by the typed data view name', async () => {
|
||||
await retry.try(async () => {
|
||||
const panelTitleNode = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(expectedDataViews[0]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(expectedDataViews[1]);
|
||||
expect(await menuEntries[2].getVisibleText()).to.be(expectedDataViews[2]);
|
||||
});
|
||||
|
||||
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('logs');
|
||||
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(menuEntries.length).to.be(2);
|
||||
expect(await menuEntries[0].getVisibleText()).to.be('logs-*');
|
||||
expect(await menuEntries[1].getVisibleText()).to.be('logstash-*');
|
||||
});
|
||||
});
|
||||
|
||||
it('should navigate to Discover with the clicked data view preselected', async () => {
|
||||
await retry.try(async () => {
|
||||
const panelTitleNode = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[2].getVisibleText()).to.be(expectedDataViews[2]);
|
||||
menuEntries[2].click();
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
expect(await PageObjects.discover.getCurrentlySelectedDataView()).to.eql(
|
||||
expectedDataViews[2]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when open/close the selector', () => {
|
||||
before(async () => {
|
||||
await PageObjects.observabilityLogExplorer.navigateTo();
|
||||
|
|
|
@ -208,6 +208,18 @@ export function ObservabilityLogExplorerPageObject({
|
|||
return testSubjects.find('datasetSelectorUncategorizedTab');
|
||||
},
|
||||
|
||||
getDataViewsContextMenu() {
|
||||
return testSubjects.find('dataViewsContextMenu');
|
||||
},
|
||||
|
||||
getDataViewsContextMenuTitle(panelTitleNode: WebElementWrapper) {
|
||||
return panelTitleNode.getVisibleText().then((title) => title.split('\n')[0]);
|
||||
},
|
||||
|
||||
getDataViewsTab() {
|
||||
return testSubjects.find('datasetSelectorDataViewsTab');
|
||||
},
|
||||
|
||||
getPanelTitle(contextMenu: WebElementWrapper) {
|
||||
return contextMenu.findByClassName('euiContextMenuPanelTitle');
|
||||
},
|
||||
|
|
|
@ -14,6 +14,9 @@ const initialPackageMap = {
|
|||
};
|
||||
const initialPackagesTexts = Object.values(initialPackageMap);
|
||||
|
||||
const expectedDataViews = ['logs-*', 'metrics-*'];
|
||||
const sortedExpectedDataViews = expectedDataViews.slice().sort();
|
||||
|
||||
const uncategorized = ['logs-gaming-*', 'logs-manufacturing-*', 'logs-retail-*'];
|
||||
const expectedUncategorized = uncategorized.map((dataset) => dataset.split('-')[1]);
|
||||
|
||||
|
@ -21,7 +24,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const browser = getService('browser');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['common', 'observabilityLogExplorer', 'svlCommonPage']);
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'discover',
|
||||
'observabilityLogExplorer',
|
||||
'svlCommonPage',
|
||||
]);
|
||||
|
||||
const noIntegrationsTitle = 'No integrations found';
|
||||
const noUncategorizedTitle = 'No data streams found';
|
||||
|
@ -46,14 +54,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.observabilityLogExplorer.openDatasetSelector();
|
||||
});
|
||||
|
||||
it('should always display the Integrations and Uncategorized top level tabs', async () => {
|
||||
it('should always display the Integrations Uncategorized and Data Views top level tabs', async () => {
|
||||
const integrationsTab = await PageObjects.observabilityLogExplorer.getIntegrationsTab();
|
||||
const uncategorizedTab = await PageObjects.observabilityLogExplorer.getUncategorizedTab();
|
||||
const dataViewsTab = await PageObjects.observabilityLogExplorer.getDataViewsTab();
|
||||
|
||||
expect(await integrationsTab.isDisplayed()).to.be(true);
|
||||
expect(await integrationsTab.getVisibleText()).to.be('Integrations');
|
||||
expect(await uncategorizedTab.isDisplayed()).to.be(true);
|
||||
expect(await uncategorizedTab.getVisibleText()).to.be('Uncategorized');
|
||||
expect(await dataViewsTab.isDisplayed()).to.be(true);
|
||||
expect(await dataViewsTab.getVisibleText()).to.be('Data Views');
|
||||
});
|
||||
|
||||
it('should always display the "Show all logs" action', async () => {
|
||||
|
@ -570,6 +581,138 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when open on the data views tab', () => {
|
||||
before(async () => {
|
||||
await PageObjects.observabilityLogExplorer.navigateTo();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.refresh();
|
||||
await PageObjects.observabilityLogExplorer.openDatasetSelector();
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsTab().then((tab) => tab.click());
|
||||
});
|
||||
|
||||
it('should display a list of available data views', async () => {
|
||||
await retry.try(async () => {
|
||||
const [panelTitleNode, menuEntries] = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) =>
|
||||
Promise.all([
|
||||
PageObjects.observabilityLogExplorer.getPanelTitle(menu),
|
||||
PageObjects.observabilityLogExplorer.getPanelEntries(menu),
|
||||
])
|
||||
);
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(expectedDataViews[0]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(expectedDataViews[1]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort the data views list by the clicked sorting option', async () => {
|
||||
await retry.try(async () => {
|
||||
const panelTitleNode = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
});
|
||||
|
||||
// Test descending order
|
||||
await PageObjects.observabilityLogExplorer.clickSortButtonBy('desc');
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(sortedExpectedDataViews[1]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(sortedExpectedDataViews[0]);
|
||||
});
|
||||
|
||||
// Test back ascending order
|
||||
await PageObjects.observabilityLogExplorer.clickSortButtonBy('asc');
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(sortedExpectedDataViews[0]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(sortedExpectedDataViews[1]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter the datasets list by the typed data view name', async () => {
|
||||
await retry.try(async () => {
|
||||
const panelTitleNode = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(expectedDataViews[0]);
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(expectedDataViews[1]);
|
||||
});
|
||||
|
||||
await PageObjects.observabilityLogExplorer.typeSearchFieldWith('logs');
|
||||
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(menuEntries.length).to.be(1);
|
||||
expect(await menuEntries[0].getVisibleText()).to.be('logs-*');
|
||||
});
|
||||
});
|
||||
|
||||
it('should navigate to Discover with the clicked data view preselected', async () => {
|
||||
await retry.try(async () => {
|
||||
const panelTitleNode = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelTitle(menu));
|
||||
|
||||
expect(
|
||||
await PageObjects.observabilityLogExplorer.getDataViewsContextMenuTitle(
|
||||
panelTitleNode
|
||||
)
|
||||
).to.be('Data Views');
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
const menuEntries = await PageObjects.observabilityLogExplorer
|
||||
.getDataViewsContextMenu()
|
||||
.then((menu) => PageObjects.observabilityLogExplorer.getPanelEntries(menu));
|
||||
|
||||
expect(await menuEntries[1].getVisibleText()).to.be(expectedDataViews[1]);
|
||||
menuEntries[1].click();
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
expect(await PageObjects.discover.getCurrentlySelectedDataView()).to.eql(
|
||||
expectedDataViews[1]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when open/close the selector', () => {
|
||||
before(async () => {
|
||||
await PageObjects.observabilityLogExplorer.navigateTo();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue