mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Cleaned up Observability plugin from timelines unused dependencies on TGrid usage (#143607)
* Clean Observability plugin from timelines unused deps on TGrid usage * - * End of standalone version of TGrid * fixed unused deps * - * Clean up variables * Fixed tests * FIxed tests * Removed unused tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c25eedc402
commit
64b5efebdd
36 changed files with 67 additions and 1065 deletions
|
@ -475,8 +475,6 @@
|
|||
"@kbn/global-search-test-plugin/*": ["x-pack/test/plugin_functional/plugins/global_search_test/*"],
|
||||
"@kbn/resolver-test-plugin": ["x-pack/test/plugin_functional/plugins/resolver_test"],
|
||||
"@kbn/resolver-test-plugin/*": ["x-pack/test/plugin_functional/plugins/resolver_test/*"],
|
||||
"@kbn/timelines-test-plugin": ["x-pack/test/plugin_functional/plugins/timelines_test"],
|
||||
"@kbn/timelines-test-plugin/*": ["x-pack/test/plugin_functional/plugins/timelines_test/*"],
|
||||
"@kbn/security-test-endpoints-plugin": ["x-pack/test/security_functional/fixtures/common/test_endpoints"],
|
||||
"@kbn/security-test-endpoints-plugin/*": ["x-pack/test/security_functional/fixtures/common/test_endpoints/*"],
|
||||
"@kbn/application-usage-test-plugin": ["x-pack/test/usage_collection/plugins/application_usage_test"],
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
"features",
|
||||
"inspector",
|
||||
"ruleRegistry",
|
||||
"timelines",
|
||||
"triggersActionsUi",
|
||||
"inspector",
|
||||
"unifiedSearch",
|
||||
|
|
|
@ -24,8 +24,6 @@ import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
|||
import { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { CasesUiStart } from '@kbn/cases-plugin/public';
|
||||
import { TimelinesUIStart } from '@kbn/timelines-plugin/public';
|
||||
|
||||
export interface ObservabilityAppServices {
|
||||
application: ApplicationStart;
|
||||
cases: CasesUiStart;
|
||||
|
@ -42,7 +40,6 @@ export interface ObservabilityAppServices {
|
|||
stateTransfer: EmbeddableStateTransfer;
|
||||
storage: IStorageWrapper;
|
||||
theme: ThemeServiceStart;
|
||||
timelines: TimelinesUIStart;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
uiSettings: IUiSettingsClient;
|
||||
isDev?: boolean;
|
||||
|
|
|
@ -12,9 +12,9 @@ import { casesFeatureId, observabilityFeatureId } from '../../common';
|
|||
import { useBulkAddToCaseActions } from '../hooks/use_alert_bulk_case_actions';
|
||||
import { TopAlert, useToGetInternalFlyout } from '../pages/alerts';
|
||||
import { getRenderCellValue } from '../pages/alerts/components/render_cell_value';
|
||||
import { addDisplayNames } from '../pages/alerts/containers/alerts_table_t_grid/add_display_names';
|
||||
import { columns as alertO11yColumns } from '../pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid';
|
||||
import { getRowActions } from '../pages/alerts/containers/alerts_table_t_grid/get_row_actions';
|
||||
import { addDisplayNames } from '../pages/alerts/containers/alerts_table/add_display_names';
|
||||
import { columns as alertO11yColumns } from '../pages/alerts/containers/alerts_table/default_columns';
|
||||
import { getRowActions } from '../pages/alerts/containers/alerts_table/get_row_actions';
|
||||
import type { ObservabilityRuleTypeRegistry } from '../rules/create_observability_rule_type_registry';
|
||||
import type { ConfigSchema } from '../plugin';
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
ADD_TO_CASE_DISABLED,
|
||||
ADD_TO_EXISTING_CASE,
|
||||
ADD_TO_NEW_CASE,
|
||||
} from '../pages/alerts/containers/alerts_table_t_grid/translations';
|
||||
} from '../pages/alerts/containers/alerts_table/translations';
|
||||
import { useGetUserCasesPermissions } from './use_get_user_cases_permissions';
|
||||
import { ObservabilityAppServices } from '../application/types';
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { TimelineNonEcsData } from '@kbn/timelines-plugin/common/search_strategy';
|
||||
import { TGridCellAction } from '@kbn/timelines-plugin/common/types/timeline';
|
||||
import { getPageRowIndex } from '@kbn/timelines-plugin/public';
|
||||
import FilterForValueButton from './filter_for_value';
|
||||
import { getMappedNonEcsValue } from './render_cell_value';
|
||||
|
||||
export const FILTER_FOR_VALUE = i18n.translate('xpack.observability.hoverActions.filterForValue', {
|
||||
defaultMessage: 'Filter for value',
|
||||
});
|
||||
|
||||
/** actions for adding filters to the search bar */
|
||||
const buildFilterCellActions = (addToQuery: (value: string) => void): TGridCellAction[] => [
|
||||
({ data, pageSize }: { data: TimelineNonEcsData[][]; pageSize: number }) =>
|
||||
({ rowIndex, columnId, Component }) => {
|
||||
const value = getMappedNonEcsValue({
|
||||
data: data[getPageRowIndex(rowIndex, pageSize)],
|
||||
fieldName: columnId,
|
||||
});
|
||||
|
||||
return (
|
||||
<FilterForValueButton
|
||||
Component={Component}
|
||||
field={columnId}
|
||||
value={value}
|
||||
addToQuery={addToQuery}
|
||||
/>
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
/** returns the default actions shown in `EuiDataGrid` cells */
|
||||
export const getDefaultCellActions = ({ addToQuery }: { addToQuery: (value: string) => void }) =>
|
||||
buildFilterCellActions(addToQuery);
|
|
@ -10,7 +10,6 @@ export * from './render_cell_value';
|
|||
export * from './severity_badge';
|
||||
export * from './workflow_status_filter';
|
||||
export * from './alerts_search_bar';
|
||||
export * from './default_cell_actions';
|
||||
export * from './filter_for_value';
|
||||
export * from './parse_alert';
|
||||
export * from './alerts_status_filter';
|
||||
|
|
|
@ -24,10 +24,7 @@ import { useKibana } from '../../../utils/kibana_react';
|
|||
import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions';
|
||||
import { parseAlert } from './parse_alert';
|
||||
import { translations, paths } from '../../../config';
|
||||
import {
|
||||
ADD_TO_EXISTING_CASE,
|
||||
ADD_TO_NEW_CASE,
|
||||
} from '../containers/alerts_table_t_grid/translations';
|
||||
import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from '../containers/alerts_table/translations';
|
||||
import { ObservabilityAppServices } from '../../../application/types';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types';
|
||||
import type { TopAlert } from '../containers/alerts_page/types';
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { getRenderCellValue, getMappedNonEcsValue } from './render_cell_value';
|
||||
export { getRenderCellValue } from './render_cell_value';
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
*/
|
||||
import { ALERT_DURATION, ALERT_REASON, ALERT_STATUS, TIMESTAMP } from '@kbn/rule-data-utils';
|
||||
import { EuiDataGridColumn } from '@elastic/eui';
|
||||
import type { ColumnHeaderOptions } from '@kbn/timelines-plugin/common';
|
||||
import { translations } from '../../../../config';
|
||||
|
||||
export const addDisplayNames = (
|
||||
column: Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> &
|
||||
ColumnHeaderOptions
|
||||
column: Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'>
|
||||
) => {
|
||||
if (column.id === ALERT_REASON) {
|
||||
return { ...column, displayAsText: translations.alertsTable.reasonColumnDescription };
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* We need to produce types and code transpilation at different folders during the build of the package.
|
||||
* We have types and code at different imports because we don't want to import the whole package in the resulting webpack bundle for the plugin.
|
||||
* This way plugins can do targeted imports to reduce the final code bundle
|
||||
*/
|
||||
import { ALERT_DURATION, ALERT_REASON, ALERT_STATUS, TIMESTAMP } from '@kbn/rule-data-utils';
|
||||
|
||||
import { EuiDataGridColumn } from '@elastic/eui';
|
||||
|
||||
import type { ColumnHeaderOptions } from '@kbn/timelines-plugin/common';
|
||||
|
||||
import { translations } from '../../../../config';
|
||||
|
||||
/**
|
||||
* columns implements a subset of `EuiDataGrid`'s `EuiDataGridColumn` interface,
|
||||
* plus additional TGrid column properties
|
||||
*/
|
||||
export const columns: Array<
|
||||
Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> & ColumnHeaderOptions
|
||||
> = [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.statusColumnDescription,
|
||||
id: ALERT_STATUS,
|
||||
initialWidth: 110,
|
||||
},
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.lastUpdatedColumnDescription,
|
||||
id: TIMESTAMP,
|
||||
initialWidth: 230,
|
||||
},
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.durationColumnDescription,
|
||||
id: ALERT_DURATION,
|
||||
initialWidth: 116,
|
||||
},
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.reasonColumnDescription,
|
||||
id: ALERT_REASON,
|
||||
linkField: '*',
|
||||
},
|
||||
];
|
|
@ -1,331 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* We need to produce types and code transpilation at different folders during the build of the package.
|
||||
* We have types and code at different imports because we don't want to import the whole package in the resulting webpack bundle for the plugin.
|
||||
* This way plugins can do targeted imports to reduce the final code bundle
|
||||
*/
|
||||
import {
|
||||
ALERT_DURATION,
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
ALERT_RULE_CATEGORY,
|
||||
ALERT_RULE_NAME,
|
||||
ALERT_STATUS,
|
||||
ALERT_UUID,
|
||||
TIMESTAMP,
|
||||
ALERT_START,
|
||||
} from '@kbn/rule-data-utils';
|
||||
|
||||
import { EuiDataGridColumn, EuiFlexGroup } from '@elastic/eui';
|
||||
|
||||
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import React, { Suspense, useMemo, useState, useCallback, useEffect } from 'react';
|
||||
|
||||
import { pick } from 'lodash';
|
||||
import type {
|
||||
TGridType,
|
||||
TGridState,
|
||||
TGridModel,
|
||||
SortDirection,
|
||||
} from '@kbn/timelines-plugin/public';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import type {
|
||||
ActionProps,
|
||||
ColumnHeaderOptions,
|
||||
ControlColumnProps,
|
||||
RowRenderer,
|
||||
} from '@kbn/timelines-plugin/common';
|
||||
import { getAlertsPermissions } from '../../../../hooks/use_alert_permission';
|
||||
|
||||
import type { TopAlert } from '../alerts_page/types';
|
||||
|
||||
import { getRenderCellValue } from '../../components/render_cell_value';
|
||||
import { observabilityAppId, observabilityFeatureId } from '../../../../../common';
|
||||
import { useGetUserCasesPermissions } from '../../../../hooks/use_get_user_cases_permissions';
|
||||
import { usePluginContext } from '../../../../hooks/use_plugin_context';
|
||||
import { LazyAlertsFlyout } from '../../../..';
|
||||
import { translations } from '../../../../config';
|
||||
import { addDisplayNames } from './add_display_names';
|
||||
import { ObservabilityAppServices } from '../../../../application/types';
|
||||
import { useBulkAddToCaseActions } from '../../../../hooks/use_alert_bulk_case_actions';
|
||||
import {
|
||||
ObservabilityActions,
|
||||
ObservabilityActionsProps,
|
||||
} from '../../components/observability_actions';
|
||||
|
||||
interface AlertsTableTGridProps {
|
||||
indexNames: string[];
|
||||
rangeFrom: string;
|
||||
rangeTo: string;
|
||||
kuery?: string;
|
||||
stateStorageKey: string;
|
||||
storage: IStorageWrapper;
|
||||
setRefetch: (ref: () => void) => void;
|
||||
itemsPerPage?: number;
|
||||
}
|
||||
|
||||
const EventsThContent = styled.div.attrs(({ className = '' }) => ({
|
||||
className: `siemEventsTable__thContent ${className}`,
|
||||
}))<{ textAlign?: string; width?: number }>`
|
||||
font-size: ${({ theme }) => theme.eui.euiFontSizeXS};
|
||||
font-weight: ${({ theme }) => theme.eui.euiFontWeightBold};
|
||||
line-height: ${({ theme }) => theme.eui.euiLineHeight};
|
||||
min-width: 0;
|
||||
padding: ${({ theme }) => theme.eui.euiSizeXS};
|
||||
text-align: ${({ textAlign }) => textAlign};
|
||||
width: ${({ width }) =>
|
||||
width != null
|
||||
? `${width}px`
|
||||
: '100%'}; /* Using width: 100% instead of flex: 1 and max-width: 100% for IE11 */
|
||||
|
||||
> button.euiButtonIcon,
|
||||
> .euiToolTipAnchor > button.euiButtonIcon {
|
||||
margin-left: ${({ theme }) => `-${theme.eui.euiSizeXS}`};
|
||||
}
|
||||
`;
|
||||
/**
|
||||
* columns implements a subset of `EuiDataGrid`'s `EuiDataGridColumn` interface,
|
||||
* plus additional TGrid column properties
|
||||
*/
|
||||
export const columns: Array<
|
||||
Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> & ColumnHeaderOptions
|
||||
> = [
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.statusColumnDescription,
|
||||
id: ALERT_STATUS,
|
||||
initialWidth: 110,
|
||||
},
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.lastUpdatedColumnDescription,
|
||||
id: TIMESTAMP,
|
||||
initialWidth: 230,
|
||||
},
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.durationColumnDescription,
|
||||
id: ALERT_DURATION,
|
||||
initialWidth: 116,
|
||||
},
|
||||
{
|
||||
columnHeaderType: 'not-filtered',
|
||||
displayAsText: translations.alertsTable.reasonColumnDescription,
|
||||
id: ALERT_REASON,
|
||||
linkField: '*',
|
||||
},
|
||||
];
|
||||
|
||||
const NO_ROW_RENDER: RowRenderer[] = [];
|
||||
|
||||
const trailingControlColumns: never[] = [];
|
||||
|
||||
const FIELDS_WITHOUT_CELL_ACTIONS = [
|
||||
'@timestamp',
|
||||
'signal.rule.risk_score',
|
||||
'signal.reason',
|
||||
'kibana.alert.duration.us',
|
||||
'kibana.alert.reason',
|
||||
];
|
||||
|
||||
export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
||||
const {
|
||||
indexNames,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
kuery,
|
||||
setRefetch,
|
||||
stateStorageKey,
|
||||
storage,
|
||||
itemsPerPage,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
timelines,
|
||||
application: { capabilities },
|
||||
} = useKibana<ObservabilityAppServices>().services;
|
||||
const { observabilityRuleTypeRegistry, config } = usePluginContext();
|
||||
|
||||
const [flyoutAlert, setFlyoutAlert] = useState<TopAlert | undefined>(undefined);
|
||||
const [tGridState, setTGridState] = useState<Partial<TGridModel> | null>(
|
||||
storage.get(stateStorageKey)
|
||||
);
|
||||
|
||||
const userCasesPermissions = useGetUserCasesPermissions();
|
||||
|
||||
const hasAlertsCrudPermissions = useCallback(
|
||||
({ ruleConsumer, ruleProducer }: { ruleConsumer: string; ruleProducer?: string }) => {
|
||||
if (ruleConsumer === 'alerts' && ruleProducer) {
|
||||
return getAlertsPermissions(capabilities, ruleProducer).crud;
|
||||
}
|
||||
return getAlertsPermissions(capabilities, ruleConsumer).crud;
|
||||
},
|
||||
[capabilities]
|
||||
);
|
||||
|
||||
const [deletedEventIds, setDeletedEventIds] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tGridState) {
|
||||
const newState = {
|
||||
...tGridState,
|
||||
columns: tGridState.columns?.map((c) =>
|
||||
pick(c, ['columnHeaderType', 'displayAsText', 'id', 'initialWidth', 'linkField'])
|
||||
),
|
||||
};
|
||||
if (newState !== storage.get(stateStorageKey)) {
|
||||
storage.set(stateStorageKey, newState);
|
||||
}
|
||||
}
|
||||
}, [tGridState, stateStorageKey, storage]);
|
||||
|
||||
const setEventsDeleted = useCallback<ObservabilityActionsProps['setEventsDeleted']>((action) => {
|
||||
if (action.isDeleted) {
|
||||
setDeletedEventIds((ids) => [...ids, ...action.eventIds]);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const leadingControlColumns: ControlColumnProps[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
id: 'expand',
|
||||
width: 120,
|
||||
headerCellRender: () => {
|
||||
return <EventsThContent>{translations.alertsTable.actionsTextLabel}</EventsThContent>;
|
||||
},
|
||||
rowCellRender: (actionProps: ActionProps) => {
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="none" responsive={false}>
|
||||
<ObservabilityActions
|
||||
{...actionProps}
|
||||
setEventsDeleted={setEventsDeleted}
|
||||
setFlyoutAlert={setFlyoutAlert}
|
||||
observabilityRuleTypeRegistry={observabilityRuleTypeRegistry}
|
||||
config={config}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}, [setEventsDeleted, observabilityRuleTypeRegistry, config]);
|
||||
|
||||
const onStateChange = useCallback(
|
||||
(state: TGridState) => {
|
||||
const pickedState = pick(state.tableById['standalone-t-grid'], [
|
||||
'columns',
|
||||
'sort',
|
||||
'selectedEventIds',
|
||||
]);
|
||||
if (JSON.stringify(pickedState) !== JSON.stringify(tGridState)) {
|
||||
setTGridState(pickedState);
|
||||
}
|
||||
},
|
||||
[tGridState]
|
||||
);
|
||||
|
||||
const addToCaseBulkActions = useBulkAddToCaseActions();
|
||||
const bulkActions = useMemo(
|
||||
() => ({
|
||||
alertStatusActions: false,
|
||||
customBulkActions: addToCaseBulkActions,
|
||||
}),
|
||||
[addToCaseBulkActions]
|
||||
);
|
||||
const tGridProps = useMemo(() => {
|
||||
const type: TGridType = 'standalone';
|
||||
const sortDirection: SortDirection = 'desc';
|
||||
return {
|
||||
appId: observabilityAppId,
|
||||
casesOwner: observabilityFeatureId,
|
||||
casePermissions: userCasesPermissions,
|
||||
type,
|
||||
columns: (tGridState?.columns ?? columns).map(addDisplayNames),
|
||||
deletedEventIds,
|
||||
disabledCellActions: FIELDS_WITHOUT_CELL_ACTIONS,
|
||||
end: rangeTo,
|
||||
filters: [],
|
||||
hasAlertsCrudPermissions,
|
||||
indexNames,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions: [10, 25, 50],
|
||||
loadingText: translations.alertsTable.loadingTextLabel,
|
||||
onStateChange,
|
||||
query: {
|
||||
query: kuery ?? '',
|
||||
language: 'kuery',
|
||||
},
|
||||
renderCellValue: getRenderCellValue({ setFlyoutAlert, observabilityRuleTypeRegistry }),
|
||||
rowRenderers: NO_ROW_RENDER,
|
||||
// TODO: implement Kibana data view runtime fields in observability
|
||||
runtimeMappings: {},
|
||||
start: rangeFrom,
|
||||
setRefetch,
|
||||
bulkActions,
|
||||
sort: tGridState?.sort ?? [
|
||||
{
|
||||
columnId: '@timestamp',
|
||||
columnType: 'date',
|
||||
sortDirection,
|
||||
},
|
||||
],
|
||||
queryFields: [
|
||||
ALERT_DURATION,
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
ALERT_RULE_CATEGORY,
|
||||
ALERT_RULE_NAME,
|
||||
ALERT_STATUS,
|
||||
ALERT_UUID,
|
||||
ALERT_START,
|
||||
TIMESTAMP,
|
||||
],
|
||||
leadingControlColumns,
|
||||
trailingControlColumns,
|
||||
unit: (totalAlerts: number) => translations.alertsTable.showingAlertsTitle(totalAlerts),
|
||||
};
|
||||
}, [
|
||||
userCasesPermissions,
|
||||
tGridState?.columns,
|
||||
tGridState?.sort,
|
||||
deletedEventIds,
|
||||
rangeTo,
|
||||
hasAlertsCrudPermissions,
|
||||
indexNames,
|
||||
itemsPerPage,
|
||||
observabilityRuleTypeRegistry,
|
||||
onStateChange,
|
||||
kuery,
|
||||
rangeFrom,
|
||||
setRefetch,
|
||||
bulkActions,
|
||||
leadingControlColumns,
|
||||
]);
|
||||
|
||||
const handleFlyoutClose = () => setFlyoutAlert(undefined);
|
||||
|
||||
return (
|
||||
<>
|
||||
{flyoutAlert && (
|
||||
<Suspense fallback={null}>
|
||||
<LazyAlertsFlyout
|
||||
alert={flyoutAlert}
|
||||
observabilityRuleTypeRegistry={observabilityRuleTypeRegistry}
|
||||
onClose={handleFlyoutClose}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
{timelines.getTGrid<'standalone'>(tGridProps)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* 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 { AlertsTableTGrid } from './alerts_table_t_grid';
|
|
@ -6,5 +6,4 @@
|
|||
*/
|
||||
|
||||
export * from './alerts_page';
|
||||
export * from './alerts_table_t_grid';
|
||||
export * from './state_container';
|
||||
|
|
|
@ -67,7 +67,6 @@ export const useAlertsActions = ({
|
|||
setEventsDeleted,
|
||||
onUpdateSuccess: onStatusUpdate,
|
||||
onUpdateFailure: onStatusUpdate,
|
||||
scopeId,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -92,7 +92,6 @@ export interface BulkActionsProps {
|
|||
onUpdateSuccess?: OnUpdateAlertStatusSuccess;
|
||||
onUpdateFailure?: OnUpdateAlertStatusError;
|
||||
customBulkActions?: CustomBulkActionProp[];
|
||||
scopeId?: string;
|
||||
}
|
||||
|
||||
export interface HeaderActionProps {
|
||||
|
|
|
@ -6,18 +6,16 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import type { Store } from 'redux';
|
||||
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { createStore } from '../store/t_grid';
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
import { TGrid as TGridComponent } from './t_grid';
|
||||
import type { TGridProps } from '../types';
|
||||
import { DragDropContextWrapper } from './drag_and_drop';
|
||||
import { initialTGridState } from '../store/t_grid/reducer';
|
||||
import type { TGridIntegratedProps } from './t_grid/integrated';
|
||||
|
||||
const EMPTY_BROWSER_FIELDS = {};
|
||||
|
@ -31,18 +29,13 @@ type TGridComponent = TGridProps & {
|
|||
|
||||
export const TGrid = (props: TGridComponent) => {
|
||||
const { store, storage, setStore, ...tGridProps } = props;
|
||||
let tGridStore = store;
|
||||
if (!tGridStore && props.type === 'standalone') {
|
||||
tGridStore = createStore(initialTGridState, storage);
|
||||
setStore(tGridStore);
|
||||
}
|
||||
let browserFields = EMPTY_BROWSER_FIELDS;
|
||||
if ((tGridProps as TGridIntegratedProps).browserFields != null) {
|
||||
browserFields = (tGridProps as TGridIntegratedProps).browserFields;
|
||||
}
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
<Provider store={tGridStore!}>
|
||||
<Provider store={store!}>
|
||||
<I18nProvider>
|
||||
<DragDropContextWrapper browserFields={browserFields} defaultsHeader={props.columns}>
|
||||
<TGridComponent {...tGridProps} />
|
||||
|
|
|
@ -9,13 +9,10 @@ import React from 'react';
|
|||
|
||||
import type { TGridProps } from '../../types';
|
||||
import { TGridIntegrated, TGridIntegratedProps } from './integrated';
|
||||
import { TGridStandalone, TGridStandaloneProps } from './standalone';
|
||||
|
||||
export const TGrid = (props: TGridProps) => {
|
||||
const { type, ...componentsProps } = props;
|
||||
if (type === 'standalone') {
|
||||
return <TGridStandalone {...(componentsProps as unknown as TGridStandaloneProps)} />;
|
||||
} else if (type === 'embedded') {
|
||||
if (type === 'embedded') {
|
||||
return <TGridIntegrated {...(componentsProps as unknown as TGridIntegratedProps)} />;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,394 +0,0 @@
|
|||
/*
|
||||
* 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 { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import type { Filter, Query } from '@kbn/es-query';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { getEsQueryConfig } from '@kbn/data-plugin/common';
|
||||
|
||||
import type { Ecs } from '../../../../common/ecs';
|
||||
import { Direction, EntityType } from '../../../../common/search_strategy';
|
||||
import { TGridCellAction } from '../../../../common/types/timeline';
|
||||
import type {
|
||||
CellValueElementProps,
|
||||
ColumnHeaderOptions,
|
||||
ControlColumnProps,
|
||||
DataProvider,
|
||||
RowRenderer,
|
||||
SortColumnTable,
|
||||
BulkActionsProp,
|
||||
AlertStatus,
|
||||
} from '../../../../common/types/timeline';
|
||||
import { useDeepEqualSelector } from '../../../hooks/use_selector';
|
||||
import { defaultHeaders } from '../body/column_headers/default_headers';
|
||||
import { getCombinedFilterQuery } from '../helpers';
|
||||
import { tGridActions, tGridSelectors } from '../../../store/t_grid';
|
||||
import type { State } from '../../../store/t_grid';
|
||||
import { useTimelineEvents } from '../../../container';
|
||||
import { StatefulBody } from '../body';
|
||||
import { LastUpdatedAt } from '../..';
|
||||
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER, UpdatedFlexItem, UpdatedFlexGroup } from '../styles';
|
||||
import { InspectButton, InspectButtonContainer } from '../../inspect';
|
||||
import { useFetchIndex } from '../../../container/source';
|
||||
import { TGridLoading, TGridEmpty, TableContext } from '../shared';
|
||||
|
||||
const FullWidthFlexGroup = styled(EuiFlexGroup)<{ $visible: boolean }>`
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
display: ${({ $visible }) => ($visible ? 'flex' : 'none')};
|
||||
`;
|
||||
|
||||
export const EVENTS_VIEWER_HEADER_HEIGHT = 90; // px
|
||||
export const STANDALONE_ID = 'standalone-t-grid';
|
||||
const EMPTY_DATA_PROVIDERS: DataProvider[] = [];
|
||||
|
||||
const TitleText = styled.span`
|
||||
margin-right: 12px;
|
||||
`;
|
||||
|
||||
const AlertsTableWrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const EventsContainerLoading = styled.div.attrs(({ className = '' }) => ({
|
||||
className: `${SELECTOR_TIMELINE_GLOBAL_CONTAINER} ${className}`,
|
||||
}))`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const ScrollableFlexItem = styled(EuiFlexItem)`
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export interface TGridStandaloneProps {
|
||||
columns: ColumnHeaderOptions[];
|
||||
dataViewId?: string | null;
|
||||
defaultCellActions?: TGridCellAction[];
|
||||
deletedEventIds: Readonly<string[]>;
|
||||
disabledCellActions: string[];
|
||||
end: string;
|
||||
entityType?: EntityType;
|
||||
loadingText: React.ReactNode;
|
||||
filters: Filter[];
|
||||
filterStatus?: AlertStatus;
|
||||
getRowRenderer?: ({
|
||||
data,
|
||||
rowRenderers,
|
||||
}: {
|
||||
data: Ecs;
|
||||
rowRenderers: RowRenderer[];
|
||||
}) => RowRenderer | null;
|
||||
hasAlertsCrudPermissions: ({
|
||||
ruleConsumer,
|
||||
ruleProducer,
|
||||
}: {
|
||||
ruleConsumer: string;
|
||||
ruleProducer?: string;
|
||||
}) => boolean;
|
||||
height?: number;
|
||||
indexNames: string[];
|
||||
itemsPerPage?: number;
|
||||
itemsPerPageOptions: number[];
|
||||
query: Query;
|
||||
onRuleChange?: () => void;
|
||||
onStateChange?: (state: State) => void;
|
||||
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
|
||||
rowRenderers: RowRenderer[];
|
||||
runtimeMappings: MappingRuntimeFields;
|
||||
setRefetch: (ref: () => void) => void;
|
||||
start: string;
|
||||
sort: SortColumnTable[];
|
||||
graphEventId?: string;
|
||||
leadingControlColumns: ControlColumnProps[];
|
||||
trailingControlColumns: ControlColumnProps[];
|
||||
bulkActions?: BulkActionsProp;
|
||||
data?: DataPublicPluginStart;
|
||||
unit?: (total: number) => React.ReactNode;
|
||||
showCheckboxes?: boolean;
|
||||
queryFields?: string[];
|
||||
}
|
||||
|
||||
const TGridStandaloneComponent: React.FC<TGridStandaloneProps> = ({
|
||||
columns,
|
||||
dataViewId = null,
|
||||
defaultCellActions,
|
||||
deletedEventIds,
|
||||
disabledCellActions,
|
||||
end,
|
||||
entityType = 'alerts',
|
||||
loadingText,
|
||||
filters,
|
||||
filterStatus,
|
||||
getRowRenderer,
|
||||
hasAlertsCrudPermissions,
|
||||
indexNames,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
onRuleChange,
|
||||
query,
|
||||
renderCellValue,
|
||||
rowRenderers,
|
||||
runtimeMappings,
|
||||
setRefetch,
|
||||
start,
|
||||
sort,
|
||||
graphEventId,
|
||||
leadingControlColumns,
|
||||
trailingControlColumns,
|
||||
data,
|
||||
unit,
|
||||
showCheckboxes = true,
|
||||
bulkActions = {},
|
||||
queryFields = [],
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
|
||||
const { uiSettings } = useKibana<CoreStart>().services;
|
||||
const [indexPatternsLoading, { browserFields, indexPatterns }] = useFetchIndex(indexNames);
|
||||
|
||||
const getTGrid = useMemo(() => tGridSelectors.getTGridByIdSelector(), []);
|
||||
const {
|
||||
itemsPerPage: itemsPerPageStore,
|
||||
itemsPerPageOptions: itemsPerPageOptionsStore,
|
||||
queryFields: queryFieldsFromState,
|
||||
sort: sortStore,
|
||||
title,
|
||||
} = useDeepEqualSelector((state) => getTGrid(state, STANDALONE_ID ?? ''));
|
||||
|
||||
const justTitle = useMemo(() => <TitleText data-test-subj="title">{title}</TitleText>, [title]);
|
||||
const esQueryConfig = getEsQueryConfig(uiSettings);
|
||||
|
||||
const filterQuery = useMemo(
|
||||
() =>
|
||||
getCombinedFilterQuery({
|
||||
config: esQueryConfig,
|
||||
browserFields,
|
||||
dataProviders: EMPTY_DATA_PROVIDERS,
|
||||
filters,
|
||||
from: start,
|
||||
indexPattern: indexPatterns,
|
||||
kqlMode: 'search',
|
||||
kqlQuery: query,
|
||||
to: end,
|
||||
}),
|
||||
[esQueryConfig, indexPatterns, browserFields, filters, start, end, query]
|
||||
);
|
||||
|
||||
const canQueryTimeline = useMemo(
|
||||
() =>
|
||||
filterQuery != null &&
|
||||
indexPatternsLoading != null &&
|
||||
!indexPatternsLoading &&
|
||||
!isEmpty(start) &&
|
||||
!isEmpty(end),
|
||||
[indexPatternsLoading, filterQuery, start, end]
|
||||
);
|
||||
|
||||
const fields = useMemo(
|
||||
() => [
|
||||
...columnsHeader.reduce<string[]>(
|
||||
(acc, c) => (c.linkField != null ? [...acc, c.id, c.linkField] : [...acc, c.id]),
|
||||
[]
|
||||
),
|
||||
...(queryFieldsFromState ?? []),
|
||||
],
|
||||
[columnsHeader, queryFieldsFromState]
|
||||
);
|
||||
|
||||
const sortField = useMemo(
|
||||
() =>
|
||||
sortStore.map(({ columnId, columnType, esTypes, sortDirection }) => ({
|
||||
field: columnId,
|
||||
type: columnType,
|
||||
direction: sortDirection as Direction,
|
||||
esTypes: esTypes ?? [],
|
||||
})),
|
||||
[sortStore]
|
||||
);
|
||||
|
||||
const [
|
||||
loading,
|
||||
{ consumers, events, updatedAt, loadPage, pageInfo, refetch, totalCount = 0, inspect },
|
||||
] = useTimelineEvents({
|
||||
dataViewId,
|
||||
entityType,
|
||||
excludeEcsData: true,
|
||||
fields,
|
||||
filterQuery,
|
||||
id: STANDALONE_ID,
|
||||
indexNames,
|
||||
limit: itemsPerPageStore,
|
||||
runtimeMappings,
|
||||
sort: sortField,
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
skip: !canQueryTimeline,
|
||||
data,
|
||||
});
|
||||
setRefetch(refetch);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(tGridActions.updateIsLoading({ id: STANDALONE_ID, isLoading: loading }));
|
||||
}, [dispatch, loading]);
|
||||
|
||||
const { hasAlertsCrud, totalSelectAllAlerts } = useMemo(() => {
|
||||
return Object.entries(consumers).reduce<{
|
||||
hasAlertsCrud: boolean;
|
||||
totalSelectAllAlerts: number;
|
||||
}>(
|
||||
(acc, [ruleConsumer, nbrAlerts]) => {
|
||||
const featureHasPermission = hasAlertsCrudPermissions({ ruleConsumer });
|
||||
return {
|
||||
hasAlertsCrud: featureHasPermission || acc.hasAlertsCrud,
|
||||
totalSelectAllAlerts: featureHasPermission
|
||||
? nbrAlerts + acc.totalSelectAllAlerts
|
||||
: acc.totalSelectAllAlerts,
|
||||
};
|
||||
},
|
||||
{
|
||||
hasAlertsCrud: false,
|
||||
totalSelectAllAlerts: 0,
|
||||
}
|
||||
);
|
||||
}, [consumers, hasAlertsCrudPermissions]);
|
||||
|
||||
const totalCountMinusDeleted = useMemo(
|
||||
() => (totalCount > 0 ? totalCount - deletedEventIds.length : 0),
|
||||
[deletedEventIds.length, totalCount]
|
||||
);
|
||||
const hasAlerts = totalCountMinusDeleted > 0;
|
||||
|
||||
// Only show the table-spanning loading indicator when the query is loading and we
|
||||
// don't have data (e.g. for the initial fetch).
|
||||
// Subsequent fetches (e.g. for pagination) will show a small loading indicator on
|
||||
// top of the table and the table will display the current page until the next page
|
||||
// is fetched. This prevents a flicker when paginating.
|
||||
const showFullLoading = loading && !hasAlerts;
|
||||
|
||||
const nonDeletedEvents = useMemo(
|
||||
() => events.filter((e) => !deletedEventIds.includes(e._id)),
|
||||
[deletedEventIds, events]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
tGridActions.createTGrid({
|
||||
id: STANDALONE_ID,
|
||||
columns,
|
||||
indexNames,
|
||||
itemsPerPage: itemsPerPage || itemsPerPageStore,
|
||||
itemsPerPageOptions,
|
||||
showCheckboxes,
|
||||
defaultColumns: columns,
|
||||
sort,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
tGridActions.initializeTGridSettings({
|
||||
id: STANDALONE_ID,
|
||||
defaultColumns: columns,
|
||||
sort,
|
||||
loadingText,
|
||||
unit,
|
||||
queryFields,
|
||||
})
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const tableContext = { tableId: STANDALONE_ID };
|
||||
|
||||
// Clear checkbox selection when new events are fetched
|
||||
useEffect(() => {
|
||||
dispatch(tGridActions.clearSelected({ id: STANDALONE_ID }));
|
||||
dispatch(
|
||||
tGridActions.setTGridSelectAll({
|
||||
id: STANDALONE_ID,
|
||||
selectAll: false,
|
||||
})
|
||||
);
|
||||
}, [nonDeletedEvents, dispatch]);
|
||||
|
||||
return (
|
||||
<InspectButtonContainer data-test-subj="events-viewer-panel">
|
||||
<AlertsTableWrapper>
|
||||
{showFullLoading && <TGridLoading />}
|
||||
{canQueryTimeline ? (
|
||||
<TableContext.Provider value={tableContext}>
|
||||
<EventsContainerLoading
|
||||
data-timeline-id={STANDALONE_ID}
|
||||
data-test-subj={`events-container-loading-${loading}`}
|
||||
>
|
||||
<UpdatedFlexGroup gutterSize="s" justifyContent="flexEnd" alignItems="center">
|
||||
<UpdatedFlexItem grow={false} $show={!loading}>
|
||||
<InspectButton title={justTitle} inspect={inspect} loading={loading} />
|
||||
</UpdatedFlexItem>
|
||||
<UpdatedFlexItem grow={false} $show={!loading}>
|
||||
<LastUpdatedAt updatedAt={updatedAt} />
|
||||
</UpdatedFlexItem>
|
||||
</UpdatedFlexGroup>
|
||||
|
||||
{!hasAlerts && !loading && <TGridEmpty />}
|
||||
|
||||
{hasAlerts && (
|
||||
<FullWidthFlexGroup direction="row" $visible={!graphEventId} gutterSize="none">
|
||||
<ScrollableFlexItem grow={1}>
|
||||
<StatefulBody
|
||||
activePage={pageInfo.activePage}
|
||||
browserFields={browserFields}
|
||||
data={nonDeletedEvents}
|
||||
defaultCellActions={defaultCellActions}
|
||||
disabledCellActions={disabledCellActions}
|
||||
filterQuery={filterQuery}
|
||||
getRowRenderer={getRowRenderer}
|
||||
hasAlertsCrud={hasAlertsCrud}
|
||||
hasAlertsCrudPermissions={hasAlertsCrudPermissions}
|
||||
id={STANDALONE_ID}
|
||||
indexNames={indexNames}
|
||||
isEventViewer={true}
|
||||
itemsPerPageOptions={itemsPerPageOptionsStore}
|
||||
leadingControlColumns={leadingControlColumns}
|
||||
loadPage={loadPage}
|
||||
refetch={refetch}
|
||||
renderCellValue={renderCellValue}
|
||||
rowRenderers={rowRenderers}
|
||||
onRuleChange={onRuleChange}
|
||||
pageSize={itemsPerPageStore}
|
||||
tabType={'query'}
|
||||
tableView="gridView"
|
||||
totalItems={totalCountMinusDeleted}
|
||||
totalSelectAllAlerts={totalSelectAllAlerts}
|
||||
unit={unit}
|
||||
filterStatus={filterStatus}
|
||||
trailingControlColumns={trailingControlColumns}
|
||||
showCheckboxes={showCheckboxes}
|
||||
bulkActions={bulkActions}
|
||||
/>
|
||||
</ScrollableFlexItem>
|
||||
</FullWidthFlexGroup>
|
||||
)}
|
||||
</EventsContainerLoading>
|
||||
</TableContext.Provider>
|
||||
) : null}
|
||||
</AlertsTableWrapper>
|
||||
</InspectButtonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const TGridStandalone = React.memo(TGridStandaloneComponent);
|
|
@ -127,7 +127,6 @@ export const AlertBulkActionsComponent = React.memo<StatefulAlertBulkActionsProp
|
|||
onUpdateSuccess,
|
||||
onUpdateFailure,
|
||||
customBulkActions,
|
||||
scopeId: id,
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -12,7 +12,6 @@ import * as i18n from '../components/t_grid/translations';
|
|||
import type { AlertStatus, BulkActionsProps } from '../../common/types/timeline';
|
||||
import { useUpdateAlertsStatus } from '../container/use_update_alerts';
|
||||
import { useAppToasts } from './use_app_toasts';
|
||||
import { STANDALONE_ID } from '../components/t_grid/standalone';
|
||||
import { useStartTransaction } from '../lib/apm/use_start_transaction';
|
||||
import { APM_USER_INTERACTIONS } from '../lib/apm/constants';
|
||||
|
||||
|
@ -31,9 +30,8 @@ export const useBulkActionItems = ({
|
|||
onUpdateSuccess,
|
||||
onUpdateFailure,
|
||||
customBulkActions,
|
||||
scopeId,
|
||||
}: BulkActionsProps) => {
|
||||
const { updateAlertStatus } = useUpdateAlertsStatus(scopeId !== STANDALONE_ID);
|
||||
const { updateAlertStatus } = useUpdateAlertsStatus(true);
|
||||
const { addSuccess, addError, addWarning } = useAppToasts();
|
||||
const { startTransaction } = useStartTransaction();
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ export const getTGridLazy = (
|
|||
) => {
|
||||
initializeStore({ store, storage, setStore });
|
||||
return (
|
||||
<Suspense fallback={<TGridLoading height={props.type === 'standalone' ? 'tall' : 'short'} />}>
|
||||
<Suspense fallback={<TGridLoading height={'short'} />}>
|
||||
<TimelineLazy {...props} store={store} storage={storage} data={data} setStore={setStore} />
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import { Store, Unsubscribe } from 'redux';
|
||||
import { throttle } from 'lodash';
|
||||
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { CoreSetup, Plugin, CoreStart } from '@kbn/core/public';
|
||||
import type { LastUpdatedAtProps, LoadingPanelProps } from './components';
|
||||
|
@ -40,20 +38,6 @@ export class TimelinesPlugin implements Plugin<void, TimelinesUIStart> {
|
|||
}
|
||||
},
|
||||
getTGrid: (props: TGridProps) => {
|
||||
if (props.type === 'standalone' && this._store) {
|
||||
const { getState } = this._store;
|
||||
const state = getState();
|
||||
if (state && state.app) {
|
||||
this._store = undefined;
|
||||
} else {
|
||||
if (props.onStateChange) {
|
||||
this._storeUnsubscribe = this._store.subscribe(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
throttle(() => props.onStateChange!(getState()), 500)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getTGridLazy(props, {
|
||||
store: this._store,
|
||||
storage: this._storage,
|
||||
|
|
|
@ -21,7 +21,6 @@ import type {
|
|||
} from './components';
|
||||
export type { SortDirection } from '../common/types';
|
||||
import type { TGridIntegratedProps } from './components/t_grid/integrated';
|
||||
import type { TGridStandaloneProps } from './components/t_grid/standalone';
|
||||
import type { UseAddToTimelineProps, UseAddToTimeline } from './hooks/use_add_to_timeline';
|
||||
import { HoverActionsConfig } from './components/hover_actions';
|
||||
export * from './store/t_grid';
|
||||
|
@ -52,19 +51,14 @@ export interface TimelinesStartPlugins {
|
|||
}
|
||||
|
||||
export type TimelinesStartServices = CoreStart & TimelinesStartPlugins;
|
||||
interface TGridStandaloneCompProps extends TGridStandaloneProps {
|
||||
type: 'standalone';
|
||||
}
|
||||
interface TGridIntegratedCompProps extends TGridIntegratedProps {
|
||||
type: 'embedded';
|
||||
}
|
||||
export type TGridType = 'standalone' | 'embedded';
|
||||
export type GetTGridProps<T extends TGridType> = T extends 'standalone'
|
||||
? TGridStandaloneCompProps
|
||||
: T extends 'embedded'
|
||||
export type TGridType = 'embedded';
|
||||
export type GetTGridProps<T extends TGridType> = T extends 'embedded'
|
||||
? TGridIntegratedCompProps
|
||||
: TGridIntegratedCompProps;
|
||||
export type TGridProps = TGridStandaloneCompProps | TGridIntegratedCompProps;
|
||||
export type TGridProps = TGridIntegratedCompProps;
|
||||
|
||||
export interface StatefulEventContextType {
|
||||
tabType: string | undefined;
|
||||
|
|
|
@ -23587,7 +23587,6 @@
|
|||
"xpack.observability.formatters.secondsTimeUnitLabel": "s",
|
||||
"xpack.observability.formatters.secondsTimeUnitLabelExtended": "secondes",
|
||||
"xpack.observability.home.addData": "Ajouter des intégrations",
|
||||
"xpack.observability.hoverActions.filterForValue": "Filtrer sur la valeur",
|
||||
"xpack.observability.hoverActions.filterForValueButtonLabel": "Inclure",
|
||||
"xpack.observability.inspector.stats.dataViewDescription": "La vue de données qui se connecte aux index Elasticsearch.",
|
||||
"xpack.observability.inspector.stats.dataViewLabel": "Vue de données",
|
||||
|
|
|
@ -23566,7 +23566,6 @@
|
|||
"xpack.observability.formatters.secondsTimeUnitLabel": "s",
|
||||
"xpack.observability.formatters.secondsTimeUnitLabelExtended": "秒",
|
||||
"xpack.observability.home.addData": "統合の追加",
|
||||
"xpack.observability.hoverActions.filterForValue": "値でフィルター",
|
||||
"xpack.observability.hoverActions.filterForValueButtonLabel": "フィルタリング",
|
||||
"xpack.observability.inspector.stats.dataViewDescription": "Elasticsearchインデックスに接続したデータビューです。",
|
||||
"xpack.observability.inspector.stats.dataViewLabel": "データビュー",
|
||||
|
|
|
@ -23597,7 +23597,6 @@
|
|||
"xpack.observability.formatters.secondsTimeUnitLabel": "s",
|
||||
"xpack.observability.formatters.secondsTimeUnitLabelExtended": "秒",
|
||||
"xpack.observability.home.addData": "添加集成",
|
||||
"xpack.observability.hoverActions.filterForValue": "筛留值",
|
||||
"xpack.observability.hoverActions.filterForValueButtonLabel": "筛选范围",
|
||||
"xpack.observability.inspector.stats.dataViewDescription": "连接到 Elasticsearch 索引的数据视图。",
|
||||
"xpack.observability.inspector.stats.dataViewLabel": "数据视图",
|
||||
|
|
|
@ -231,15 +231,6 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* ATTENTION FUTURE DEVELOPER
|
||||
*
|
||||
* These tests should only be valid for 7.17.x
|
||||
* You can run this test if you go to this file:
|
||||
* x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx
|
||||
* and at line 397 and change showCheckboxes to true
|
||||
*
|
||||
*/
|
||||
describe.skip('Bulk Actions', () => {
|
||||
before(async () => {
|
||||
await security.testUser.setRoles(['global_alerts_logs_all_else_read']);
|
||||
|
|
|
@ -31,7 +31,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
testFiles: [
|
||||
resolve(__dirname, './test_suites/resolver'),
|
||||
resolve(__dirname, './test_suites/global_search'),
|
||||
resolve(__dirname, './test_suites/timelines'),
|
||||
],
|
||||
|
||||
services,
|
||||
|
@ -62,9 +61,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
resolverTest: {
|
||||
pathname: '/app/resolverTest',
|
||||
},
|
||||
timelineTest: {
|
||||
pathname: '/app/timelinesTest',
|
||||
},
|
||||
},
|
||||
|
||||
// choose where screenshots should be saved
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"id": "timelinesTest",
|
||||
"owner": { "name": "Security solution", "githubTeam": "security-solution" },
|
||||
"version": "1.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"configPath": ["xpack", "timelinesTest"],
|
||||
"requiredPlugins": ["timelines", "data"],
|
||||
"requiredBundles": ["kibanaReact"],
|
||||
"server": false,
|
||||
"ui": true
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* 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 { Router } from 'react-router-dom';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { TimelinesUIStart } from '@kbn/timelines-plugin/public';
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
|
||||
type CoreStartTimelines = CoreStart & { data: DataPublicPluginStart };
|
||||
|
||||
/**
|
||||
* Render the Timeline Test app. Returns a cleanup function.
|
||||
*/
|
||||
export function renderApp(
|
||||
coreStart: CoreStartTimelines,
|
||||
parameters: AppMountParameters,
|
||||
timelinesPluginSetup: TimelinesUIStart | null
|
||||
) {
|
||||
ReactDOM.render(
|
||||
<AppRoot
|
||||
coreStart={coreStart}
|
||||
parameters={parameters}
|
||||
timelinesPluginSetup={timelinesPluginSetup}
|
||||
/>,
|
||||
parameters.element
|
||||
);
|
||||
|
||||
return () => {
|
||||
ReactDOM.unmountComponentAtNode(parameters.element);
|
||||
};
|
||||
}
|
||||
|
||||
const AppRoot = React.memo(
|
||||
({
|
||||
coreStart,
|
||||
parameters,
|
||||
timelinesPluginSetup,
|
||||
}: {
|
||||
coreStart: CoreStartTimelines;
|
||||
parameters: AppMountParameters;
|
||||
timelinesPluginSetup: TimelinesUIStart | null;
|
||||
}) => {
|
||||
const refetch = useRef();
|
||||
|
||||
const setRefetch = useCallback((_refetch) => {
|
||||
refetch.current = _refetch;
|
||||
}, []);
|
||||
|
||||
const hasAlertsCrudPermissions = useCallback(() => true, []);
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<Router history={parameters.history}>
|
||||
<KibanaContextProvider services={coreStart}>
|
||||
<EuiThemeProvider>
|
||||
{(timelinesPluginSetup &&
|
||||
timelinesPluginSetup.getTGrid &&
|
||||
timelinesPluginSetup.getTGrid<'standalone'>({
|
||||
type: 'standalone',
|
||||
columns: [],
|
||||
indexNames: [],
|
||||
deletedEventIds: [],
|
||||
disabledCellActions: [],
|
||||
end: '',
|
||||
filters: [],
|
||||
hasAlertsCrudPermissions,
|
||||
itemsPerPageOptions: [1, 2, 3],
|
||||
loadingText: 'Loading events',
|
||||
renderCellValue: () => <div data-test-subj="timeline-wrapper">test</div>,
|
||||
sort: [],
|
||||
leadingControlColumns: [],
|
||||
trailingControlColumns: [],
|
||||
query: {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
},
|
||||
setRefetch,
|
||||
start: '',
|
||||
rowRenderers: [],
|
||||
runtimeMappings: {},
|
||||
filterStatus: 'open',
|
||||
unit: (n: number) => `${n}`,
|
||||
})) ??
|
||||
null}
|
||||
</EuiThemeProvider>
|
||||
</KibanaContextProvider>
|
||||
</Router>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* 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 { PluginInitializer } from '@kbn/core/public';
|
||||
import {
|
||||
TimelinesTestPlugin,
|
||||
TimelinesTestPluginSetupDependencies,
|
||||
TimelinesTestPluginStartDependencies,
|
||||
} from './plugin';
|
||||
|
||||
export const plugin: PluginInitializer<
|
||||
void,
|
||||
void,
|
||||
TimelinesTestPluginSetupDependencies,
|
||||
TimelinesTestPluginStartDependencies
|
||||
> = () => new TimelinesTestPlugin();
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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 { Plugin, CoreStart, CoreSetup, AppMountParameters } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TimelinesUIStart } from '@kbn/timelines-plugin/public';
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { renderApp } from './applications/timelines_test';
|
||||
|
||||
export type TimelinesTestPluginSetup = void;
|
||||
export type TimelinesTestPluginStart = void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface TimelinesTestPluginSetupDependencies {}
|
||||
|
||||
export interface TimelinesTestPluginStartDependencies {
|
||||
timelines: TimelinesUIStart;
|
||||
data: DataPublicPluginStart;
|
||||
}
|
||||
|
||||
export class TimelinesTestPlugin
|
||||
implements
|
||||
Plugin<
|
||||
TimelinesTestPluginSetup,
|
||||
void,
|
||||
TimelinesTestPluginSetupDependencies,
|
||||
TimelinesTestPluginStartDependencies
|
||||
>
|
||||
{
|
||||
private timelinesPlugin: TimelinesUIStart | null = null;
|
||||
public setup(
|
||||
core: CoreSetup<TimelinesTestPluginStartDependencies, TimelinesTestPluginStart>,
|
||||
setupDependencies: TimelinesTestPluginSetupDependencies
|
||||
) {
|
||||
core.application.register({
|
||||
id: 'timelinesTest',
|
||||
title: i18n.translate('xpack.timelinesTest.pluginTitle', {
|
||||
defaultMessage: 'Timelines Test',
|
||||
}),
|
||||
mount: async (params: AppMountParameters<unknown>) => {
|
||||
const startServices = await core.getStartServices();
|
||||
const [coreStart, { data }] = startServices;
|
||||
return renderApp({ ...coreStart, data }, params, this.timelinesPlugin);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public start(core: CoreStart, { timelines }: TimelinesTestPluginStartDependencies) {
|
||||
this.timelinesPlugin = timelines;
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
describe('Timelines plugin API', function () {
|
||||
const pageObjects = getPageObjects(['common']);
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
describe('timelines plugin rendering', function () {
|
||||
before(async () => {
|
||||
await pageObjects.common.navigateToApp('timelineTest');
|
||||
});
|
||||
it('shows the timeline component on navigation', async () => {
|
||||
await testSubjects.existOrFail('events-viewer-panel');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue