[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:
Xavier Mouligneau 2021-08-20 12:52:34 -04:00 committed by GitHub
parent ec2d041638
commit e8e53e36e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 109 additions and 135 deletions

View file

@ -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}
</>
);
}

View file

@ -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;
};
}

View file

@ -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',
})}

View file

@ -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,

View file

@ -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[] = [];

View file

@ -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 {

View file

@ -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 });

View file

@ -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;
},
});

View file

@ -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;

View file

@ -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 {

View file

@ -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(),

View file

@ -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);
}
}

View file

@ -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,

View file

@ -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 ?? '',

View file

@ -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}

View file

@ -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);

View file

@ -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> {

View file

@ -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,

View file

@ -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

View file

@ -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: {