Skip to content

Add retrieveResults parameter to get search request results instead of status during polling#4

Merged
weltenwort merged 1 commit intoweltenwort:backport/8.14/pr-187303from
lukasolson:retrieve_results_8_14
Jul 9, 2024
Merged

Add retrieveResults parameter to get search request results instead of status during polling#4
weltenwort merged 1 commit intoweltenwort:backport/8.14/pr-187303from
lukasolson:retrieve_results_8_14

Conversation

@lukasolson
Copy link
Copy Markdown

Summary

Adds retrieveResults parameter for the backport.

Checklist

Delete any items that are not applicable to this PR.

Risk Matrix

Delete this section if it is not applicable to this PR.

Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release.

When forming the risk matrix, consider some of the following examples and how they may potentially impact the change:

Risk Probability Severity Mitigation/Notes
Multiple Spaces—unexpected behavior in non-default Kibana Space. Low High Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces.
Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. High Low Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure.
Code should gracefully handle cases when feature X or plugin Y are disabled. Medium High Unit tests will verify that any feature flag or plugin combination still results in our service operational.
See more potential risk examples

For maintainers

@weltenwort weltenwort merged commit e8987ff into weltenwort:backport/8.14/pr-187303 Jul 9, 2024
elasticmachine pushed a commit that referenced this pull request Jan 20, 2025
…re replacements (elastic#206660)

## Summary

Today, when a developer deprecates a feature and replaces its privileges
with those of another feature, we reasonably assume that the new feature
fully replaces the old one in all possible contexts - whether in role
management UIs or in the Spaces feature toggles visibility UI. However,
when deprecated privileges are replaced by the privileges of multiple
features, such as in [this
case](elastic#202863 (comment))
where the Discover/Dashboard/Maps feature privileges are replaced by the
privileges of Discover_v2/Dashboard_v2/Maps_v2, respectively, **and**
the privileges of the Saved Query Management feature, the choice is
ambiguous.

Which of these features should be treated as the replacement for the
deprecated feature in contexts that deal with entire features (like the
Spaces feature toggles visibility UI) rather than individual privileges
(like in role management UIs)? Should all referenced features be
considered replacements? Or just a subset - or even a single feature? If
so, which one? Currently, we treat all referenced features as
replacements for the deprecated feature, which creates problems, as
described in detail in [this
discussion](elastic#202863 (comment)).

This PR allows developers to customize this behavior by specifying which
features Kibana should treat as direct successors to deprecated features
in contexts that deal with whole features rather than individual
privileges:

```ts
deps.features.registerKibanaFeature({
  deprecated: {
    notice: 'The feature is deprecated because … well, there’s a reason.',
    --> replacedBy: ['feature_id_v2'], <--
  },
  id: 'feature_id'
  name: `Case #4 feature ${suffix} (DEPRECATED)`,
  …
});
```

## How to test

1. Run test server
```bash
node scripts/functional_tests_server.js --config x-pack/test/security_api_integration/features.config.ts
```

2. Execute the following request from the Dev Tools (`case_4_feature_a`
is a deprecated feature that is replaced by multiple features and
**doesn't use** `deprecated.replacedBy`)
```http
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_a"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
```

3. Observe that in response deprecated `case_4_feature_a` is replaced by
two features (you can also check
http://localhost:5620/app/management/kibana/spaces/edit/default to see
how it's reflected in UI)
```http
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_a_v2",
    "case_4_feature_c"
  ],
  "_reserved": true
}
```

4. Execute the following request from the Dev Tools (`case_4_feature_b`
is a deprecated feature that is replaced by multiple features, but
**uses** `deprecated.replacedBy` to set the conceptual
feature-successor)
```http
PUT kbn:/api/spaces/space/default?overwrite=true
{
  "id":"default",
  "name":"Default",
  "description":"This is your default space!",
  "color":"#00bfb3",
  "disabledFeatures":["case_4_feature_b"],
  "_reserved":true,
  "imageUrl":"",
  "initials":"D"
}
```

5. Observe that in response deprecated `case_4_feature_b` is replaced by
a single feature (you can also check
http://localhost:5620/app/management/kibana/spaces/edit/default to see
how it's reflected in UI)
```http
{
  "id": "default",
  "name": "Default",
  "description": "This is your default space!",
  "color": "#00bfb3",
  "initials": "D",
  "imageUrl": "",
  "disabledFeatures": [
    "case_4_feature_b_v2"
  ],
  "_reserved": true
}
```

__Required by:__
elastic#202863 (comment)

//cc @davismcphee
weltenwort pushed a commit that referenced this pull request Jan 29, 2025
…t explicit feature replacements (elastic#206660) (elastic#206925)

# Backport

This will backport the following commits from `main` to `8.x`:
- [feat(security): extend &#x60;Feature&#x60; definition to support
explicit feature replacements
(elastic#206660)](elastic#206660)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Aleh
Zasypkin","email":"aleh.zasypkin@elastic.co"},"sourceCommit":{"committedDate":"2025-01-16T11:35:32Z","message":"feat(security):
extend `Feature` definition to support explicit feature replacements
(elastic#206660)\n\n## Summary\n\nToday, when a developer deprecates a feature
and replaces its privileges\nwith those of another feature, we
reasonably assume that the new feature\nfully replaces the old one in
all possible contexts - whether in role\nmanagement UIs or in the Spaces
feature toggles visibility UI. However,\nwhen deprecated privileges are
replaced by the privileges of multiple\nfeatures, such as in
[this\ncase](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114)\nwhere
the Discover/Dashboard/Maps feature privileges are replaced by
the\nprivileges of Discover_v2/Dashboard_v2/Maps_v2, respectively,
**and**\nthe privileges of the Saved Query Management feature, the
choice is\nambiguous.\n\nWhich of these features should be treated as
the replacement for the\ndeprecated feature in contexts that deal with
entire features (like the\nSpaces feature toggles visibility UI) rather
than individual privileges\n(like in role management UIs)? Should all
referenced features be\nconsidered replacements? Or just a subset - or
even a single feature? If\nso, which one? Currently, we treat all
referenced features as\nreplacements for the deprecated feature, which
creates problems, as\ndescribed in detail in
[this\ndiscussion](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114).\n\nThis
PR allows developers to customize this behavior by specifying
which\nfeatures Kibana should treat as direct successors to deprecated
features\nin contexts that deal with whole features rather than
individual\nprivileges:\n\n```ts\ndeps.features.registerKibanaFeature({\n
deprecated: {\n notice: 'The feature is deprecated because … well,
there’s a reason.',\n --> replacedBy: ['feature_id_v2'], <--\n },\n id:
'feature_id'\n name: `Case #4 feature ${suffix} (DEPRECATED)`,\n
…\n});\n```\n\n## How to test\n\n1. Run test server\n```bash\nnode
scripts/functional_tests_server.js --config
x-pack/test/security_api_integration/features.config.ts\n```\n\n2.
Execute the following request from the Dev Tools (`case_4_feature_a`\nis
a deprecated feature that is replaced by multiple features
and\n**doesn't use** `deprecated.replacedBy`)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_a\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n3. Observe that in
response deprecated `case_4_feature_a` is replaced by\ntwo features (you
can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_a_v2\",\n \"case_4_feature_c\"\n ],\n \"_reserved\":
true\n}\n```\n\n4. Execute the following request from the Dev Tools
(`case_4_feature_b`\nis a deprecated feature that is replaced by
multiple features, but\n**uses** `deprecated.replacedBy` to set the
conceptual\nfeature-successor)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_b\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n5. Observe that in
response deprecated `case_4_feature_b` is replaced by\na single feature
(you can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_b_v2\"\n ],\n \"_reserved\": true\n}\n```\n\n__Required
by:__\nhttps://github.com/elastic/pull/202863#discussion_r1892672114\n\n//cc
@davismcphee","sha":"dd3ce0e7f534279f48be8c125853c89aa92969e2","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Security/Spaces","release_note:skip","Feature:Security/Authorization","v9.0.0","backport:prev-minor"],"title":"feat(security):
extend `Feature` definition to support explicit feature
replacements","number":206660,"url":"https://github.com/elastic/kibana/pull/206660","mergeCommit":{"message":"feat(security):
extend `Feature` definition to support explicit feature replacements
(elastic#206660)\n\n## Summary\n\nToday, when a developer deprecates a feature
and replaces its privileges\nwith those of another feature, we
reasonably assume that the new feature\nfully replaces the old one in
all possible contexts - whether in role\nmanagement UIs or in the Spaces
feature toggles visibility UI. However,\nwhen deprecated privileges are
replaced by the privileges of multiple\nfeatures, such as in
[this\ncase](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114)\nwhere
the Discover/Dashboard/Maps feature privileges are replaced by
the\nprivileges of Discover_v2/Dashboard_v2/Maps_v2, respectively,
**and**\nthe privileges of the Saved Query Management feature, the
choice is\nambiguous.\n\nWhich of these features should be treated as
the replacement for the\ndeprecated feature in contexts that deal with
entire features (like the\nSpaces feature toggles visibility UI) rather
than individual privileges\n(like in role management UIs)? Should all
referenced features be\nconsidered replacements? Or just a subset - or
even a single feature? If\nso, which one? Currently, we treat all
referenced features as\nreplacements for the deprecated feature, which
creates problems, as\ndescribed in detail in
[this\ndiscussion](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114).\n\nThis
PR allows developers to customize this behavior by specifying
which\nfeatures Kibana should treat as direct successors to deprecated
features\nin contexts that deal with whole features rather than
individual\nprivileges:\n\n```ts\ndeps.features.registerKibanaFeature({\n
deprecated: {\n notice: 'The feature is deprecated because … well,
there’s a reason.',\n --> replacedBy: ['feature_id_v2'], <--\n },\n id:
'feature_id'\n name: `Case #4 feature ${suffix} (DEPRECATED)`,\n
…\n});\n```\n\n## How to test\n\n1. Run test server\n```bash\nnode
scripts/functional_tests_server.js --config
x-pack/test/security_api_integration/features.config.ts\n```\n\n2.
Execute the following request from the Dev Tools (`case_4_feature_a`\nis
a deprecated feature that is replaced by multiple features
and\n**doesn't use** `deprecated.replacedBy`)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_a\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n3. Observe that in
response deprecated `case_4_feature_a` is replaced by\ntwo features (you
can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_a_v2\",\n \"case_4_feature_c\"\n ],\n \"_reserved\":
true\n}\n```\n\n4. Execute the following request from the Dev Tools
(`case_4_feature_b`\nis a deprecated feature that is replaced by
multiple features, but\n**uses** `deprecated.replacedBy` to set the
conceptual\nfeature-successor)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_b\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n5. Observe that in
response deprecated `case_4_feature_b` is replaced by\na single feature
(you can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_b_v2\"\n ],\n \"_reserved\": true\n}\n```\n\n__Required
by:__\nhttps://github.com/elastic/pull/202863#discussion_r1892672114\n\n//cc
@davismcphee","sha":"dd3ce0e7f534279f48be8c125853c89aa92969e2"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/206660","number":206660,"mergeCommit":{"message":"feat(security):
extend `Feature` definition to support explicit feature replacements
(elastic#206660)\n\n## Summary\n\nToday, when a developer deprecates a feature
and replaces its privileges\nwith those of another feature, we
reasonably assume that the new feature\nfully replaces the old one in
all possible contexts - whether in role\nmanagement UIs or in the Spaces
feature toggles visibility UI. However,\nwhen deprecated privileges are
replaced by the privileges of multiple\nfeatures, such as in
[this\ncase](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114)\nwhere
the Discover/Dashboard/Maps feature privileges are replaced by
the\nprivileges of Discover_v2/Dashboard_v2/Maps_v2, respectively,
**and**\nthe privileges of the Saved Query Management feature, the
choice is\nambiguous.\n\nWhich of these features should be treated as
the replacement for the\ndeprecated feature in contexts that deal with
entire features (like the\nSpaces feature toggles visibility UI) rather
than individual privileges\n(like in role management UIs)? Should all
referenced features be\nconsidered replacements? Or just a subset - or
even a single feature? If\nso, which one? Currently, we treat all
referenced features as\nreplacements for the deprecated feature, which
creates problems, as\ndescribed in detail in
[this\ndiscussion](https://github.com/elastic/kibana/pull/202863#discussion_r1892672114).\n\nThis
PR allows developers to customize this behavior by specifying
which\nfeatures Kibana should treat as direct successors to deprecated
features\nin contexts that deal with whole features rather than
individual\nprivileges:\n\n```ts\ndeps.features.registerKibanaFeature({\n
deprecated: {\n notice: 'The feature is deprecated because … well,
there’s a reason.',\n --> replacedBy: ['feature_id_v2'], <--\n },\n id:
'feature_id'\n name: `Case #4 feature ${suffix} (DEPRECATED)`,\n
…\n});\n```\n\n## How to test\n\n1. Run test server\n```bash\nnode
scripts/functional_tests_server.js --config
x-pack/test/security_api_integration/features.config.ts\n```\n\n2.
Execute the following request from the Dev Tools (`case_4_feature_a`\nis
a deprecated feature that is replaced by multiple features
and\n**doesn't use** `deprecated.replacedBy`)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_a\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n3. Observe that in
response deprecated `case_4_feature_a` is replaced by\ntwo features (you
can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_a_v2\",\n \"case_4_feature_c\"\n ],\n \"_reserved\":
true\n}\n```\n\n4. Execute the following request from the Dev Tools
(`case_4_feature_b`\nis a deprecated feature that is replaced by
multiple features, but\n**uses** `deprecated.replacedBy` to set the
conceptual\nfeature-successor)\n```http\nPUT
kbn:/api/spaces/space/default?overwrite=true\n{\n \"id\":\"default\",\n
\"name\":\"Default\",\n \"description\":\"This is your default
space!\",\n \"color\":\"#00bfb3\",\n
\"disabledFeatures\":[\"case_4_feature_b\"],\n \"_reserved\":true,\n
\"imageUrl\":\"\",\n \"initials\":\"D\"\n}\n```\n\n5. Observe that in
response deprecated `case_4_feature_b` is replaced by\na single feature
(you can also
check\nhttp://localhost:5620/app/management/kibana/spaces/edit/default
to see\nhow it's reflected in UI)\n```http\n{\n \"id\": \"default\",\n
\"name\": \"Default\",\n \"description\": \"This is your default
space!\",\n \"color\": \"#00bfb3\",\n \"initials\": \"D\",\n
\"imageUrl\": \"\",\n \"disabledFeatures\": [\n
\"case_4_feature_b_v2\"\n ],\n \"_reserved\": true\n}\n```\n\n__Required
by:__\nhttps://github.com/elastic/pull/202863#discussion_r1892672114\n\n//cc
@davismcphee","sha":"dd3ce0e7f534279f48be8c125853c89aa92969e2"}}]}]
BACKPORT-->

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@elastic.co>
weltenwort pushed a commit that referenced this pull request Apr 7, 2026
Closes elastic#258318
Closes elastic#258319

## Summary

Adds logic to the alert episodes table to display `.alert_actions`
information.

This includes:
- New action-specific API paths.
- Snooze
  - **Per group hash.**
- Button in the actions column opens a popover where an `until` can be
picked.
  - **When snoozed**
    - A bell shows up in the status column.
- Mouse over the bell icon to see until when the snooze is in effect.
- Unsnooze
  - **Per group hash.**
  - Clicking the button removes the snooze.
- Ack/Unack
  - **Per episode.**
  - Button in the actions column
  - When "acked", an icon shows in the status column.
- Tags
- This PR only handles displaying tags. They need to be created via API.
- Resolve/Unresolve
  - **Per group hash.**
  - Button inside the ellipsis always
- The status is turned to `inactive` **regardless of the "real"
status.**

<img width="1704" height="672" alt="Screenshot 2026-03-25 at 16 04 12"
src="https://github.com/user-attachments/assets/5ef4111a-6e0c-4114-a60e-ce5f81a86ac6"
/>


## Testing


<details> <summary>POST mock episodes</summary>

```
POST _bulk
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:00:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:01:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "pending" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:02:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:03:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "inactive" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:04:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:05:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:06:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-001", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:07:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:08:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "active" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:09:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "recovering" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:10:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "recovering" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:11:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:12:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "recovering" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:13:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-2", "episode": { "id": "ep-002", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:14:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-003", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:15:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-1", "episode": { "id": "ep-003", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:16:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-4", "episode": { "id": "ep-004", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:17:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-4", "episode": { "id": "ep-004", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:18:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-4", "episode": { "id": "ep-004", "status": "recovering" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:19:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "gh-4", "episode": { "id": "ep-004", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:20:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-5", "episode": { "id": "ep-005", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:21:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-5", "episode": { "id": "ep-005", "status": "pending" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:22:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-5", "episode": { "id": "ep-005", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:23:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:24:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:25:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "active" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:26:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-1" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-006", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:14:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-2" }, "group_hash": "elasticgh-7", "episode": { "id": "ep-007", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:15:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-2" }, "group_hash": "elasticgh-7", "episode": { "id": "ep-007", "status": "inactive" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:16:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-3" }, "group_hash": "elasticgh-8", "episode": { "id": "ep-008", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:17:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-3" }, "group_hash": "elasticgh-8", "episode": { "id": "ep-008", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:18:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-3" }, "group_hash": "elasticgh-8", "episode": { "id": "ep-008", "status": "recovering" }, "status": "recovered" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:20:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-4" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-009", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:21:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-4" }, "group_hash": "elasticgh-9", "episode": { "id": "ep-009", "status": "pending" }, "status": "no_data" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:23:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-5" }, "group_hash": "elasticgh-10", "episode": { "id": "ep-010", "status": "pending" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:24:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-5" }, "group_hash": "elasticgh-10", "episode": { "id": "ep-010", "status": "active" }, "status": "breached" }
{ "create": { "_index": ".rule-events" }}
{ "@timestamp": "2026-01-27T16:25:00.000Z", "source": "internal", "type": "alert", "rule": { "id": "rule-5" }, "group_hash": "elasticgh-10", "episode": { "id": "ep-010", "status": "active" }, "status": "no_data" }
```

</details>

- In the POST above, episodes 1 and 3, and episodes 6 and 9 have the
same group hashes.
- Go to `https://localhost:5601/app/observability/alerts-v2` and try all
buttons.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
weltenwort pushed a commit that referenced this pull request Apr 10, 2026
## Summary

Part of: elastic/security-team#15982.
(Resolves requirement `#4`)

This change introduces a dedicated **`StepCategory.KibanaCases`**
(`kibana.cases`) so Cases workflow steps are grouped under **Kibana →
Cases** in the workflow actions menu instead of sitting in the flat
Kibana list.

**Actions menu (`workflows_management`)**

- Builds a **Cases** subgroup (`id: kibana.cases`) under the Kibana
group via **`nestedGroups`**, then merges any non-empty nested group
into the parent’s **`options`** so the UI stays a normal tree of groups.
- Assigns **`pathIds`** on every group (full path from the root) so
choosing a nested group from **search** opens the correct depth (Kibana
→ Cases → …) instead of only appending the last segment.
- **`ActionsMenu`** uses `selectedOption.pathIds ?? [...currentPath,
id]` when entering a group.

**Shared spec**

- Adds **`StepCategory.KibanaCases`** in `@kbn/kbn-workflows` so step
definitions and UI routing can target the Cases bucket explicitly.

**Cases plugin**

- Updates all Cases **common workflow step** definitions to use
**`StepCategory.KibanaCases`** instead of **`StepCategory.Kibana`**.

**Agent builder**

- **`get_step_definitions_tool`**: maps connector types **`cases.*`** →
**`KibanaCases`** and keeps **`kibana.*`** → **`Kibana`**.

**Tests**

- Extends **`get_action_options.test.ts`** for nested Cases, empty Cases
group hidden, **`pathIds`**, and ordering expectations.

---

## Demo


https://github.com/user-attachments/assets/dc14c35d-f63c-4165-9c23-1590a22edf80

---
weltenwort pushed a commit that referenced this pull request Apr 21, 2026
## Summary

Fixes this test that has been failing CI on main and 9.4 backports:
```
Fleet Cypress Tests #4 / Assets - Real API for integration with ML and transforms should install integration with ML module & transforms
```

Cause was due to new version of lmd published
([lmd-3.0.0](elastic/integrations#17626)) that
caused index template and transform names to be changed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants