[RAC] Get o11y alerts in alerts table (#109346)
* get back index names in o11y * testing and integration * fix types * Avoid using the rule data client for field list * Remove left-over index argument * no needs of alert consumer anymore Co-authored-by: Felix Stürmer <stuermer@weltenwort.de>
This commit is contained in:
parent
ec2d041638
commit
e8e53e36e5
|
@ -105,7 +105,6 @@ export const CaseComponent = React.memo<CaseComponentProps>(
|
|||
const [initLoadingData, setInitLoadingData] = useState(true);
|
||||
const init = useRef(true);
|
||||
const timelineUi = useTimelineContext()?.ui;
|
||||
const alertConsumers = useTimelineContext()?.alertConsumers;
|
||||
|
||||
const {
|
||||
caseUserActions,
|
||||
|
@ -487,9 +486,7 @@ export const CaseComponent = React.memo<CaseComponentProps>(
|
|||
</EuiFlexGroup>
|
||||
</ContentWrapper>
|
||||
</WhitePageWrapper>
|
||||
{timelineUi?.renderTimelineDetailsPanel
|
||||
? timelineUi.renderTimelineDetailsPanel({ alertConsumers })
|
||||
: null}
|
||||
{timelineUi?.renderTimelineDetailsPanel ? timelineUi.renderTimelineDetailsPanel() : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React, { useState } from 'react';
|
||||
import { EuiMarkdownEditorUiPlugin, EuiMarkdownAstNodePosition } from '@elastic/eui';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { Plugin } from 'unified';
|
||||
/**
|
||||
* @description - manage the plugins, hooks, and ui components needed to enable timeline functionality within the cases plugin
|
||||
|
@ -29,7 +28,6 @@ interface TimelineProcessingPluginRendererProps {
|
|||
}
|
||||
|
||||
export interface CasesTimelineIntegration {
|
||||
alertConsumers?: AlertConsumers[];
|
||||
editor_plugins: {
|
||||
parsingPlugin: Plugin;
|
||||
processingPluginRenderer: React.FC<
|
||||
|
@ -45,11 +43,7 @@ export interface CasesTimelineIntegration {
|
|||
};
|
||||
ui?: {
|
||||
renderInvestigateInTimelineActionComponent?: (alertIds: string[]) => JSX.Element;
|
||||
renderTimelineDetailsPanel?: ({
|
||||
alertConsumers,
|
||||
}: {
|
||||
alertConsumers?: AlertConsumers[];
|
||||
}) => JSX.Element;
|
||||
renderTimelineDetailsPanel?: () => JSX.Element;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,19 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IndexPatternBase } from '@kbn/es-query';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { IIndexPattern, SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public';
|
||||
import { SearchBar, TimeHistory } from '../../../../../../src/plugins/data/public';
|
||||
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
|
||||
|
||||
export function AlertsSearchBar({
|
||||
dynamicIndexPattern,
|
||||
dynamicIndexPatterns,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
onQueryChange,
|
||||
query,
|
||||
}: {
|
||||
dynamicIndexPattern: IIndexPattern[];
|
||||
dynamicIndexPatterns: IndexPatternBase[];
|
||||
rangeFrom?: string;
|
||||
rangeTo?: string;
|
||||
query?: string;
|
||||
|
@ -31,9 +32,19 @@ export function AlertsSearchBar({
|
|||
}, []);
|
||||
const [queryLanguage, setQueryLanguage] = useState<'lucene' | 'kuery'>('kuery');
|
||||
|
||||
const compatibleIndexPatterns = useMemo(
|
||||
() =>
|
||||
dynamicIndexPatterns.map((dynamicIndexPattern) => ({
|
||||
title: dynamicIndexPattern.title ?? '',
|
||||
id: dynamicIndexPattern.id ?? '',
|
||||
fields: dynamicIndexPattern.fields,
|
||||
})),
|
||||
[dynamicIndexPatterns]
|
||||
);
|
||||
|
||||
return (
|
||||
<SearchBar
|
||||
indexPatterns={dynamicIndexPattern}
|
||||
indexPatterns={compatibleIndexPatterns}
|
||||
placeholder={i18n.translate('xpack.observability.alerts.searchBarPlaceholder', {
|
||||
defaultMessage: 'kibana.alert.evaluation.threshold > 75',
|
||||
})}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
* This way plugins can do targeted imports to reduce the final code bundle
|
||||
*/
|
||||
import {
|
||||
AlertConsumers as AlertConsumersTyped,
|
||||
ALERT_DURATION as ALERT_DURATION_TYPED,
|
||||
ALERT_REASON as ALERT_REASON_TYPED,
|
||||
ALERT_RULE_CONSUMER,
|
||||
|
@ -62,14 +61,13 @@ import { LazyAlertsFlyout } from '../..';
|
|||
import { parseAlert } from './parse_alert';
|
||||
import { CoreStart } from '../../../../../../src/core/public';
|
||||
|
||||
const AlertConsumers: typeof AlertConsumersTyped = AlertConsumersNonTyped;
|
||||
const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED;
|
||||
const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED;
|
||||
const ALERT_STATUS: typeof ALERT_STATUS_TYPED = ALERT_STATUS_NON_TYPED;
|
||||
const ALERT_WORKFLOW_STATUS: typeof ALERT_WORKFLOW_STATUS_TYPED = ALERT_WORKFLOW_STATUS_NON_TYPED;
|
||||
|
||||
interface AlertsTableTGridProps {
|
||||
indexName: string;
|
||||
indexNames: string[];
|
||||
rangeFrom: string;
|
||||
rangeTo: string;
|
||||
kuery: string;
|
||||
|
@ -147,13 +145,6 @@ const NO_ROW_RENDER: RowRenderer[] = [];
|
|||
|
||||
const trailingControlColumns: never[] = [];
|
||||
|
||||
const OBSERVABILITY_ALERT_CONSUMERS = [
|
||||
AlertConsumers.APM,
|
||||
AlertConsumers.LOGS,
|
||||
AlertConsumers.INFRASTRUCTURE,
|
||||
AlertConsumers.UPTIME,
|
||||
];
|
||||
|
||||
function ObservabilityActions({
|
||||
data,
|
||||
eventId,
|
||||
|
@ -290,7 +281,7 @@ function ObservabilityActions({
|
|||
}
|
||||
|
||||
export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
||||
const { indexName, rangeFrom, rangeTo, kuery, workflowStatus, setRefetch, addToQuery } = props;
|
||||
const { indexNames, rangeFrom, rangeTo, kuery, workflowStatus, setRefetch, addToQuery } = props;
|
||||
const { timelines } = useKibana<{ timelines: TimelinesUIStart }>().services;
|
||||
|
||||
const [flyoutAlert, setFlyoutAlert] = useState<TopAlert | undefined>(undefined);
|
||||
|
@ -328,7 +319,6 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
|||
const type: TGridType = 'standalone';
|
||||
const sortDirection: SortDirection = 'desc';
|
||||
return {
|
||||
alertConsumers: OBSERVABILITY_ALERT_CONSUMERS,
|
||||
appId: observabilityFeatureId,
|
||||
casePermissions,
|
||||
type,
|
||||
|
@ -337,7 +327,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
|||
defaultCellActions: getDefaultCellActions({ addToQuery }),
|
||||
end: rangeTo,
|
||||
filters: [],
|
||||
indexNames: [indexName],
|
||||
indexNames,
|
||||
itemsPerPage: 10,
|
||||
itemsPerPageOptions: [10, 25, 50],
|
||||
loadingText: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', {
|
||||
|
@ -372,7 +362,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
|||
};
|
||||
}, [
|
||||
casePermissions,
|
||||
indexName,
|
||||
indexNames,
|
||||
kuery,
|
||||
leadingControlColumns,
|
||||
rangeFrom,
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
import { EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { IndexPatternBase } from '@kbn/es-query';
|
||||
import { ParsedTechnicalFields } from '../../../../rule_registry/common/parse_technical_fields';
|
||||
import type { AlertWorkflowStatus } from '../../../common/typings';
|
||||
import { ExperimentalBadge } from '../../components/shared/experimental_badge';
|
||||
|
@ -35,7 +37,7 @@ interface AlertsPageProps {
|
|||
}
|
||||
|
||||
export function AlertsPage({ routeParams }: AlertsPageProps) {
|
||||
const { core, ObservabilityPageTemplate } = usePluginContext();
|
||||
const { core, plugins, ObservabilityPageTemplate } = usePluginContext();
|
||||
const { prepend } = core.http.basePath;
|
||||
const history = useHistory();
|
||||
const refetch = useRef<() => void>();
|
||||
|
@ -60,17 +62,41 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
|
|||
// observability. For now link to the settings page.
|
||||
const manageRulesHref = prepend('/app/management/insightsAndAlerting/triggersActions/alerts');
|
||||
|
||||
const { data: dynamicIndexPatternResp } = useFetcher(({ signal }) => {
|
||||
const { data: indexNames = NO_INDEX_NAMES } = useFetcher(({ signal }) => {
|
||||
return callObservabilityApi({
|
||||
signal,
|
||||
endpoint: 'GET /api/observability/rules/alerts/dynamic_index_pattern',
|
||||
params: {
|
||||
query: {
|
||||
namespace: 'default',
|
||||
registrationContexts: [
|
||||
'observability.apm',
|
||||
'observability.logs',
|
||||
'observability.infrastructure',
|
||||
'observability.metrics',
|
||||
'observability.uptime',
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const dynamicIndexPattern = useMemo(
|
||||
() => (dynamicIndexPatternResp ? [dynamicIndexPatternResp] : []),
|
||||
[dynamicIndexPatternResp]
|
||||
);
|
||||
const dynamicIndexPatternsAsyncState = useAsync(async (): Promise<IndexPatternBase[]> => {
|
||||
if (indexNames.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'dynamic-observability-alerts-table-index-pattern',
|
||||
title: indexNames.join(','),
|
||||
fields: await plugins.data.indexPatterns.getFieldsForWildcard({
|
||||
pattern: indexNames.join(','),
|
||||
allowNoIndex: true,
|
||||
}),
|
||||
},
|
||||
];
|
||||
}, [indexNames]);
|
||||
|
||||
const setWorkflowStatusFilter = useCallback(
|
||||
(value: AlertWorkflowStatus) => {
|
||||
|
@ -165,7 +191,7 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<AlertsSearchBar
|
||||
dynamicIndexPattern={dynamicIndexPattern}
|
||||
dynamicIndexPatterns={dynamicIndexPatternsAsyncState.value ?? NO_INDEX_PATTERNS}
|
||||
rangeFrom={rangeFrom}
|
||||
rangeTo={rangeTo}
|
||||
query={kuery}
|
||||
|
@ -183,7 +209,7 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
|
|||
|
||||
<EuiFlexItem>
|
||||
<AlertsTableTGrid
|
||||
indexName={dynamicIndexPattern.length > 0 ? dynamicIndexPattern[0].title : ''}
|
||||
indexNames={indexNames}
|
||||
rangeFrom={rangeFrom}
|
||||
rangeTo={rangeTo}
|
||||
kuery={kuery}
|
||||
|
@ -196,3 +222,6 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
|
|||
</ObservabilityPageTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
const NO_INDEX_NAMES: string[] = [];
|
||||
const NO_INDEX_PATTERNS: IndexPatternBase[] = [];
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
ScopedAnnotationsClientFactory,
|
||||
AnnotationsAPI,
|
||||
} from './lib/annotations/bootstrap_annotations';
|
||||
import { Dataset, RuleRegistryPluginSetupContract } from '../../rule_registry/server';
|
||||
import { RuleRegistryPluginSetupContract } from '../../rule_registry/server';
|
||||
import { PluginSetupContract as FeaturesSetup } from '../../features/server';
|
||||
import { uiSettings } from './ui_settings';
|
||||
import { registerRoutes } from './routes/register_routes';
|
||||
|
@ -101,16 +101,6 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
const start = () => core.getStartServices().then(([coreStart]) => coreStart);
|
||||
|
||||
const { ruleDataService } = plugins.ruleRegistry;
|
||||
const ruleDataClient = ruleDataService.initializeIndex({
|
||||
feature: 'observability',
|
||||
registrationContext: 'observability',
|
||||
dataset: Dataset.alerts,
|
||||
componentTemplateRefs: [],
|
||||
componentTemplates: [],
|
||||
indexTemplate: {
|
||||
version: 0,
|
||||
},
|
||||
});
|
||||
|
||||
registerRoutes({
|
||||
core: {
|
||||
|
@ -119,7 +109,7 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
},
|
||||
logger: this.initContext.logger.get(),
|
||||
repository: getGlobalObservabilityServerRouteRepository(),
|
||||
ruleDataClient,
|
||||
ruleDataService,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { CoreSetup, CoreStart, Logger, RouteRegistrar } from 'kibana/server';
|
||||
import Boom from '@hapi/boom';
|
||||
import { RequestAbortedError } from '@elastic/elasticsearch/lib/errors';
|
||||
import { IRuleDataClient } from '../../../rule_registry/server';
|
||||
import { RuleDataPluginService } from '../../../rule_registry/server';
|
||||
import { ObservabilityRequestHandlerContext } from '../types';
|
||||
import { AbstractObservabilityServerRouteRepository } from './types';
|
||||
|
||||
|
@ -21,7 +21,7 @@ export function registerRoutes({
|
|||
repository,
|
||||
core,
|
||||
logger,
|
||||
ruleDataClient,
|
||||
ruleDataService,
|
||||
}: {
|
||||
core: {
|
||||
setup: CoreSetup;
|
||||
|
@ -29,7 +29,7 @@ export function registerRoutes({
|
|||
};
|
||||
repository: AbstractObservabilityServerRouteRepository;
|
||||
logger: Logger;
|
||||
ruleDataClient: IRuleDataClient;
|
||||
ruleDataService: RuleDataPluginService;
|
||||
}) {
|
||||
const routes = repository.getRoutes();
|
||||
|
||||
|
@ -62,7 +62,7 @@ export function registerRoutes({
|
|||
core,
|
||||
logger,
|
||||
params: decodedParams,
|
||||
ruleDataClient,
|
||||
ruleDataService,
|
||||
})) as any;
|
||||
|
||||
return response.ok({ body: data });
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { observabilityFeatureId } from '../../common';
|
||||
import * as t from 'io-ts';
|
||||
import { createObservabilityServerRoute } from './create_observability_server_route';
|
||||
import { createObservabilityServerRouteRepository } from './create_observability_server_route_repository';
|
||||
|
||||
|
@ -14,10 +14,27 @@ const alertsDynamicIndexPatternRoute = createObservabilityServerRoute({
|
|||
options: {
|
||||
tags: [],
|
||||
},
|
||||
handler: async ({ ruleDataClient }) => {
|
||||
const reader = ruleDataClient.getReader({ namespace: observabilityFeatureId });
|
||||
params: t.type({
|
||||
query: t.type({
|
||||
registrationContexts: t.array(t.string),
|
||||
namespace: t.string,
|
||||
}),
|
||||
}),
|
||||
handler: async ({ ruleDataService, params }) => {
|
||||
const { namespace, registrationContexts } = params.query;
|
||||
const indexNames = registrationContexts.flatMap((registrationContext) => {
|
||||
const indexName = ruleDataService
|
||||
.getRegisteredIndexInfo(registrationContext)
|
||||
?.getPrimaryAlias(namespace);
|
||||
|
||||
return reader.getDynamicIndexPattern();
|
||||
if (indexName != null) {
|
||||
return [indexName];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
return indexNames;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {
|
|||
ServerRouteRepository,
|
||||
} from '@kbn/server-route-repository';
|
||||
import { CoreSetup, CoreStart, KibanaRequest, Logger } from 'kibana/server';
|
||||
import { IRuleDataClient } from '../../../rule_registry/server';
|
||||
import { RuleDataPluginService } from '../../../rule_registry/server';
|
||||
|
||||
import { ObservabilityServerRouteRepository } from './get_global_observability_server_route_repository';
|
||||
import { ObservabilityRequestHandlerContext } from '../types';
|
||||
|
@ -24,7 +24,7 @@ export interface ObservabilityRouteHandlerResources {
|
|||
start: () => Promise<CoreStart>;
|
||||
setup: CoreSetup;
|
||||
};
|
||||
ruleDataClient: IRuleDataClient;
|
||||
ruleDataService: RuleDataPluginService;
|
||||
request: KibanaRequest;
|
||||
context: ObservabilityRequestHandlerContext;
|
||||
logger: Logger;
|
||||
|
|
|
@ -650,6 +650,8 @@ export class AlertsClient {
|
|||
|
||||
public async getAuthorizedAlertsIndices(featureIds: string[]): Promise<string[] | undefined> {
|
||||
try {
|
||||
// ATTENTION FUTURE DEVELOPER when you are a super user the augmentedRuleTypes.authorizedRuleTypes will
|
||||
// return all of the features that you can access and does not care about your featureIds
|
||||
const augmentedRuleTypes = await this.authorization.getAugmentedRuleTypesWithAuthorization(
|
||||
featureIds,
|
||||
[ReadOperations.Find, ReadOperations.Get, WriteOperations.Update],
|
||||
|
@ -665,7 +667,7 @@ export class AlertsClient {
|
|||
}
|
||||
|
||||
const toReturn = Array.from(authorizedFeatures).flatMap((feature) => {
|
||||
if (isValidFeatureId(feature)) {
|
||||
if (featureIds.includes(feature) && isValidFeatureId(feature)) {
|
||||
if (feature === 'siem') {
|
||||
return `${mapConsumerToIndexName[feature]}-${this.spaceId}`;
|
||||
} else {
|
||||
|
|
|
@ -12,6 +12,7 @@ type Schema = PublicMethodsOf<RuleDataPluginService>;
|
|||
|
||||
const createRuleDataPluginService = () => {
|
||||
const mocked: jest.Mocked<Schema> = {
|
||||
getRegisteredIndexInfo: jest.fn(),
|
||||
getResourcePrefix: jest.fn(),
|
||||
getResourceName: jest.fn(),
|
||||
isWriteEnabled: jest.fn(),
|
||||
|
|
|
@ -29,6 +29,7 @@ export class RuleDataPluginService {
|
|||
private readonly resourceInstaller: ResourceInstaller;
|
||||
private installCommonResources: Promise<Either<Error, 'ok'>>;
|
||||
private isInitialized: boolean;
|
||||
private registeredIndices: Map<string, IndexInfo> = new Map();
|
||||
|
||||
constructor(private readonly options: ConstructorOptions) {
|
||||
this.resourceInstaller = new ResourceInstaller({
|
||||
|
@ -105,6 +106,8 @@ export class RuleDataPluginService {
|
|||
indexOptions,
|
||||
});
|
||||
|
||||
this.registeredIndices.set(indexOptions.registrationContext, indexInfo);
|
||||
|
||||
const waitUntilClusterClientAvailable = async (): Promise<WaitResult> => {
|
||||
try {
|
||||
const clusterClient = await this.options.getClusterClient();
|
||||
|
@ -148,4 +151,13 @@ export class RuleDataPluginService {
|
|||
waitUntilReadyForWriting,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the index information associated with the given `registrationContext`.
|
||||
* @param registrationContext
|
||||
* @returns the IndexInfo or undefined
|
||||
*/
|
||||
public getRegisteredIndexInfo(registrationContext: string): IndexInfo | undefined {
|
||||
return this.registeredIndices.get(registrationContext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
getCaseDetailsUrl,
|
||||
getCaseDetailsUrlWithCommentId,
|
||||
|
@ -53,14 +52,11 @@ export interface CaseProps extends Props {
|
|||
updateCase: (newCase: Case) => void;
|
||||
}
|
||||
|
||||
const SECURITY_SOLUTION_ALERT_CONSUMERS: AlertConsumers[] = [AlertConsumers.SIEM];
|
||||
|
||||
const TimelineDetailsPanel = ({ alertConsumers }: { alertConsumers?: AlertConsumers[] }) => {
|
||||
const TimelineDetailsPanel = () => {
|
||||
const { browserFields, docValueFields } = useSourcererScope(SourcererScopeName.detections);
|
||||
|
||||
return (
|
||||
<DetailsPanel
|
||||
alertConsumers={alertConsumers}
|
||||
browserFields={browserFields}
|
||||
docValueFields={docValueFields}
|
||||
entityType="alerts"
|
||||
|
@ -232,7 +228,6 @@ export const CaseView = React.memo(({ caseId, subCaseId, userCanCrud }: Props) =
|
|||
showAlertDetails,
|
||||
subCaseId,
|
||||
timelineIntegration: {
|
||||
alertConsumers: SECURITY_SOLUTION_ALERT_CONSUMERS,
|
||||
editor_plugins: {
|
||||
parsingPlugin: timelineMarkdownPlugin.parser,
|
||||
processingPluginRenderer: timelineMarkdownPlugin.renderer,
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { BrowserFields, DocValueFields } from '../../../../common/containers/source';
|
||||
import { ExpandableEvent, ExpandableEventTitle } from './expandable_event';
|
||||
import { useTimelineEventsDetails } from '../../../containers/details';
|
||||
|
@ -51,7 +50,6 @@ const StyledEuiFlyoutBody = styled(EuiFlyoutBody)`
|
|||
`;
|
||||
|
||||
interface EventDetailsPanelProps {
|
||||
alertConsumers?: AlertConsumers[];
|
||||
browserFields: BrowserFields;
|
||||
docValueFields: DocValueFields[];
|
||||
entityType?: EntityType;
|
||||
|
@ -68,10 +66,7 @@ interface EventDetailsPanelProps {
|
|||
timelineId: string;
|
||||
}
|
||||
|
||||
const SECURITY_SOLUTION_ALERT_CONSUMERS: AlertConsumers[] = [AlertConsumers.SIEM];
|
||||
|
||||
const EventDetailsPanelComponent: React.FC<EventDetailsPanelProps> = ({
|
||||
alertConsumers = SECURITY_SOLUTION_ALERT_CONSUMERS, // Default to Security Solution so only other applications have to pass this in
|
||||
browserFields,
|
||||
docValueFields,
|
||||
entityType = 'events', // Default to events so only alerts have to pass entityType in
|
||||
|
@ -82,7 +77,6 @@ const EventDetailsPanelComponent: React.FC<EventDetailsPanelProps> = ({
|
|||
timelineId,
|
||||
}) => {
|
||||
const [loading, detailsData] = useTimelineEventsDetails({
|
||||
alertConsumers,
|
||||
docValueFields,
|
||||
entityType,
|
||||
indexName: expandedEvent.indexName ?? '',
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { EuiFlyout, EuiFlyoutProps } from '@elastic/eui';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
|
||||
import { timelineActions, timelineSelectors } from '../../store/timeline';
|
||||
import { timelineDefaults } from '../../store/timeline/defaults';
|
||||
|
@ -21,7 +20,6 @@ import { NetworkDetailsPanel } from './network_details';
|
|||
import { EntityType } from '../../../../../timelines/common';
|
||||
|
||||
interface DetailsPanelProps {
|
||||
alertConsumers?: AlertConsumers[];
|
||||
browserFields: BrowserFields;
|
||||
docValueFields: DocValueFields[];
|
||||
entityType?: EntityType;
|
||||
|
@ -38,7 +36,6 @@ interface DetailsPanelProps {
|
|||
*/
|
||||
export const DetailsPanel = React.memo(
|
||||
({
|
||||
alertConsumers,
|
||||
browserFields,
|
||||
docValueFields,
|
||||
entityType,
|
||||
|
@ -77,7 +74,6 @@ export const DetailsPanel = React.memo(
|
|||
panelSize = 'm';
|
||||
visiblePanel = (
|
||||
<EventDetailsPanel
|
||||
alertConsumers={alertConsumers}
|
||||
browserFields={browserFields}
|
||||
docValueFields={docValueFields}
|
||||
entityType={entityType}
|
||||
|
|
|
@ -9,7 +9,6 @@ import { isEmpty, noop } from 'lodash/fp';
|
|||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
|
||||
import { inputsModel } from '../../../common/store';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
|
@ -30,7 +29,6 @@ export interface EventsArgs {
|
|||
}
|
||||
|
||||
export interface UseTimelineEventsDetailsProps {
|
||||
alertConsumers?: AlertConsumers[];
|
||||
entityType?: EntityType;
|
||||
docValueFields: DocValueFields[];
|
||||
indexName: string;
|
||||
|
@ -38,10 +36,7 @@ export interface UseTimelineEventsDetailsProps {
|
|||
skip: boolean;
|
||||
}
|
||||
|
||||
const EMPTY_ARRAY: AlertConsumers[] = [];
|
||||
|
||||
export const useTimelineEventsDetails = ({
|
||||
alertConsumers = EMPTY_ARRAY,
|
||||
entityType = EntityType.EVENTS,
|
||||
docValueFields,
|
||||
indexName,
|
||||
|
@ -112,7 +107,6 @@ export const useTimelineEventsDetails = ({
|
|||
setTimelineDetailsRequest((prevRequest) => {
|
||||
const myRequest = {
|
||||
...(prevRequest ?? {}),
|
||||
alertConsumers,
|
||||
docValueFields,
|
||||
entityType,
|
||||
indexName,
|
||||
|
@ -124,7 +118,7 @@ export const useTimelineEventsDetails = ({
|
|||
}
|
||||
return prevRequest;
|
||||
});
|
||||
}, [alertConsumers, docValueFields, entityType, eventId, indexName]);
|
||||
}, [docValueFields, entityType, eventId, indexName]);
|
||||
|
||||
useEffect(() => {
|
||||
timelineDetailsSearch(timelineDetailsRequest);
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
|
||||
import { IEsSearchRequest } from '../../../../../../src/plugins/data/common';
|
||||
import { ESQuery } from '../../typed_json';
|
||||
|
@ -44,7 +43,6 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest {
|
|||
docValueFields?: DocValueFields[];
|
||||
factoryQueryType?: TimelineFactoryQueryTypes;
|
||||
entityType?: EntityType;
|
||||
alertConsumers?: AlertConsumers[];
|
||||
}
|
||||
|
||||
export interface TimelineRequestSortField<Field = string> extends SortField<Field> {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
@ -80,7 +79,6 @@ const ScrollableFlexItem = styled(EuiFlexItem)`
|
|||
`;
|
||||
|
||||
export interface TGridStandaloneProps {
|
||||
alertConsumers: AlertConsumers[];
|
||||
appId: string;
|
||||
casePermissions: {
|
||||
crud: boolean;
|
||||
|
@ -117,7 +115,6 @@ export interface TGridStandaloneProps {
|
|||
|
||||
const TGridStandaloneComponent: React.FC<TGridStandaloneProps> = ({
|
||||
afterCaseSelection,
|
||||
alertConsumers,
|
||||
appId,
|
||||
casePermissions,
|
||||
columns,
|
||||
|
@ -210,7 +207,6 @@ const TGridStandaloneComponent: React.FC<TGridStandaloneProps> = ({
|
|||
loading,
|
||||
{ events, updatedAt, loadPage, pageInfo, refetch, totalCount = 0, inspect },
|
||||
] = useTimelineEvents({
|
||||
alertConsumers,
|
||||
docValueFields: [],
|
||||
entityType,
|
||||
excludeEcsData: true,
|
||||
|
|
|
@ -5,26 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
AlertConsumers as CONSUMERS,
|
||||
ALERT_RULE_CONSUMER,
|
||||
ALERT_RULE_TYPE_ID,
|
||||
SPACE_IDS,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { ALERT_RULE_CONSUMER, ALERT_RULE_TYPE_ID, SPACE_IDS } from '@kbn/rule-data-utils';
|
||||
import { map, mergeMap, catchError } from 'rxjs/operators';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
import type {
|
||||
AlertConsumers,
|
||||
mapConsumerToIndexName as mapConsumerToIndexNameTyped,
|
||||
isValidFeatureId as isValidFeatureIdTyped,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import {
|
||||
mapConsumerToIndexName as mapConsumerToIndexNameNonTyped,
|
||||
isValidFeatureId as isValidFeatureIdNonTyped,
|
||||
// @ts-expect-error
|
||||
} from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac';
|
||||
|
||||
import {
|
||||
AlertingAuthorizationEntity,
|
||||
AlertingAuthorizationFilterType,
|
||||
|
@ -49,9 +33,6 @@ import {
|
|||
ISearchOptions,
|
||||
} from '../../../../../../src/plugins/data/common';
|
||||
|
||||
const mapConsumerToIndexName: typeof mapConsumerToIndexNameTyped = mapConsumerToIndexNameNonTyped;
|
||||
const isValidFeatureId: typeof isValidFeatureIdTyped = isValidFeatureIdNonTyped;
|
||||
|
||||
export const timelineSearchStrategyProvider = <T extends TimelineFactoryQueryTypes>(
|
||||
data: PluginStart,
|
||||
alerting: AlertingPluginStartContract
|
||||
|
@ -63,7 +44,6 @@ export const timelineSearchStrategyProvider = <T extends TimelineFactoryQueryTyp
|
|||
search: (request, options, deps) => {
|
||||
const factoryQueryType = request.factoryQueryType;
|
||||
const entityType = request.entityType;
|
||||
const alertConsumers = request.alertConsumers;
|
||||
|
||||
if (factoryQueryType == null) {
|
||||
throw new Error('factoryQueryType is required');
|
||||
|
@ -71,13 +51,7 @@ export const timelineSearchStrategyProvider = <T extends TimelineFactoryQueryTyp
|
|||
|
||||
const queryFactory: TimelineFactory<T> = timelineFactory[factoryQueryType];
|
||||
|
||||
if (alertConsumers != null && entityType != null && entityType === EntityType.ALERTS) {
|
||||
const allFeatureIdsValid = alertConsumers.every((id) => isValidFeatureId(id));
|
||||
|
||||
if (!allFeatureIdsValid) {
|
||||
throw new Error('An invalid alerts consumer feature id was provided');
|
||||
}
|
||||
|
||||
if (entityType != null && entityType === EntityType.ALERTS) {
|
||||
return timelineAlertsSearchStrategy({
|
||||
es: esAsInternal,
|
||||
request,
|
||||
|
@ -85,7 +59,6 @@ export const timelineSearchStrategyProvider = <T extends TimelineFactoryQueryTyp
|
|||
deps,
|
||||
queryFactory,
|
||||
alerting,
|
||||
alertConsumers: alertConsumers ?? [],
|
||||
});
|
||||
} else {
|
||||
return timelineSearchStrategy({ es, request, options, deps, queryFactory });
|
||||
|
@ -131,7 +104,6 @@ const timelineAlertsSearchStrategy = <T extends TimelineFactoryQueryTypes>({
|
|||
deps,
|
||||
queryFactory,
|
||||
alerting,
|
||||
alertConsumers,
|
||||
}: {
|
||||
es: ISearchStrategy;
|
||||
request: TimelineStrategyRequestType<T>;
|
||||
|
@ -139,17 +111,10 @@ const timelineAlertsSearchStrategy = <T extends TimelineFactoryQueryTypes>({
|
|||
deps: SearchStrategyDependencies;
|
||||
alerting: AlertingPluginStartContract;
|
||||
queryFactory: TimelineFactory<T>;
|
||||
alertConsumers: AlertConsumers[];
|
||||
}) => {
|
||||
// Based on what solution alerts you want to see, figures out what corresponding
|
||||
// index to query (ex: siem --> .alerts-security.alerts)
|
||||
const indices = alertConsumers.flatMap((consumer) => {
|
||||
if (consumer === CONSUMERS.SIEM) {
|
||||
return request.defaultIndex ?? request.indexType;
|
||||
}
|
||||
|
||||
return `${mapConsumerToIndexName[consumer]}`;
|
||||
});
|
||||
const indices = request.defaultIndex ?? request.indexType;
|
||||
const requestWithAlertsIndices = { ...request, defaultIndex: indices, indexName: indices };
|
||||
|
||||
// Note: Alerts RBAC are built off of the alerting's authorization class, which
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AlertConsumers as AlertConsumersTyped } from '@kbn/rule-data-utils';
|
||||
// @ts-expect-error
|
||||
import { AlertConsumers as AlertConsumersNonTyped } from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac';
|
||||
import { Router } from 'react-router-dom';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
@ -17,8 +14,6 @@ import { KibanaContextProvider } from '../../../../../../../../src/plugins/kiban
|
|||
import { TimelinesUIStart } from '../../../../../../../plugins/timelines/public';
|
||||
import { DataPublicPluginStart } from '../../../../../../../../src/plugins/data/public';
|
||||
|
||||
const AlertConsumers: typeof AlertConsumersTyped = AlertConsumersNonTyped;
|
||||
|
||||
type CoreStartTimelines = CoreStart & { data: DataPublicPluginStart };
|
||||
|
||||
/**
|
||||
|
@ -42,7 +37,6 @@ export function renderApp(
|
|||
ReactDOM.unmountComponentAtNode(parameters.element);
|
||||
};
|
||||
}
|
||||
const ALERT_RULE_CONSUMER = [AlertConsumers.SIEM];
|
||||
|
||||
const AppRoot = React.memo(
|
||||
({
|
||||
|
@ -67,7 +61,6 @@ const AppRoot = React.memo(
|
|||
{(timelinesPluginSetup &&
|
||||
timelinesPluginSetup.getTGrid &&
|
||||
timelinesPluginSetup.getTGrid<'standalone'>({
|
||||
alertConsumers: ALERT_RULE_CONSUMER,
|
||||
appId: 'securitySolution',
|
||||
type: 'standalone',
|
||||
casePermissions: {
|
||||
|
|
Loading…
Reference in a new issue