Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c6ff65f
Introduce an event bus
cnasikas May 19, 2026
6d07213
Event bus improvements
cnasikas May 19, 2026
4eaaa3c
Add unit tests
cnasikas May 19, 2026
80ac903
Merge branch 'main' into alerting_v2_event_bus
cnasikas May 19, 2026
c09ec2d
Changes from make api-docs
kibanamachine May 19, 2026
cd3fefd
Merge branch 'main' into alerting_v2_event_bus
cnasikas May 20, 2026
e43a1b4
Switch to setImmediate and EventEmitterAsyncResource
cnasikas May 20, 2026
9afde85
PR feedback
cnasikas May 20, 2026
28a9188
Create alert action publisher
cnasikas May 20, 2026
0f8e5c0
Fix TS errors
cnasikas May 20, 2026
068fedd
Merge branch 'alerting_v2_event_bus' into alerting-v2-workflow-trigge…
cnasikas May 20, 2026
2714f6f
Create the alert action pub/sub
cnasikas May 21, 2026
0c4978c
Adapting to the new bus.
adcoelho May 21, 2026
1dd5101
Merge remote-tracking branch 'upstream/main' into alerting-v2-workflo…
adcoelho May 22, 2026
94de798
Changes from node scripts/check
kibanamachine May 22, 2026
c06e33b
Type fixes
adcoelho May 22, 2026
741b21f
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
adcoelho May 22, 2026
e7a671b
fix test
adcoelho May 22, 2026
fe5bd86
Add logger.
adcoelho May 22, 2026
ee0a69f
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
adcoelho May 22, 2026
3aafc90
updating limits
adcoelho May 22, 2026
7a15f24
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
adcoelho May 22, 2026
a6569f5
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
cnasikas May 25, 2026
a45ed7f
Fix limits
cnasikas May 25, 2026
0a9a061
PR feedback
cnasikas May 25, 2026
1a60880
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
cnasikas May 28, 2026
19b1d78
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
elasticmachine May 29, 2026
c28445c
Merge branch 'main' into alerting-v2-workflow-trigger-assign-episode
cnasikas Jun 2, 2026
7a89d3a
Change trigger ID
cnasikas Jun 2, 2026
4aaa6dc
Add limits to the schema
cnasikas Jun 2, 2026
d8a8114
Merge branch 'alerting-v2-workflow-trigger-assign-episode' of github.…
cnasikas Jun 2, 2026
11fbf49
Fix unit test
cnasikas Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pageLoadAssetSize:
aiAssistantManagementSelection: 11569
aiops: 15227
alerting: 22371
alertingVTwo: 51297
alertingVTwo: 57086
apm: 54371
apmSourcesAccess: 2278
automaticImport: 18629
Expand All @@ -29,7 +29,7 @@ pageLoadAssetSize:
contentConnectors: 33014
contentManagement: 8350
controls: 10300
core: 547151
core: 3415547
cps: 9209
crossClusterReplication: 12662
customIntegrations: 11715
Expand All @@ -56,7 +56,7 @@ pageLoadAssetSize:
embeddableAlertsTable: 6524
enterpriseSearch: 37565
entityStore: 9718
esql: 29547
esql: 29469
esqlDataGrid: 10209
esUiShared: 101220
evals: 6117
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
* and copy the schemaHash from the response for the trigger id.
*/
export const APPROVED_TRIGGER_DEFINITIONS: Array<{ id: string; schemaHash: string }> = [
{
id: 'alertingV2.episodeAssigned',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably need to change this as well

schemaHash: 'fce48752c788e4620a70cf8d33040117c7a46c78508a5cf98c5c0fa93f631ad0',
},
{
id: 'cases.caseCreated',
schemaHash: '5b562db9463664a1e28ff2f3ee7edec229e83912569190cd8a83f53d38da9ed8',
Expand Down
2 changes: 1 addition & 1 deletion x-pack/platform/plugins/shared/alerting_v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ If you want implementation detail for one subsystem, continue with:
- API shape or saved object contracts: inspect `server/routes/` and `server/saved_objects/` together with the relevant subsystem docs
- Workflow triggers (workflows_extensions registration + runtime emission): see the public and server READMEs
- [`public/lib/workflow_extensions/README.md`](public/lib/workflow_extensions/README.md)
- [`server/lib/services/workflow_extensions_service/README.md`](server/lib/services/workflow_extensions_service/README.md)
- [`server/lib/services/workflow_service/README.md`](server/lib/services/workflow_service/README.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { z } from '@kbn/zod/v4';
import type { CommonTriggerDefinition } from '@kbn/workflows-extensions/common';

export const EPISODE_ASSIGNED_TRIGGER_ID = 'alertingV2.episodeAssigned' as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to specify V2 here? isn't that an implementation detail?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to distinguish between the existing alerting and the new one (v2) in case we want to introduce triggers for the v1 alerting in the future. Any suggestions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't have any suggestions atm, maybe @tinnytintin10 could help

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed briefly this one with @tinnytintin10, suggestion is to remove V2 from name, as it also felt a bit as implementation detail. The next step will be for us to offer extra metadata for supporting V1 and V2 if you decide to ship something for alerting V1. Also you can explicitly put in the description that this one is related to V2 if that could help users further, wdyt?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tiamliu based on our recent conversations around how to cater to "mixed experiences" effectively, please check my thinking here?

I think we should call these kinds of events/triggers "alerting.[event_name]" and as much as possible make them version-agnostic. For events that apply to both, we can send them through the same flow, and include "version" as a payload discriminator for people who only want to handle one or the other for some reason (likely suggested by a consultant, support, etc).

Very rough examples:

for alerting.ruleSnoozed
payload might be

{
  "rule.id":  "some-id-for-rule",
  "alerting.version": 2,
  "timestamp": //...
    // ...etc
}

for alerting.alertAssigned and alerting.alertAcknowledged, maybe only v2 alerts have these features, but that's still okay.

Thoughts?

@tinnytintin10 tinnytintin10 Jun 1, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we actually want event-driven triggers for v1?

maybe we can make a deliberate product choice that event driven triggers are v2-only. v1 users still have the run workflow action, so they can invoke workflows when alerts fire or recover. event-driven triggers would only come with v2, and only show up once v2 features are enabled. If users want event driven triggers, that's a reason to move to v2. i think this would enable us to:

  1. sidesteps the whole naming question here. If there's never a v1 trigger to disambiguate from, we don't need the version in the ID and can just call it alerting.episodeAssigned.
  2. and on the product side, event-driven automation is exactly the kind of capability that pulls people toward v2, instead of us building it twice for a framework we're winding down.

wdyt @jasonrhodes @tiamliu

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first instinct is to say I think this is a really good idea. We need to support v1 rules and alerts for a long time, likely, as users will want to continue using their existing rules and put off migration as long as possible even if we make it easy, but that doesn't mean we need to give v1 rules every new thing.

If we do get pressure to add these down the road, let's let those events bear the burden of naming with something like alerting.classic.{trigger_name} or alerting.legacy.{trigger_name} depending on how far down the road we are when we decide we want it. I may be missing some context that makes this not work, so I'll check with Tia and others on our side, but on first glance I think this is a nice solution.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good middle ground. We can deliberately choose to support event triggers in alerting v2 only. In alerting v1, workflows can be attached as an action and can already be triggered per execution. If we make this a v2 only thing , it also gives users incentive to migrate. Thanks for all your thorough considerations @cnasikas @jasonrhodes @tinnytintin10

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you all! I also agree. I will go with alerting.episodeAssigned then. Thanks!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yngrdyn @tinnytintin10 I pushed the changes. Could you plz take another look?


export const episodeAssignedPayloadSchema = z
.object({
occurredAt: z.string().describe(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.occurredAt', {
defaultMessage: 'ISO timestamp of when the assignment occurred.',
})
),
groupHash: z.string().describe(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.groupHash', {
defaultMessage: 'Stable hash of the alert grouping the episode belongs to.',
})
),
episodeId: z.string().describe(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.episodeId', {
defaultMessage: 'Identifier of the alerting episode whose assignee changed.',
})
),
ruleId: z.string().describe(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.ruleId', {
defaultMessage: 'Identifier of the alerting rule the episode belongs to.',
})
),
spaceId: z.string().describe(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.spaceId', {
defaultMessage: 'Kibana space the episode lives in.',
})
),
actorUid: z
.string()
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
.nullable()
.describe(
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.actorUid', {
defaultMessage:
'User-profile uid of the actor who changed the assignee, or null when performed by an internal/system context.',
})
),
assigneeUid: z
.string()
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
.nullable()
.describe(
i18n.translate('xpack.alertingVTwo.triggers.episodeAssigned.schema.assigneeUid', {
defaultMessage:
'User-profile uid of the new assignee, or null when the episode was unassigned.',
})
),
})
.strict();

export type EpisodeAssignedPayload = z.infer<typeof episodeAssignedPayloadSchema>;

export const episodeAssignedTriggerCommonDefinition: CommonTriggerDefinition<
typeof episodeAssignedPayloadSchema
> = {
id: EPISODE_ASSIGNED_TRIGGER_ID,
eventSchema: episodeAssignedPayloadSchema,
};
Comment thread
cnasikas marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 {
EPISODE_ASSIGNED_TRIGGER_ID,
episodeAssignedPayloadSchema,
episodeAssignedTriggerCommonDefinition,
} from './episode_assigned';
export type { EpisodeAssignedPayload } from './episode_assigned';
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This folder owns the **public-side (browser) registration** of `alerting_v2` wor
- **Public setup contract** (`WorkflowsExtensionsPublicPluginSetup`) — UI metadata for the Workflows builder (title, description, icon, docs, snippets). This is what controls discoverability of a trigger in the Workflows UI.
- **Server setup contract** (`WorkflowsExtensionsServerPluginSetup`) — runtime validation/execution of triggers and steps.

This README covers the **public** side. For the server side (where runtime emission also lives), see [`server/lib/services/workflow_extensions_service/README.md`](../../../server/lib/services/workflow_extensions_service/README.md).
This README covers the **public** side. For the server side (where runtime emission also lives), see [`server/lib/services/workflow_service/README.md`](../../../server/lib/services/workflow_service/README.md).

## Registering a new trigger

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
* 2.0.
*/

import type {
PublicTriggerDefinition,
WorkflowsExtensionsPublicPluginSetup,
} from '@kbn/workflows-extensions/public';
import type { WorkflowsExtensionsPublicPluginSetup } from '@kbn/workflows-extensions/public';
import { episodeAssignedTriggerPublicDefinition } from './triggers/episode_assigned';

/**
* Registers all alerting-v2 public workflow trigger definitions (UI metadata).
Expand All @@ -17,11 +15,5 @@ import type {
export function registerTriggerDefinitions(
workflowsExtensions: WorkflowsExtensionsPublicPluginSetup
): void {
const triggerDefinitions: PublicTriggerDefinition[] = [
// Add PublicTriggerDefinition entries here (spread common id + eventSchema + title, icon, docs).
];

for (const definition of triggerDefinitions) {
workflowsExtensions.registerTriggerDefinition(definition);
}
workflowsExtensions.registerTriggerDefinition(episodeAssignedTriggerPublicDefinition);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 { EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import type { PublicTriggerDefinition } from '@kbn/workflows-extensions/public';
import {
EPISODE_ASSIGNED_TRIGGER_ID,
episodeAssignedTriggerCommonDefinition,
} from '../../../../common/workflows/triggers';

const EpisodeAssignedIcon = () => React.createElement(EuiIcon, { type: 'user' });
Comment thread
cnasikas marked this conversation as resolved.
Outdated

export const episodeAssignedTriggerPublicDefinition: PublicTriggerDefinition = {
...episodeAssignedTriggerCommonDefinition,
icon: EpisodeAssignedIcon,
Comment thread
cnasikas marked this conversation as resolved.
Outdated
title: i18n.translate('xpack.alertingVTwo.workflowTriggers.episodeAssigned.title', {
defaultMessage: 'Alerting - Episode assigned',
}),
description: i18n.translate('xpack.alertingVTwo.workflowTriggers.episodeAssigned.description', {
defaultMessage: 'Emitted when an alerting episode is assigned to a user.',
}),
documentation: {
details: i18n.translate(
'xpack.alertingVTwo.workflowTriggers.episodeAssigned.documentation.details',
{
defaultMessage:
'Emitted after an episode assign action is persisted with a non-null assignee. The payload includes event.episodeId, event.ruleId, event.spaceId, and event.assigneeUid for trigger conditions.',
}
),
examples: [
i18n.translate('xpack.alertingVTwo.workflowTriggers.episodeAssigned.documentation.example', {
defaultMessage: `## Run for a specific rule
\`\`\`yaml
triggers:
- type: {triggerId}
on:
condition: 'event.ruleId: "my-rule-id"'
\`\`\``,
values: {
triggerId: EPISODE_ASSIGNED_TRIGGER_ID,
Comment thread
adcoelho marked this conversation as resolved.
Outdated
},
}),
],
},
snippets: {
condition: 'event.assigneeUid: "user-profile-uid"',
},
};
2 changes: 2 additions & 0 deletions x-pack/platform/plugins/shared/alerting_v2/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { bindOnSetup } from './setup/bind_on_setup';
import { bindOnStart } from './setup/bind_on_start';
import { bindRoutes } from './setup/bind_routes';
import { bindServices } from './setup/bind_services';
import { bindEvents } from './setup/bind_events';
import { bindRuleExecutionServices } from './setup/bind_rule_executor';
import { bindDispatcherExecutionServices } from './setup/bind_dispatcher_executor';
import { bindTasks } from './setup/bind_tasks';
Expand All @@ -28,6 +29,7 @@ export const module = new ContainerModule((options) => {
bindContract(options);
bindRoutes(options);
bindServices(options);
bindEvents(options);
bindRuleExecutionServices(options);
bindDispatcherExecutionServices(options);
bindTasks(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import type { ElasticsearchClient } from '@kbn/core/server';
import type { UserProfileServiceStart } from '@kbn/core-user-profile-server';
import type { DeeplyMockedApi } from '@kbn/core-elasticsearch-client-server-mocks';
import { httpServerMock } from '@kbn/core-http-server-mocks';
import type { PublicMethodsOf } from '@kbn/utility-types';
import { createAlertActionEventPublisher } from '../events/alert_action_event_publisher/alert_action_event_publisher.mock';
import type { AlertActionEventPublisher } from '../events/alert_action_event_publisher/alert_action_event_publisher';
import { createQueryService } from '../services/query_service/query_service.mock';
import { createStorageService } from '../services/storage_service/storage_service.mock';
import { createUserService, createUserProfile } from '../services/user_service/user_service.mock';
Expand All @@ -19,21 +22,32 @@ export function createAlertActionsClient(): {
queryServiceEsClient: DeeplyMockedApi<ElasticsearchClient>;
storageServiceEsClient: jest.Mocked<ElasticsearchClient>;
userProfileService: jest.Mocked<UserProfileServiceStart>;
alertActionEventPublisher: AlertActionEventPublisher;
} {
const { queryService, mockEsClient: queryServiceEsClient } = createQueryService();
const { storageService, mockEsClient: storageServiceEsClient } = createStorageService();
const { userService, userProfileService } = createUserService();
const { publisher: alertActionEventPublisher } = createAlertActionEventPublisher();
const request = httpServerMock.createKibanaRequest();

userProfileService.getCurrent.mockResolvedValue(createUserProfile('test-uid'));

const alertActionsClient = new AlertActionsClient(
queryService,
storageService,
userService,
'default'
request,
'default',
alertActionEventPublisher
);

return { alertActionsClient, queryServiceEsClient, storageServiceEsClient, userProfileService };
return {
alertActionsClient,
queryServiceEsClient,
storageServiceEsClient,
userProfileService,
alertActionEventPublisher,
};
}

export function createAlertActionsClientMock(): jest.Mocked<PublicMethodsOf<AlertActionsClient>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { UserProfileServiceStart } from '@kbn/core-user-profile-server';
import type { DeeplyMockedApi } from '@kbn/core-elasticsearch-client-server-mocks';
import type { BulkCreateAlertActionItemBody } from '@kbn/alerting-v2-schemas';
import { ALERT_EPISODE_ACTION_TYPE, type CreateAlertActionBody } from '@kbn/alerting-v2-schemas';
import type { AlertActionEventPublisher } from '../events/alert_action_event_publisher/alert_action_event_publisher';
import type { AlertActionsClient } from './alert_actions_client';
import { createAlertActionsClient } from './alert_actions_client.mock';
import {
Expand All @@ -24,14 +25,18 @@ describe('AlertActionsClient', () => {
let queryServiceEsClient: DeeplyMockedApi<ElasticsearchClient>;
let storageServiceEsClient: jest.Mocked<ElasticsearchClient>;
let userProfileService: jest.Mocked<UserProfileServiceStart>;
let alertActionEventPublisher: AlertActionEventPublisher;
let emitEpisodeActionsSpy: jest.SpyInstance;

beforeEach(() => {
({
alertActionsClient: client,
queryServiceEsClient,
storageServiceEsClient,
userProfileService,
alertActionEventPublisher,
} = createAlertActionsClient());
emitEpisodeActionsSpy = jest.spyOn(alertActionEventPublisher, 'emitEpisodeActions');
storageServiceEsClient.bulk.mockResolvedValueOnce({ items: [], errors: false, took: 1 });
});

Expand Down Expand Up @@ -216,6 +221,83 @@ describe('AlertActionsClient', () => {
});
});

describe('episode action domain events', () => {
it('calls emitEpisodeActions with the persisted assign action document', async () => {
queryServiceEsClient.esql.query.mockResolvedValueOnce(getAlertEventESQLResponse());

await client.createAction({
groupHash: 'test-group-hash',
action: {
action_type: ALERT_EPISODE_ACTION_TYPE.ASSIGN,
episode_id: 'episode-1',
assignee_uid: 'assignee-uid-1',
},
});

expect(emitEpisodeActionsSpy).toHaveBeenCalledTimes(1);
expect(emitEpisodeActionsSpy).toHaveBeenCalledWith(expect.anything(), [
expect.objectContaining({
action_type: ALERT_EPISODE_ACTION_TYPE.ASSIGN,
assignee_uid: 'assignee-uid-1',
episode_id: 'episode-1',
group_hash: 'test-group-hash',
actor: 'test-uid',
}),
]);
});

it('does not call emitEpisodeActions when persistence fails', async () => {
queryServiceEsClient.esql.query.mockResolvedValueOnce(getEmptyESQLResponse());

await expect(
client.createAction({
groupHash: 'unknown-group-hash',
action: {
action_type: ALERT_EPISODE_ACTION_TYPE.ASSIGN,
episode_id: 'episode-1',
assignee_uid: 'assignee-uid-1',
},
})
).rejects.toThrow();

expect(emitEpisodeActionsSpy).not.toHaveBeenCalled();
});

it('calls emitEpisodeActions for bulk assign actions only among persisted docs', async () => {
const actions: BulkCreateAlertActionItemBody[] = [
{
group_hash: 'group-hash-1',
action_type: ALERT_EPISODE_ACTION_TYPE.ASSIGN,
episode_id: 'episode-1',
assignee_uid: 'assignee-uid-1',
},
{
group_hash: 'group-hash-2',
action_type: ALERT_EPISODE_ACTION_TYPE.ACK,
episode_id: 'episode-2',
},
];

queryServiceEsClient.esql.query.mockResolvedValueOnce(
getBulkAlertEventsESQLResponse([
{ group_hash: 'group-hash-1', episode_id: 'episode-1' },
{ group_hash: 'group-hash-2', episode_id: 'episode-2' },
])
);

await client.createBulkActions(actions);

expect(emitEpisodeActionsSpy).toHaveBeenCalledTimes(1);
expect(emitEpisodeActionsSpy.mock.calls[0][1]).toHaveLength(2);
expect(emitEpisodeActionsSpy.mock.calls[0][1][0]).toMatchObject({
action_type: ALERT_EPISODE_ACTION_TYPE.ASSIGN,
});
expect(emitEpisodeActionsSpy.mock.calls[0][1][1]).toMatchObject({
action_type: ALERT_EPISODE_ACTION_TYPE.ACK,
});
});
});

describe('error codes and details', () => {
it('attaches ALERT_EVENT_NOT_FOUND code with group_hash and episode_id details on createAction', async () => {
queryServiceEsClient.esql.query.mockResolvedValueOnce(getEmptyESQLResponse());
Expand Down
Loading
Loading