From 31fe21681f6e8c405d902599e08813ae0349efe3 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 18 Nov 2024 14:08:10 +0100 Subject: [PATCH 01/14] [ResponseOps][Alerts] Remove the alerts table registry (#194978) - Removes the alerts table registry in favor of a props-only configuration - Converts all the usages of the alerts table to use only props - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Christos Nasikas --- packages/kbn-repo-packages/package-map.json | 4154 +++++++++++++++++ .../use_data_grid_column_cell_actions.tsx | 4 +- .../components/alerts_table_sandbox.tsx | 8 +- .../public/plugin.tsx | 91 +- .../translations/translations/fr-FR.json | 8 +- .../translations/translations/ja-JP.json | 8 +- .../translations/translations/zh-CN.json | 6 +- .../common/lib/kibana/kibana_react.mock.tsx | 1 - .../components/case_view_alerts.test.tsx | 9 - .../case_view/components/case_view_alerts.tsx | 54 +- .../fleet/cypress.config.space_awareness.d.ts | 3 + .../fleet/cypress.config.space_awareness.js | 42 + .../alert_actions.tsx | 6 +- .../flyout_body.tsx | 40 + .../register_alerts_table_configuration.tsx | 145 - .../render_cell_value.tsx | 50 +- .../use_alerts_flyout.tsx | 55 - .../ml/public/alerting/register_ml_alerts.ts | 7 - .../contexts/kibana/use_field_formatter.ts | 5 +- .../explorer/alerts/alerts_panel.tsx | 149 +- .../triggers_actions_ui/common/utils.ts | 8 + .../alert_table_config_registry.test.ts | 184 - .../alert_table_config_registry.ts | 103 - .../public/application/connectors_app.tsx | 7 +- .../public/application/hooks/constants.ts | 14 +- .../public/application/rules_app.tsx | 7 +- .../connector_rules_list.test.tsx | 1 - .../components/stack_alerts_page.test.tsx | 61 +- .../components/stack_alerts_page.tsx | 21 +- .../alerts_table/alerts_data_grid.mock.tsx | 259 + .../alerts_table/alerts_data_grid.test.tsx | 479 ++ .../alerts_table/alerts_data_grid.tsx | 680 +++ .../alerts_flyout/alerts_flyout.test.tsx | 99 +- .../alerts_flyout/alerts_flyout.tsx | 91 +- .../alerts_flyout/alerts_flyout_header.tsx | 23 - .../default_alerts_flyout.test.tsx | 17 +- .../alerts_flyout/default_alerts_flyout.tsx | 157 +- .../sections/alerts_table/alerts_table.scss | 7 - .../alerts_table/alerts_table.test.tsx | 1357 +++--- .../sections/alerts_table/alerts_table.tsx | 1273 +++-- .../alerts_table/alerts_table_state.test.tsx | 1058 ----- .../alerts_table/alerts_table_state.tsx | 620 --- .../bulk_actions/bulk_actions.test.tsx | 763 ++- .../bulk_actions/components/column_header.tsx | 8 +- .../bulk_actions/components/index.ts | 2 +- .../bulk_actions/components/row_cell.tsx | 81 +- .../bulk_actions/components/toolbar.tsx | 12 +- .../get_leading_control_column.tsx | 29 - .../sections/alerts_table/cases/cell.test.tsx | 13 +- .../sections/alerts_table/cases/cell.tsx | 12 +- .../sections/alerts_table/cases/index.mock.ts | 12 +- .../alert_lifecycle_status_cell.test.tsx | 12 +- .../cells/alert_lifecycle_status_cell.tsx | 10 +- .../alerts_table/cells/default_cell.test.tsx | 12 +- .../alerts_table/cells/default_cell.tsx | 10 +- .../alerts_table/cells/index.test.tsx | 13 +- .../sections/alerts_table/cells/index.tsx | 12 +- .../alerts_table/cells/render_cell_value.tsx | 29 +- .../sections/alerts_table/configuration.tsx | 51 +- .../sections/alerts_table/constants.ts | 6 + .../contexts/alerts_table_context.ts | 15 - .../contexts/alerts_table_context.tsx | 27 + .../sections/alerts_table/empty_state.tsx | 31 +- .../hooks/alert_mute/use_alert_muted_state.ts | 8 +- .../alert_mute/use_get_muted_alerts.test.tsx | 43 +- .../hooks/alert_mute/use_get_muted_alerts.tsx | 60 +- .../hooks/alert_mute/use_mute_alert.test.tsx | 2 +- .../alert_mute/use_unmute_alert.test.tsx | 2 +- .../hooks/alert_mute/use_unmute_alert.ts | 2 +- .../alerts_table/hooks/apis/bulk_get_cases.ts | 11 +- .../hooks/apis/get_rules_muted_alerts.test.ts | 8 +- ...erts.ts => get_rules_with_muted_alerts.ts} | 22 +- .../alerts_table/hooks/use_actions_column.ts | 8 +- .../hooks/use_bulk_actions.test.tsx | 49 +- .../alerts_table/hooks/use_bulk_actions.ts | 29 +- .../hooks/use_bulk_get_cases.test.tsx | 10 +- .../alerts_table/hooks/use_bulk_get_cases.tsx | 45 +- .../use_bulk_get_maintenance_windows.test.ts | 26 +- .../use_bulk_get_maintenance_windows.tsx | 52 +- .../use_bulk_untrack_alerts_by_query.test.ts | 2 +- .../hooks/use_columns/use_columns.test.tsx | 2 +- .../hooks/use_columns/use_columns.ts | 2 +- .../alerts_table/hooks/use_pagination.test.ts | 32 +- .../alerts_table/hooks/use_pagination.ts | 19 +- .../sections/alerts_table/index.mock.ts | 8 +- .../sections/alerts_table/index.ts | 2 +- .../maintenance_windows/cell.test.tsx | 12 +- .../alerts_table/maintenance_windows/cell.tsx | 40 +- .../maintenance_windows/index.mock.ts | 26 +- .../row_actions/alert_actions_cell.tsx | 25 +- .../view_alert_details_alert_action.tsx | 8 +- .../view_rule_details_alert_action.tsx | 4 +- .../toolbar/components/inspect/index.test.tsx | 4 +- .../toolbar/components/inspect/index.tsx | 9 +- .../toolbar/components/inspect/modal.test.tsx | 2 +- .../toolbar/components/inspect/modal.tsx | 10 +- .../toolbar/toolbar_visibility.tsx | 94 +- .../sections/alerts_table/types.ts | 22 +- .../sections/alerts_table/utils.ts} | 4 +- .../rule_details/components/rule.test.tsx | 8 +- .../sections/rule_details/components/rule.tsx | 19 +- .../components/rule_alert_list.tsx | 5 +- .../application/sections/test_utils.tsx | 14 +- .../public/common/get_alerts_table.tsx | 8 +- .../get_alerts_table_default_row_actions.tsx | 2 +- .../public/common/get_alerts_table_state.tsx | 35 +- .../common/lib/kibana/kibana_react.mock.ts | 15 +- .../triggers_actions_ui/public/index.ts | 5 +- .../triggers_actions_ui/public/mocks.ts | 13 +- .../triggers_actions_ui/public/plugin.ts | 48 +- .../triggers_actions_ui/public/types.ts | 394 +- .../shared/triggers_actions_ui/tsconfig.json | 2 + .../components/app/alerts_overview/index.tsx | 15 +- .../shared/alerts/alerts_overview.tsx | 14 +- .../tabs/alerts/alerts_tab_content.tsx | 16 +- .../alerts_flyout/alerts_flyout.mock.ts | 3 +- .../alerts_flyout/alerts_flyout.stories.tsx | 3 +- .../alerts_flyout/alerts_flyout.test.tsx | 182 +- .../alerts_flyout/alerts_flyout.tsx | 44 +- .../alerts_flyout/alerts_flyout_body.test.tsx | 8 +- .../alerts_flyout/alerts_flyout_body.tsx | 32 +- .../alerts_flyout/alerts_flyout_footer.tsx | 42 +- .../alerts_flyout/alerts_flyout_header.tsx | 16 +- .../use_get_alert_flyout_components.tsx | 59 - .../get_alerts_page_table_configuration.tsx | 77 - .../components/alerts_table/alerts_table.tsx | 52 + ...ell_value.test.tsx => cell_value.test.tsx} | 50 +- .../{render_cell_value.tsx => cell_value.tsx} | 71 +- .../get_alerts_page_table_configuration.tsx | 58 - .../register_alerts_table_configuration.tsx | 66 - .../get_rule_details_table_configuration.tsx | 58 - .../alerts_table/slo/default_columns.tsx | 62 - .../get_slo_alerts_table_configuration.tsx | 40 - .../public/components/alerts_table/types.ts | 20 + .../plugins/observability/public/index.ts | 8 + .../components/related_alerts.tsx | 20 +- .../public/pages/alerts/alerts.tsx | 28 +- .../alerts/components/alert_actions.test.tsx | 13 +- .../pages/alerts/components/alert_actions.tsx | 17 +- .../public/pages/cases/components/cases.tsx | 3 +- .../public/pages/overview/overview.tsx | 27 +- .../components/rule_details_tabs.tsx | 22 +- .../plugins/observability/public/plugin.ts | 25 +- .../plugins/observability/tsconfig.json | 1 - .../alerts/components/slo_alerts_table.tsx | 94 +- .../components/slo_detail_alerts.tsx | 13 +- .../monitor_alerts/monitor_detail_alerts.tsx | 18 +- .../common/types/timeline/cells/index.ts | 4 +- .../cell_action/toggle_column.test.ts | 1 - .../cell_action/toggle_column.ts | 9 - .../tabs/alerts_tab/index.tsx | 38 +- .../settings_flyout/alerts_preview/index.tsx | 35 +- .../register_alerts_table_configuration.tsx | 124 - .../preview_table_cell_renderer.tsx | 9 +- .../pages/rule_details/index.tsx | 5 +- .../components/alerts_table/actions_cell.tsx | 91 + .../additional_toolbar_controls.test.tsx} | 24 +- .../additional_toolbar_controls.tsx | 142 + .../components/alerts_table/index.tsx | 313 +- .../components/alerts_table/types.ts | 21 + .../fetch_page_context.tsx | 12 +- .../security_solution_detections/index.ts | 4 +- .../render_cell_value.test.tsx | 23 +- .../render_cell_value.tsx | 58 +- .../use_actions_column.tsx | 164 - .../use_bulk_actions.tsx | 6 +- .../use_cell_actions.test.tsx | 18 +- .../use_cell_actions.tsx | 179 +- .../use_persistent_controls.tsx | 142 - ...trigger_actions_browser_fields_options.tsx | 48 +- .../detection_engine/detection_engine.tsx | 4 +- .../index.tsx | 4 +- .../public/management/cypress/tasks/alerts.ts | 2 +- .../security_solution/public/plugin.tsx | 33 +- .../body/renderers/column_renderer.ts | 6 +- .../body/renderers/get_column_renderer.ts | 4 +- .../body/renderers/user_profile_renderer.tsx | 4 +- .../services/observability/alerts/common.ts | 2 +- .../services/observability/overview/common.ts | 2 +- .../cypress/screens/alerts.ts | 2 +- 180 files changed, 9635 insertions(+), 6978 deletions(-) create mode 100644 packages/kbn-repo-packages/package-map.json create mode 100644 x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts create mode 100644 x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js create mode 100644 x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx delete mode 100644 x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/register_alerts_table_configuration.tsx delete mode 100644 x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/use_alerts_flyout.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.test.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.ts create mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx create mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.test.tsx create mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout_header.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/get_leading_control_column.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/contexts/alerts_table_context.ts create mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/contexts/alerts_table_context.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/{get_rules_muted_alerts.ts => get_rules_with_muted_alerts.ts} (67%) rename x-pack/platform/plugins/shared/{ml/public/alerting/anomaly_detection_alerts_table/index.ts => triggers_actions_ui/public/application/sections/alerts_table/utils.ts} (73%) delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/use_get_alert_flyout_components.tsx delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts/get_alerts_page_table_configuration.tsx create mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table.tsx rename x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/{render_cell_value.test.tsx => cell_value.test.tsx} (51%) rename x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/{render_cell_value.tsx => cell_value.tsx} (58%) delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/observability/get_alerts_page_table_configuration.tsx delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/register_alerts_table_configuration.tsx delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/rule_details/get_rule_details_table_configuration.tsx delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/slo/default_columns.tsx delete mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/slo/get_slo_alerts_table_configuration.tsx delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx create mode 100644 x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/actions_cell.tsx rename x-pack/solutions/security/plugins/security_solution/public/detections/{hooks/trigger_actions_alert_table/use_persistent_controls.test.tsx => components/alerts_table/additional_toolbar_controls.test.tsx} (78%) create mode 100644 x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/additional_toolbar_controls.tsx delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx diff --git a/packages/kbn-repo-packages/package-map.json b/packages/kbn-repo-packages/package-map.json new file mode 100644 index 0000000000000..4e76377822453 --- /dev/null +++ b/packages/kbn-repo-packages/package-map.json @@ -0,0 +1,4154 @@ +[ + [ + "@kbn/aad-fixtures-plugin", + "x-pack/test/alerting_api_integration/common/plugins/aad" + ], + [ + "@kbn/actions-plugin", + "x-pack/platform/plugins/shared/actions" + ], + [ + "@kbn/actions-simulators-plugin", + "x-pack/test/alerting_api_integration/common/plugins/actions_simulators" + ], + [ + "@kbn/actions-types", + "src/platform/packages/shared/kbn-actions-types" + ], + [ + "@kbn/advanced-settings-plugin", + "src/plugins/advanced_settings" + ], + [ + "@kbn/ai-assistant", + "x-pack/platform/packages/shared/kbn-ai-assistant" + ], + [ + "@kbn/ai-assistant-common", + "x-pack/platform/packages/shared/ai-assistant/common" + ], + [ + "@kbn/ai-assistant-icon", + "x-pack/platform/packages/shared/ai-assistant/icon" + ], + [ + "@kbn/ai-assistant-management-plugin", + "src/platform/plugins/shared/ai_assistant_management/selection" + ], + [ + "@kbn/aiops-change-point-detection", + "x-pack/platform/packages/private/ml/aiops_change_point_detection" + ], + [ + "@kbn/aiops-common", + "x-pack/platform/packages/shared/ml/aiops_common" + ], + [ + "@kbn/aiops-components", + "x-pack/platform/packages/private/ml/aiops_components" + ], + [ + "@kbn/aiops-log-pattern-analysis", + "x-pack/platform/packages/shared/ml/aiops_log_pattern_analysis" + ], + [ + "@kbn/aiops-log-rate-analysis", + "x-pack/platform/packages/shared/ml/aiops_log_rate_analysis" + ], + [ + "@kbn/aiops-plugin", + "x-pack/platform/plugins/shared/aiops" + ], + [ + "@kbn/aiops-test-utils", + "x-pack/platform/packages/private/ml/aiops_test_utils" + ], + [ + "@kbn/alerting-api-integration-helpers", + "x-pack/test/alerting_api_integration/packages/helpers" + ], + [ + "@kbn/alerting-api-integration-test-plugin", + "x-pack/test/alerting_api_integration/common/plugins/alerts" + ], + [ + "@kbn/alerting-comparators", + "x-pack/platform/packages/shared/kbn-alerting-comparators" + ], + [ + "@kbn/alerting-example-plugin", + "x-pack/examples/alerting_example" + ], + [ + "@kbn/alerting-fixture-plugin", + "x-pack/test/functional_with_es_ssl/plugins/alerts" + ], + [ + "@kbn/alerting-plugin", + "x-pack/platform/plugins/shared/alerting" + ], + [ + "@kbn/alerting-state-types", + "x-pack/platform/packages/private/kbn-alerting-state-types" + ], + [ + "@kbn/alerting-types", + "src/platform/packages/shared/kbn-alerting-types" + ], + [ + "@kbn/alerts-as-data-utils", + "src/platform/packages/shared/kbn-alerts-as-data-utils" + ], + [ + "@kbn/alerts-grouping", + "x-pack/solutions/observability/packages/kbn-alerts-grouping" + ], + [ + "@kbn/alerts-restricted-fixtures-plugin", + "x-pack/test/alerting_api_integration/common/plugins/alerts_restricted" + ], + [ + "@kbn/alerts-ui-shared", + "src/platform/packages/shared/kbn-alerts-ui-shared" + ], + [ + "@kbn/ambient-common-types", + "packages/kbn-ambient-common-types" + ], + [ + "@kbn/ambient-ftr-types", + "packages/kbn-ambient-ftr-types" + ], + [ + "@kbn/ambient-storybook-types", + "packages/kbn-ambient-storybook-types" + ], + [ + "@kbn/ambient-ui-types", + "packages/kbn-ambient-ui-types" + ], + [ + "@kbn/analytics", + "packages/kbn-analytics" + ], + [ + "@kbn/analytics-collection-utils", + "packages/analytics/utils/analytics_collection_utils" + ], + [ + "@kbn/analytics-ftr-helpers-plugin", + "test/analytics/plugins/analytics_ftr_helpers" + ], + [ + "@kbn/analytics-plugin-a-plugin", + "test/analytics/plugins/analytics_plugin_a" + ], + [ + "@kbn/apm-config-loader", + "packages/kbn-apm-config-loader" + ], + [ + "@kbn/apm-data-access-plugin", + "x-pack/solutions/observability/plugins/apm_data_access" + ], + [ + "@kbn/apm-data-view", + "src/platform/packages/shared/kbn-apm-data-view" + ], + [ + "@kbn/apm-ftr-e2e", + "x-pack/solutions/observability/plugins/apm/ftr_e2e" + ], + [ + "@kbn/apm-plugin", + "x-pack/solutions/observability/plugins/apm" + ], + [ + "@kbn/apm-synthtrace", + "packages/kbn-apm-synthtrace" + ], + [ + "@kbn/apm-synthtrace-client", + "packages/kbn-apm-synthtrace-client" + ], + [ + "@kbn/apm-types", + "x-pack/solutions/observability/packages/kbn-apm-types" + ], + [ + "@kbn/apm-utils", + "src/platform/packages/shared/kbn-apm-utils" + ], + [ + "@kbn/app-link-test-plugin", + "test/plugin_functional/plugins/app_link_test" + ], + [ + "@kbn/application-usage-test-plugin", + "x-pack/test/usage_collection/plugins/application_usage_test" + ], + [ + "@kbn/asset-inventory-plugin", + "x-pack/solutions/security/plugins/asset_inventory" + ], + [ + "@kbn/audit-log-plugin", + "x-pack/test/security_api_integration/plugins/audit_log" + ], + [ + "@kbn/avc-banner", + "src/platform/packages/shared/kbn-avc-banner" + ], + [ + "@kbn/axe-config", + "packages/kbn-axe-config" + ], + [ + "@kbn/babel-preset", + "packages/kbn-babel-preset" + ], + [ + "@kbn/babel-register", + "packages/kbn-babel-register" + ], + [ + "@kbn/babel-transform", + "packages/kbn-babel-transform" + ], + [ + "@kbn/banners-plugin", + "x-pack/plugins/banners" + ], + [ + "@kbn/bazel-runner", + "packages/kbn-bazel-runner" + ], + [ + "@kbn/calculate-auto", + "packages/kbn-calculate-auto" + ], + [ + "@kbn/calculate-width-from-char-count", + "packages/kbn-calculate-width-from-char-count" + ], + [ + "@kbn/canvas-plugin", + "x-pack/platform/plugins/private/canvas" + ], + [ + "@kbn/capture-oas-snapshot-cli", + "packages/kbn-capture-oas-snapshot-cli" + ], + [ + "@kbn/cases-api-integration-test-plugin", + "x-pack/test/cases_api_integration/common/plugins/cases" + ], + [ + "@kbn/cases-components", + "src/platform/packages/shared/kbn-cases-components" + ], + [ + "@kbn/cases-plugin", + "x-pack/platform/plugins/shared/cases" + ], + [ + "@kbn/cbor", + "packages/kbn-cbor" + ], + [ + "@kbn/cell-actions", + "src/platform/packages/shared/kbn-cell-actions" + ], + [ + "@kbn/chart-expressions-common", + "src/plugins/chart_expressions/common" + ], + [ + "@kbn/chart-icons", + "packages/kbn-chart-icons" + ], + [ + "@kbn/charts-plugin", + "src/plugins/charts" + ], + [ + "@kbn/charts-theme", + "packages/kbn-charts-theme" + ], + [ + "@kbn/check-mappings-update-cli", + "packages/kbn-check-mappings-update-cli" + ], + [ + "@kbn/check-prod-native-modules-cli", + "packages/kbn-check-prod-native-modules-cli" + ], + [ + "@kbn/ci-stats-core", + "packages/kbn-ci-stats-core" + ], + [ + "@kbn/ci-stats-performance-metrics", + "packages/kbn-ci-stats-performance-metrics" + ], + [ + "@kbn/ci-stats-reporter", + "packages/kbn-ci-stats-reporter" + ], + [ + "@kbn/ci-stats-shipper-cli", + "packages/kbn-ci-stats-shipper-cli" + ], + [ + "@kbn/cli-dev-mode", + "packages/kbn-cli-dev-mode" + ], + [ + "@kbn/cloud", + "packages/cloud" + ], + [ + "@kbn/cloud-chat-plugin", + "x-pack/plugins/cloud_integrations/cloud_chat" + ], + [ + "@kbn/cloud-data-migration-plugin", + "x-pack/platform/plugins/private/cloud_integrations/cloud_data_migration" + ], + [ + "@kbn/cloud-defend-plugin", + "x-pack/solutions/security/plugins/cloud_defend" + ], + [ + "@kbn/cloud-experiments-plugin", + "x-pack/plugins/cloud_integrations/cloud_experiments" + ], + [ + "@kbn/cloud-full-story-plugin", + "x-pack/plugins/cloud_integrations/cloud_full_story" + ], + [ + "@kbn/cloud-integration-saml-provider-plugin", + "x-pack/test/cloud_integration/plugins/saml_provider" + ], + [ + "@kbn/cloud-links-plugin", + "x-pack/plugins/cloud_integrations/cloud_links" + ], + [ + "@kbn/cloud-plugin", + "x-pack/plugins/cloud" + ], + [ + "@kbn/cloud-security-posture", + "x-pack/solutions/security/packages/kbn-cloud-security-posture/public" + ], + [ + "@kbn/cloud-security-posture-common", + "x-pack/platform/packages/shared/kbn-cloud-security-posture/common" + ], + [ + "@kbn/cloud-security-posture-graph", + "x-pack/solutions/security/packages/kbn-cloud-security-posture/graph" + ], + [ + "@kbn/cloud-security-posture-plugin", + "x-pack/solutions/security/plugins/cloud_security_posture" + ], + [ + "@kbn/code-editor", + "packages/shared-ux/code_editor/impl" + ], + [ + "@kbn/code-editor-mock", + "packages/shared-ux/code_editor/mocks" + ], + [ + "@kbn/code-owners", + "packages/kbn-code-owners" + ], + [ + "@kbn/coloring", + "packages/kbn-coloring" + ], + [ + "@kbn/config", + "packages/kbn-config" + ], + [ + "@kbn/config-mocks", + "packages/kbn-config-mocks" + ], + [ + "@kbn/config-schema", + "packages/kbn-config-schema" + ], + [ + "@kbn/console-plugin", + "src/platform/plugins/shared/console" + ], + [ + "@kbn/content-management-content-editor", + "packages/content-management/content_editor" + ], + [ + "@kbn/content-management-content-insights-public", + "packages/content-management/content_insights/content_insights_public" + ], + [ + "@kbn/content-management-content-insights-server", + "packages/content-management/content_insights/content_insights_server" + ], + [ + "@kbn/content-management-examples-plugin", + "examples/content_management_examples" + ], + [ + "@kbn/content-management-favorites-common", + "packages/content-management/favorites/favorites_common" + ], + [ + "@kbn/content-management-favorites-public", + "packages/content-management/favorites/favorites_public" + ], + [ + "@kbn/content-management-favorites-server", + "packages/content-management/favorites/favorites_server" + ], + [ + "@kbn/content-management-plugin", + "src/plugins/content_management" + ], + [ + "@kbn/content-management-tabbed-table-list-view", + "packages/content-management/tabbed_table_list_view" + ], + [ + "@kbn/content-management-table-list-view", + "packages/content-management/table_list_view" + ], + [ + "@kbn/content-management-table-list-view-common", + "packages/content-management/table_list_view_common" + ], + [ + "@kbn/content-management-table-list-view-table", + "packages/content-management/table_list_view_table" + ], + [ + "@kbn/content-management-user-profiles", + "packages/content-management/user_profiles" + ], + [ + "@kbn/content-management-utils", + "src/platform/packages/shared/kbn-content-management-utils" + ], + [ + "@kbn/controls-example-plugin", + "examples/controls_example" + ], + [ + "@kbn/controls-plugin", + "src/platform/plugins/shared/controls" + ], + [ + "@kbn/core", + "src/core" + ], + [ + "@kbn/core-analytics-browser", + "src/core/packages/analytics/browser" + ], + [ + "@kbn/core-analytics-browser-internal", + "src/core/packages/analytics/browser-internal" + ], + [ + "@kbn/core-analytics-browser-mocks", + "packages/core/analytics/core-analytics-browser-mocks" + ], + [ + "@kbn/core-analytics-server", + "src/core/packages/analytics/server" + ], + [ + "@kbn/core-analytics-server-internal", + "src/core/packages/analytics/server-internal" + ], + [ + "@kbn/core-analytics-server-mocks", + "packages/core/analytics/core-analytics-server-mocks" + ], + [ + "@kbn/core-app-status-plugin", + "test/plugin_functional/plugins/core_app_status" + ], + [ + "@kbn/core-application-browser", + "src/core/packages/application/browser" + ], + [ + "@kbn/core-application-browser-internal", + "src/core/packages/application/browser-internal" + ], + [ + "@kbn/core-application-browser-mocks", + "packages/core/application/core-application-browser-mocks" + ], + [ + "@kbn/core-application-common", + "src/core/packages/application/common" + ], + [ + "@kbn/core-apps-browser-internal", + "src/core/packages/apps/browser-internal" + ], + [ + "@kbn/core-apps-browser-mocks", + "packages/core/apps/core-apps-browser-mocks" + ], + [ + "@kbn/core-apps-server-internal", + "src/core/packages/apps/server-internal" + ], + [ + "@kbn/core-base-browser-internal", + "src/core/packages/base/browser-internal" + ], + [ + "@kbn/core-base-browser-mocks", + "packages/core/base/core-base-browser-mocks" + ], + [ + "@kbn/core-base-common", + "src/core/packages/base/common" + ], + [ + "@kbn/core-base-common-internal", + "packages/core/base/core-base-common-internal" + ], + [ + "@kbn/core-base-server-internal", + "packages/core/base/core-base-server-internal" + ], + [ + "@kbn/core-base-server-mocks", + "packages/core/base/core-base-server-mocks" + ], + [ + "@kbn/core-capabilities-browser-internal", + "packages/core/capabilities/core-capabilities-browser-internal" + ], + [ + "@kbn/core-capabilities-browser-mocks", + "packages/core/capabilities/core-capabilities-browser-mocks" + ], + [ + "@kbn/core-capabilities-common", + "packages/core/capabilities/core-capabilities-common" + ], + [ + "@kbn/core-capabilities-server", + "packages/core/capabilities/core-capabilities-server" + ], + [ + "@kbn/core-capabilities-server-internal", + "packages/core/capabilities/core-capabilities-server-internal" + ], + [ + "@kbn/core-capabilities-server-mocks", + "packages/core/capabilities/core-capabilities-server-mocks" + ], + [ + "@kbn/core-chrome-browser", + "packages/core/chrome/core-chrome-browser" + ], + [ + "@kbn/core-chrome-browser-internal", + "packages/core/chrome/core-chrome-browser-internal" + ], + [ + "@kbn/core-chrome-browser-mocks", + "packages/core/chrome/core-chrome-browser-mocks" + ], + [ + "@kbn/core-config-server-internal", + "packages/core/config/core-config-server-internal" + ], + [ + "@kbn/core-custom-branding-browser", + "packages/core/custom-branding/core-custom-branding-browser" + ], + [ + "@kbn/core-custom-branding-browser-internal", + "packages/core/custom-branding/core-custom-branding-browser-internal" + ], + [ + "@kbn/core-custom-branding-browser-mocks", + "packages/core/custom-branding/core-custom-branding-browser-mocks" + ], + [ + "@kbn/core-custom-branding-common", + "packages/core/custom-branding/core-custom-branding-common" + ], + [ + "@kbn/core-custom-branding-server", + "packages/core/custom-branding/core-custom-branding-server" + ], + [ + "@kbn/core-custom-branding-server-internal", + "packages/core/custom-branding/core-custom-branding-server-internal" + ], + [ + "@kbn/core-custom-branding-server-mocks", + "packages/core/custom-branding/core-custom-branding-server-mocks" + ], + [ + "@kbn/core-deprecations-browser", + "packages/core/deprecations/core-deprecations-browser" + ], + [ + "@kbn/core-deprecations-browser-internal", + "packages/core/deprecations/core-deprecations-browser-internal" + ], + [ + "@kbn/core-deprecations-browser-mocks", + "packages/core/deprecations/core-deprecations-browser-mocks" + ], + [ + "@kbn/core-deprecations-common", + "packages/core/deprecations/core-deprecations-common" + ], + [ + "@kbn/core-deprecations-server", + "packages/core/deprecations/core-deprecations-server" + ], + [ + "@kbn/core-deprecations-server-internal", + "packages/core/deprecations/core-deprecations-server-internal" + ], + [ + "@kbn/core-deprecations-server-mocks", + "packages/core/deprecations/core-deprecations-server-mocks" + ], + [ + "@kbn/core-doc-links-browser", + "packages/core/doc-links/core-doc-links-browser" + ], + [ + "@kbn/core-doc-links-browser-internal", + "packages/core/doc-links/core-doc-links-browser-internal" + ], + [ + "@kbn/core-doc-links-browser-mocks", + "packages/core/doc-links/core-doc-links-browser-mocks" + ], + [ + "@kbn/core-doc-links-server", + "packages/core/doc-links/core-doc-links-server" + ], + [ + "@kbn/core-doc-links-server-internal", + "packages/core/doc-links/core-doc-links-server-internal" + ], + [ + "@kbn/core-doc-links-server-mocks", + "packages/core/doc-links/core-doc-links-server-mocks" + ], + [ + "@kbn/core-elasticsearch-client-server-internal", + "packages/core/elasticsearch/core-elasticsearch-client-server-internal" + ], + [ + "@kbn/core-elasticsearch-client-server-mocks", + "packages/core/elasticsearch/core-elasticsearch-client-server-mocks" + ], + [ + "@kbn/core-elasticsearch-server", + "packages/core/elasticsearch/core-elasticsearch-server" + ], + [ + "@kbn/core-elasticsearch-server-internal", + "packages/core/elasticsearch/core-elasticsearch-server-internal" + ], + [ + "@kbn/core-elasticsearch-server-mocks", + "packages/core/elasticsearch/core-elasticsearch-server-mocks" + ], + [ + "@kbn/core-environment-server-internal", + "packages/core/environment/core-environment-server-internal" + ], + [ + "@kbn/core-environment-server-mocks", + "packages/core/environment/core-environment-server-mocks" + ], + [ + "@kbn/core-execution-context-browser", + "packages/core/execution-context/core-execution-context-browser" + ], + [ + "@kbn/core-execution-context-browser-internal", + "packages/core/execution-context/core-execution-context-browser-internal" + ], + [ + "@kbn/core-execution-context-browser-mocks", + "packages/core/execution-context/core-execution-context-browser-mocks" + ], + [ + "@kbn/core-execution-context-common", + "packages/core/execution-context/core-execution-context-common" + ], + [ + "@kbn/core-execution-context-server", + "packages/core/execution-context/core-execution-context-server" + ], + [ + "@kbn/core-execution-context-server-internal", + "packages/core/execution-context/core-execution-context-server-internal" + ], + [ + "@kbn/core-execution-context-server-mocks", + "packages/core/execution-context/core-execution-context-server-mocks" + ], + [ + "@kbn/core-fatal-errors-browser", + "packages/core/fatal-errors/core-fatal-errors-browser" + ], + [ + "@kbn/core-fatal-errors-browser-internal", + "packages/core/fatal-errors/core-fatal-errors-browser-internal" + ], + [ + "@kbn/core-fatal-errors-browser-mocks", + "packages/core/fatal-errors/core-fatal-errors-browser-mocks" + ], + [ + "@kbn/core-feature-flags-browser", + "packages/core/feature-flags/core-feature-flags-browser" + ], + [ + "@kbn/core-feature-flags-browser-internal", + "packages/core/feature-flags/core-feature-flags-browser-internal" + ], + [ + "@kbn/core-feature-flags-browser-mocks", + "packages/core/feature-flags/core-feature-flags-browser-mocks" + ], + [ + "@kbn/core-feature-flags-server", + "packages/core/feature-flags/core-feature-flags-server" + ], + [ + "@kbn/core-feature-flags-server-internal", + "packages/core/feature-flags/core-feature-flags-server-internal" + ], + [ + "@kbn/core-feature-flags-server-mocks", + "packages/core/feature-flags/core-feature-flags-server-mocks" + ], + [ + "@kbn/core-history-block-plugin", + "test/plugin_functional/plugins/core_history_block" + ], + [ + "@kbn/core-http-browser", + "packages/core/http/core-http-browser" + ], + [ + "@kbn/core-http-browser-internal", + "packages/core/http/core-http-browser-internal" + ], + [ + "@kbn/core-http-browser-mocks", + "packages/core/http/core-http-browser-mocks" + ], + [ + "@kbn/core-http-common", + "packages/core/http/core-http-common" + ], + [ + "@kbn/core-http-context-server-internal", + "packages/core/http/core-http-context-server-internal" + ], + [ + "@kbn/core-http-context-server-mocks", + "packages/core/http/core-http-context-server-mocks" + ], + [ + "@kbn/core-http-plugin", + "test/plugin_functional/plugins/core_http" + ], + [ + "@kbn/core-http-request-handler-context-server", + "packages/core/http/core-http-request-handler-context-server" + ], + [ + "@kbn/core-http-request-handler-context-server-internal", + "packages/core/http/core-http-request-handler-context-server-internal" + ], + [ + "@kbn/core-http-resources-server", + "packages/core/http/core-http-resources-server" + ], + [ + "@kbn/core-http-resources-server-internal", + "packages/core/http/core-http-resources-server-internal" + ], + [ + "@kbn/core-http-resources-server-mocks", + "packages/core/http/core-http-resources-server-mocks" + ], + [ + "@kbn/core-http-router-server-internal", + "packages/core/http/core-http-router-server-internal" + ], + [ + "@kbn/core-http-router-server-mocks", + "packages/core/http/core-http-router-server-mocks" + ], + [ + "@kbn/core-http-server", + "packages/core/http/core-http-server" + ], + [ + "@kbn/core-http-server-internal", + "packages/core/http/core-http-server-internal" + ], + [ + "@kbn/core-http-server-mocks", + "packages/core/http/core-http-server-mocks" + ], + [ + "@kbn/core-http-server-utils", + "packages/core/http/core-http-server-utils" + ], + [ + "@kbn/core-i18n-browser", + "packages/core/i18n/core-i18n-browser" + ], + [ + "@kbn/core-i18n-browser-internal", + "packages/core/i18n/core-i18n-browser-internal" + ], + [ + "@kbn/core-i18n-browser-mocks", + "packages/core/i18n/core-i18n-browser-mocks" + ], + [ + "@kbn/core-i18n-server", + "packages/core/i18n/core-i18n-server" + ], + [ + "@kbn/core-i18n-server-internal", + "packages/core/i18n/core-i18n-server-internal" + ], + [ + "@kbn/core-i18n-server-mocks", + "packages/core/i18n/core-i18n-server-mocks" + ], + [ + "@kbn/core-injected-metadata-browser-internal", + "packages/core/injected-metadata/core-injected-metadata-browser-internal" + ], + [ + "@kbn/core-injected-metadata-browser-mocks", + "packages/core/injected-metadata/core-injected-metadata-browser-mocks" + ], + [ + "@kbn/core-injected-metadata-common-internal", + "packages/core/injected-metadata/core-injected-metadata-common-internal" + ], + [ + "@kbn/core-integrations-browser-internal", + "packages/core/integrations/core-integrations-browser-internal" + ], + [ + "@kbn/core-integrations-browser-mocks", + "packages/core/integrations/core-integrations-browser-mocks" + ], + [ + "@kbn/core-lifecycle-browser", + "packages/core/lifecycle/core-lifecycle-browser" + ], + [ + "@kbn/core-lifecycle-browser-internal", + "packages/core/lifecycle/core-lifecycle-browser-internal" + ], + [ + "@kbn/core-lifecycle-browser-mocks", + "packages/core/lifecycle/core-lifecycle-browser-mocks" + ], + [ + "@kbn/core-lifecycle-server", + "packages/core/lifecycle/core-lifecycle-server" + ], + [ + "@kbn/core-lifecycle-server-internal", + "packages/core/lifecycle/core-lifecycle-server-internal" + ], + [ + "@kbn/core-lifecycle-server-mocks", + "packages/core/lifecycle/core-lifecycle-server-mocks" + ], + [ + "@kbn/core-logging-browser-internal", + "packages/core/logging/core-logging-browser-internal" + ], + [ + "@kbn/core-logging-browser-mocks", + "packages/core/logging/core-logging-browser-mocks" + ], + [ + "@kbn/core-logging-common-internal", + "packages/core/logging/core-logging-common-internal" + ], + [ + "@kbn/core-logging-server", + "packages/core/logging/core-logging-server" + ], + [ + "@kbn/core-logging-server-internal", + "packages/core/logging/core-logging-server-internal" + ], + [ + "@kbn/core-logging-server-mocks", + "packages/core/logging/core-logging-server-mocks" + ], + [ + "@kbn/core-metrics-collectors-server-internal", + "packages/core/metrics/core-metrics-collectors-server-internal" + ], + [ + "@kbn/core-metrics-collectors-server-mocks", + "packages/core/metrics/core-metrics-collectors-server-mocks" + ], + [ + "@kbn/core-metrics-server", + "packages/core/metrics/core-metrics-server" + ], + [ + "@kbn/core-metrics-server-internal", + "packages/core/metrics/core-metrics-server-internal" + ], + [ + "@kbn/core-metrics-server-mocks", + "packages/core/metrics/core-metrics-server-mocks" + ], + [ + "@kbn/core-mount-utils-browser", + "packages/core/mount-utils/core-mount-utils-browser" + ], + [ + "@kbn/core-mount-utils-browser-internal", + "packages/core/mount-utils/core-mount-utils-browser-internal" + ], + [ + "@kbn/core-node-server", + "packages/core/node/core-node-server" + ], + [ + "@kbn/core-node-server-internal", + "packages/core/node/core-node-server-internal" + ], + [ + "@kbn/core-node-server-mocks", + "packages/core/node/core-node-server-mocks" + ], + [ + "@kbn/core-notifications-browser", + "packages/core/notifications/core-notifications-browser" + ], + [ + "@kbn/core-notifications-browser-internal", + "packages/core/notifications/core-notifications-browser-internal" + ], + [ + "@kbn/core-notifications-browser-mocks", + "packages/core/notifications/core-notifications-browser-mocks" + ], + [ + "@kbn/core-overlays-browser", + "packages/core/overlays/core-overlays-browser" + ], + [ + "@kbn/core-overlays-browser-internal", + "packages/core/overlays/core-overlays-browser-internal" + ], + [ + "@kbn/core-overlays-browser-mocks", + "packages/core/overlays/core-overlays-browser-mocks" + ], + [ + "@kbn/core-plugin-a-plugin", + "test/plugin_functional/plugins/core_plugin_a" + ], + [ + "@kbn/core-plugin-appleave-plugin", + "test/plugin_functional/plugins/core_plugin_appleave" + ], + [ + "@kbn/core-plugin-b-plugin", + "test/plugin_functional/plugins/core_plugin_b" + ], + [ + "@kbn/core-plugin-chromeless-plugin", + "test/plugin_functional/plugins/core_plugin_chromeless" + ], + [ + "@kbn/core-plugin-deep-links-plugin", + "test/plugin_functional/plugins/core_plugin_deep_links" + ], + [ + "@kbn/core-plugin-deprecations-plugin", + "test/plugin_functional/plugins/core_plugin_deprecations" + ], + [ + "@kbn/core-plugin-dynamic-resolving-a", + "test/plugin_functional/plugins/core_dynamic_resolving_a" + ], + [ + "@kbn/core-plugin-dynamic-resolving-b", + "test/plugin_functional/plugins/core_dynamic_resolving_b" + ], + [ + "@kbn/core-plugin-execution-context-plugin", + "test/plugin_functional/plugins/core_plugin_execution_context" + ], + [ + "@kbn/core-plugin-helpmenu-plugin", + "test/plugin_functional/plugins/core_plugin_helpmenu" + ], + [ + "@kbn/core-plugin-initializer-context-plugin", + "test/node_roles_functional/plugins/core_plugin_initializer_context" + ], + [ + "@kbn/core-plugin-route-timeouts-plugin", + "test/plugin_functional/plugins/core_plugin_route_timeouts" + ], + [ + "@kbn/core-plugin-static-assets-plugin", + "test/plugin_functional/plugins/core_plugin_static_assets" + ], + [ + "@kbn/core-plugins-base-server-internal", + "packages/core/plugins/core-plugins-base-server-internal" + ], + [ + "@kbn/core-plugins-browser", + "packages/core/plugins/core-plugins-browser" + ], + [ + "@kbn/core-plugins-browser-internal", + "packages/core/plugins/core-plugins-browser-internal" + ], + [ + "@kbn/core-plugins-browser-mocks", + "packages/core/plugins/core-plugins-browser-mocks" + ], + [ + "@kbn/core-plugins-contracts-browser", + "packages/core/plugins/core-plugins-contracts-browser" + ], + [ + "@kbn/core-plugins-contracts-server", + "packages/core/plugins/core-plugins-contracts-server" + ], + [ + "@kbn/core-plugins-server", + "packages/core/plugins/core-plugins-server" + ], + [ + "@kbn/core-plugins-server-internal", + "packages/core/plugins/core-plugins-server-internal" + ], + [ + "@kbn/core-plugins-server-mocks", + "packages/core/plugins/core-plugins-server-mocks" + ], + [ + "@kbn/core-preboot-server", + "packages/core/preboot/core-preboot-server" + ], + [ + "@kbn/core-preboot-server-internal", + "packages/core/preboot/core-preboot-server-internal" + ], + [ + "@kbn/core-preboot-server-mocks", + "packages/core/preboot/core-preboot-server-mocks" + ], + [ + "@kbn/core-provider-plugin", + "test/plugin_functional/plugins/core_provider_plugin" + ], + [ + "@kbn/core-rendering-browser", + "packages/core/rendering/core-rendering-browser" + ], + [ + "@kbn/core-rendering-browser-internal", + "packages/core/rendering/core-rendering-browser-internal" + ], + [ + "@kbn/core-rendering-browser-mocks", + "packages/core/rendering/core-rendering-browser-mocks" + ], + [ + "@kbn/core-rendering-server-internal", + "packages/core/rendering/core-rendering-server-internal" + ], + [ + "@kbn/core-rendering-server-mocks", + "packages/core/rendering/core-rendering-server-mocks" + ], + [ + "@kbn/core-root-browser-internal", + "packages/core/root/core-root-browser-internal" + ], + [ + "@kbn/core-root-server-internal", + "packages/core/root/core-root-server-internal" + ], + [ + "@kbn/core-saved-objects-api-browser", + "packages/core/saved-objects/core-saved-objects-api-browser" + ], + [ + "@kbn/core-saved-objects-api-server", + "packages/core/saved-objects/core-saved-objects-api-server" + ], + [ + "@kbn/core-saved-objects-api-server-internal", + "packages/core/saved-objects/core-saved-objects-api-server-internal" + ], + [ + "@kbn/core-saved-objects-api-server-mocks", + "packages/core/saved-objects/core-saved-objects-api-server-mocks" + ], + [ + "@kbn/core-saved-objects-base-server-internal", + "packages/core/saved-objects/core-saved-objects-base-server-internal" + ], + [ + "@kbn/core-saved-objects-base-server-mocks", + "packages/core/saved-objects/core-saved-objects-base-server-mocks" + ], + [ + "@kbn/core-saved-objects-browser", + "packages/core/saved-objects/core-saved-objects-browser" + ], + [ + "@kbn/core-saved-objects-browser-internal", + "packages/core/saved-objects/core-saved-objects-browser-internal" + ], + [ + "@kbn/core-saved-objects-browser-mocks", + "packages/core/saved-objects/core-saved-objects-browser-mocks" + ], + [ + "@kbn/core-saved-objects-common", + "packages/core/saved-objects/core-saved-objects-common" + ], + [ + "@kbn/core-saved-objects-import-export-server-internal", + "packages/core/saved-objects/core-saved-objects-import-export-server-internal" + ], + [ + "@kbn/core-saved-objects-import-export-server-mocks", + "packages/core/saved-objects/core-saved-objects-import-export-server-mocks" + ], + [ + "@kbn/core-saved-objects-migration-server-internal", + "packages/core/saved-objects/core-saved-objects-migration-server-internal" + ], + [ + "@kbn/core-saved-objects-migration-server-mocks", + "packages/core/saved-objects/core-saved-objects-migration-server-mocks" + ], + [ + "@kbn/core-saved-objects-server", + "packages/core/saved-objects/core-saved-objects-server" + ], + [ + "@kbn/core-saved-objects-server-internal", + "packages/core/saved-objects/core-saved-objects-server-internal" + ], + [ + "@kbn/core-saved-objects-server-mocks", + "packages/core/saved-objects/core-saved-objects-server-mocks" + ], + [ + "@kbn/core-saved-objects-utils-server", + "packages/core/saved-objects/core-saved-objects-utils-server" + ], + [ + "@kbn/core-security-browser", + "packages/core/security/core-security-browser" + ], + [ + "@kbn/core-security-browser-internal", + "packages/core/security/core-security-browser-internal" + ], + [ + "@kbn/core-security-browser-mocks", + "packages/core/security/core-security-browser-mocks" + ], + [ + "@kbn/core-security-common", + "packages/core/security/core-security-common" + ], + [ + "@kbn/core-security-server", + "packages/core/security/core-security-server" + ], + [ + "@kbn/core-security-server-internal", + "packages/core/security/core-security-server-internal" + ], + [ + "@kbn/core-security-server-mocks", + "packages/core/security/core-security-server-mocks" + ], + [ + "@kbn/core-status-common", + "packages/core/status/core-status-common" + ], + [ + "@kbn/core-status-server", + "packages/core/status/core-status-server" + ], + [ + "@kbn/core-status-server-internal", + "packages/core/status/core-status-server-internal" + ], + [ + "@kbn/core-status-server-mocks", + "packages/core/status/core-status-server-mocks" + ], + [ + "@kbn/core-test-helpers-deprecations-getters", + "packages/core/test-helpers/core-test-helpers-deprecations-getters" + ], + [ + "@kbn/core-test-helpers-http-setup-browser", + "packages/core/test-helpers/core-test-helpers-http-setup-browser" + ], + [ + "@kbn/core-test-helpers-kbn-server", + "packages/core/test-helpers/core-test-helpers-kbn-server" + ], + [ + "@kbn/core-test-helpers-model-versions", + "packages/core/test-helpers/core-test-helpers-model-versions" + ], + [ + "@kbn/core-test-helpers-so-type-serializer", + "packages/core/test-helpers/core-test-helpers-so-type-serializer" + ], + [ + "@kbn/core-test-helpers-test-utils", + "packages/core/test-helpers/core-test-helpers-test-utils" + ], + [ + "@kbn/core-theme-browser", + "packages/core/theme/core-theme-browser" + ], + [ + "@kbn/core-theme-browser-internal", + "packages/core/theme/core-theme-browser-internal" + ], + [ + "@kbn/core-theme-browser-mocks", + "packages/core/theme/core-theme-browser-mocks" + ], + [ + "@kbn/core-ui-settings-browser", + "packages/core/ui-settings/core-ui-settings-browser" + ], + [ + "@kbn/core-ui-settings-browser-internal", + "packages/core/ui-settings/core-ui-settings-browser-internal" + ], + [ + "@kbn/core-ui-settings-browser-mocks", + "packages/core/ui-settings/core-ui-settings-browser-mocks" + ], + [ + "@kbn/core-ui-settings-common", + "packages/core/ui-settings/core-ui-settings-common" + ], + [ + "@kbn/core-ui-settings-server", + "packages/core/ui-settings/core-ui-settings-server" + ], + [ + "@kbn/core-ui-settings-server-internal", + "packages/core/ui-settings/core-ui-settings-server-internal" + ], + [ + "@kbn/core-ui-settings-server-mocks", + "packages/core/ui-settings/core-ui-settings-server-mocks" + ], + [ + "@kbn/core-usage-data-base-server-internal", + "packages/core/usage-data/core-usage-data-base-server-internal" + ], + [ + "@kbn/core-usage-data-server", + "packages/core/usage-data/core-usage-data-server" + ], + [ + "@kbn/core-usage-data-server-internal", + "packages/core/usage-data/core-usage-data-server-internal" + ], + [ + "@kbn/core-usage-data-server-mocks", + "packages/core/usage-data/core-usage-data-server-mocks" + ], + [ + "@kbn/core-user-profile-browser", + "packages/core/user-profile/core-user-profile-browser" + ], + [ + "@kbn/core-user-profile-browser-internal", + "packages/core/user-profile/core-user-profile-browser-internal" + ], + [ + "@kbn/core-user-profile-browser-mocks", + "packages/core/user-profile/core-user-profile-browser-mocks" + ], + [ + "@kbn/core-user-profile-common", + "packages/core/user-profile/core-user-profile-common" + ], + [ + "@kbn/core-user-profile-server", + "packages/core/user-profile/core-user-profile-server" + ], + [ + "@kbn/core-user-profile-server-internal", + "packages/core/user-profile/core-user-profile-server-internal" + ], + [ + "@kbn/core-user-profile-server-mocks", + "packages/core/user-profile/core-user-profile-server-mocks" + ], + [ + "@kbn/core-user-settings-server", + "packages/core/user-settings/core-user-settings-server" + ], + [ + "@kbn/core-user-settings-server-internal", + "packages/core/user-settings/core-user-settings-server-internal" + ], + [ + "@kbn/core-user-settings-server-mocks", + "packages/core/user-settings/core-user-settings-server-mocks" + ], + [ + "@kbn/cross-cluster-replication-plugin", + "x-pack/platform/plugins/private/cross_cluster_replication" + ], + [ + "@kbn/crypto", + "packages/kbn-crypto" + ], + [ + "@kbn/crypto-browser", + "packages/kbn-crypto-browser" + ], + [ + "@kbn/custom-branding-plugin", + "x-pack/plugins/custom_branding" + ], + [ + "@kbn/custom-icons", + "src/platform/packages/shared/kbn-custom-icons" + ], + [ + "@kbn/custom-integrations", + "x-pack/solutions/observability/packages/kbn-custom-integrations" + ], + [ + "@kbn/custom-integrations-plugin", + "src/platform/plugins/shared/custom_integrations" + ], + [ + "@kbn/cypress-config", + "packages/kbn-cypress-config" + ], + [ + "@kbn/dashboard-enhanced-plugin", + "x-pack/platform/plugins/shared/dashboard_enhanced" + ], + [ + "@kbn/dashboard-plugin", + "src/platform/plugins/shared/dashboard" + ], + [ + "@kbn/data-forge", + "x-pack/platform/packages/shared/kbn-data-forge" + ], + [ + "@kbn/data-plugin", + "src/plugins/data" + ], + [ + "@kbn/data-quality-plugin", + "x-pack/platform/plugins/shared/data_quality" + ], + [ + "@kbn/data-search-plugin", + "test/plugin_functional/plugins/data_search" + ], + [ + "@kbn/data-service", + "packages/kbn-data-service" + ], + [ + "@kbn/data-stream-adapter", + "x-pack/solutions/security/packages/data-stream-adapter" + ], + [ + "@kbn/data-usage-plugin", + "x-pack/platform/plugins/private/data_usage" + ], + [ + "@kbn/data-view-editor-plugin", + "src/platform/plugins/shared/data_view_editor" + ], + [ + "@kbn/data-view-field-editor-example-plugin", + "examples/data_view_field_editor_example" + ], + [ + "@kbn/data-view-field-editor-plugin", + "src/platform/plugins/shared/data_view_field_editor" + ], + [ + "@kbn/data-view-management-plugin", + "src/platform/plugins/shared/data_view_management" + ], + [ + "@kbn/data-view-utils", + "src/platform/packages/shared/kbn-data-view-utils" + ], + [ + "@kbn/data-views-plugin", + "src/platform/plugins/shared/data_views" + ], + [ + "@kbn/data-visualizer-plugin", + "x-pack/platform/plugins/private/data_visualizer" + ], + [ + "@kbn/dataset-quality-plugin", + "x-pack/platform/plugins/shared/dataset_quality" + ], + [ + "@kbn/datemath", + "src/platform/packages/shared/kbn-datemath" + ], + [ + "@kbn/deeplinks-analytics", + "src/platform/packages/shared/deeplinks/analytics" + ], + [ + "@kbn/deeplinks-devtools", + "src/platform/packages/shared/deeplinks/devtools" + ], + [ + "@kbn/deeplinks-fleet", + "src/platform/packages/shared/deeplinks/fleet" + ], + [ + "@kbn/deeplinks-management", + "src/platform/packages/shared/deeplinks/management" + ], + [ + "@kbn/deeplinks-ml", + "src/platform/packages/shared/deeplinks/ml" + ], + [ + "@kbn/deeplinks-observability", + "src/platform/packages/shared/deeplinks/observability" + ], + [ + "@kbn/deeplinks-search", + "src/platform/packages/shared/deeplinks/search" + ], + [ + "@kbn/deeplinks-security", + "src/platform/packages/shared/deeplinks/security" + ], + [ + "@kbn/deeplinks-shared", + "packages/deeplinks/shared" + ], + [ + "@kbn/default-nav-analytics", + "src/platform/packages/private/default-nav/analytics" + ], + [ + "@kbn/default-nav-devtools", + "src/platform/packages/private/default-nav/devtools" + ], + [ + "@kbn/default-nav-management", + "src/platform/packages/private/default-nav/management" + ], + [ + "@kbn/default-nav-ml", + "src/platform/packages/private/default-nav/ml" + ], + [ + "@kbn/dependency-ownership", + "packages/kbn-dependency-ownership" + ], + [ + "@kbn/dependency-usage", + "packages/kbn-dependency-usage" + ], + [ + "@kbn/dev-cli-errors", + "packages/kbn-dev-cli-errors" + ], + [ + "@kbn/dev-cli-runner", + "packages/kbn-dev-cli-runner" + ], + [ + "@kbn/dev-proc-runner", + "packages/kbn-dev-proc-runner" + ], + [ + "@kbn/dev-tools-plugin", + "src/platform/plugins/shared/dev_tools" + ], + [ + "@kbn/dev-utils", + "packages/kbn-dev-utils" + ], + [ + "@kbn/developer-examples-plugin", + "examples/developer_examples" + ], + [ + "@kbn/discover-contextual-components", + "src/platform/packages/shared/kbn-discover-contextual-components" + ], + [ + "@kbn/discover-customization-examples-plugin", + "examples/discover_customization_examples" + ], + [ + "@kbn/discover-enhanced-plugin", + "x-pack/platform/plugins/private/discover_enhanced" + ], + [ + "@kbn/discover-plugin", + "src/platform/plugins/shared/discover" + ], + [ + "@kbn/discover-shared-plugin", + "src/platform/plugins/shared/discover_shared" + ], + [ + "@kbn/discover-utils", + "src/platform/packages/shared/kbn-discover-utils" + ], + [ + "@kbn/doc-links", + "src/platform/packages/shared/kbn-doc-links" + ], + [ + "@kbn/docs-utils", + "packages/kbn-docs-utils" + ], + [ + "@kbn/dom-drag-drop", + "packages/kbn-dom-drag-drop" + ], + [ + "@kbn/ebt-tools", + "packages/kbn-ebt-tools" + ], + [ + "@kbn/ecs-data-quality-dashboard", + "x-pack/solutions/security/packages/ecs_data_quality_dashboard" + ], + [ + "@kbn/ecs-data-quality-dashboard-plugin", + "x-pack/solutions/security/plugins/ecs_data_quality_dashboard" + ], + [ + "@kbn/elastic-agent-utils", + "src/platform/packages/shared/kbn-elastic-agent-utils" + ], + [ + "@kbn/elastic-assistant", + "x-pack/platform/packages/shared/kbn-elastic-assistant" + ], + [ + "@kbn/elastic-assistant-common", + "x-pack/platform/packages/shared/kbn-elastic-assistant-common" + ], + [ + "@kbn/elastic-assistant-plugin", + "x-pack/solutions/security/plugins/elastic_assistant" + ], + [ + "@kbn/elasticsearch-client-plugin", + "test/plugin_functional/plugins/elasticsearch_client_plugin" + ], + [ + "@kbn/elasticsearch-client-xpack-plugin", + "x-pack/test/plugin_api_integration/plugins/elasticsearch_client" + ], + [ + "@kbn/embeddable-enhanced-plugin", + "x-pack/platform/plugins/shared/embeddable_enhanced" + ], + [ + "@kbn/embeddable-examples-plugin", + "examples/embeddable_examples" + ], + [ + "@kbn/embeddable-plugin", + "src/platform/plugins/shared/embeddable" + ], + [ + "@kbn/embedded-lens-example-plugin", + "x-pack/examples/embedded_lens_example" + ], + [ + "@kbn/encrypted-saved-objects-plugin", + "x-pack/plugins/encrypted_saved_objects" + ], + [ + "@kbn/enterprise-search-plugin", + "x-pack/solutions/search/plugins/enterprise_search" + ], + [ + "@kbn/entities-data-access-plugin", + "x-pack/solutions/observability/plugins/observability_solution/entities_data_access" + ], + [ + "@kbn/entities-schema", + "x-pack/platform/packages/shared/kbn-entities-schema" + ], + [ + "@kbn/entity-manager-fixture-plugin", + "x-pack/test/api_integration/apis/entity_manager/fixture_plugin" + ], + [ + "@kbn/entityManager-app-plugin", + "x-pack/solutions/observability/plugins/observability_solution/entity_manager_app" + ], + [ + "@kbn/entityManager-plugin", + "x-pack/platform/plugins/shared/entity_manager" + ], + [ + "@kbn/error-boundary-example-plugin", + "examples/error_boundary" + ], + [ + "@kbn/es", + "packages/kbn-es" + ], + [ + "@kbn/es-archiver", + "packages/kbn-es-archiver" + ], + [ + "@kbn/es-errors", + "packages/kbn-es-errors" + ], + [ + "@kbn/es-query", + "src/platform/packages/shared/kbn-es-query" + ], + [ + "@kbn/es-types", + "packages/kbn-es-types" + ], + [ + "@kbn/es-ui-shared-plugin", + "src/platform/plugins/shared/es_ui_shared" + ], + [ + "@kbn/eslint-config", + "packages/kbn-eslint-config" + ], + [ + "@kbn/eslint-plugin-css", + "packages/kbn-eslint-plugin-css" + ], + [ + "@kbn/eslint-plugin-disable", + "packages/kbn-eslint-plugin-disable" + ], + [ + "@kbn/eslint-plugin-eslint", + "packages/kbn-eslint-plugin-eslint" + ], + [ + "@kbn/eslint-plugin-i18n", + "packages/kbn-eslint-plugin-i18n" + ], + [ + "@kbn/eslint-plugin-imports", + "packages/kbn-eslint-plugin-imports" + ], + [ + "@kbn/eslint-plugin-telemetry", + "packages/kbn-eslint-plugin-telemetry" + ], + [ + "@kbn/eso-model-version-example", + "examples/eso_model_version_example" + ], + [ + "@kbn/eso-plugin", + "x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin" + ], + [ + "@kbn/esql", + "src/platform/plugins/shared/esql" + ], + [ + "@kbn/esql-ast", + "src/platform/packages/shared/kbn-esql-ast" + ], + [ + "@kbn/esql-ast-inspector-plugin", + "examples/esql_ast_inspector" + ], + [ + "@kbn/esql-datagrid", + "src/platform/plugins/shared/esql_datagrid" + ], + [ + "@kbn/esql-editor", + "src/platform/packages/private/kbn-esql-editor" + ], + [ + "@kbn/esql-utils", + "src/platform/packages/shared/kbn-esql-utils" + ], + [ + "@kbn/esql-validation-autocomplete", + "src/platform/packages/shared/kbn-esql-validation-autocomplete" + ], + [ + "@kbn/esql-validation-example-plugin", + "examples/esql_validation_example" + ], + [ + "@kbn/eui-provider-dev-warning", + "test/plugin_functional/plugins/eui_provider_dev_warning" + ], + [ + "@kbn/event-annotation-common", + "packages/kbn-event-annotation-common" + ], + [ + "@kbn/event-annotation-components", + "packages/kbn-event-annotation-components" + ], + [ + "@kbn/event-annotation-listing-plugin", + "src/plugins/event_annotation_listing" + ], + [ + "@kbn/event-annotation-plugin", + "src/plugins/event_annotation" + ], + [ + "@kbn/event-log-fixture-plugin", + "x-pack/test/plugin_api_integration/plugins/event_log" + ], + [ + "@kbn/event-log-plugin", + "x-pack/platform/plugins/shared/event_log" + ], + [ + "@kbn/expandable-flyout", + "x-pack/solutions/security/packages/expandable-flyout" + ], + [ + "@kbn/expect", + "packages/kbn-expect" + ], + [ + "@kbn/exploratory-view-example-plugin", + "x-pack/examples/exploratory_view_example" + ], + [ + "@kbn/exploratory-view-plugin", + "x-pack/solutions/observability/plugins/exploratory_view" + ], + [ + "@kbn/expression-error-plugin", + "src/platform/plugins/shared/expression_error" + ], + [ + "@kbn/expression-gauge-plugin", + "src/plugins/chart_expressions/expression_gauge" + ], + [ + "@kbn/expression-heatmap-plugin", + "src/plugins/chart_expressions/expression_heatmap" + ], + [ + "@kbn/expression-image-plugin", + "src/platform/plugins/shared/expression_image" + ], + [ + "@kbn/expression-legacy-metric-vis-plugin", + "src/plugins/chart_expressions/expression_legacy_metric" + ], + [ + "@kbn/expression-metric-plugin", + "src/platform/plugins/shared/expression_metric" + ], + [ + "@kbn/expression-metric-vis-plugin", + "src/plugins/chart_expressions/expression_metric" + ], + [ + "@kbn/expression-partition-vis-plugin", + "src/plugins/chart_expressions/expression_partition_vis" + ], + [ + "@kbn/expression-repeat-image-plugin", + "src/platform/plugins/shared/expression_repeat_image" + ], + [ + "@kbn/expression-reveal-image-plugin", + "src/platform/plugins/shared/expression_reveal_image" + ], + [ + "@kbn/expression-shape-plugin", + "src/platform/plugins/shared/expression_shape" + ], + [ + "@kbn/expression-tagcloud-plugin", + "src/plugins/chart_expressions/expression_tagcloud" + ], + [ + "@kbn/expression-xy-plugin", + "src/plugins/chart_expressions/expression_xy" + ], + [ + "@kbn/expressions-explorer-plugin", + "examples/expressions_explorer" + ], + [ + "@kbn/expressions-plugin", + "src/plugins/expressions" + ], + [ + "@kbn/failed-test-reporter-cli", + "packages/kbn-failed-test-reporter-cli" + ], + [ + "@kbn/feature-controls-examples-plugin", + "examples/feature_control_examples" + ], + [ + "@kbn/feature-flags-example-plugin", + "examples/feature_flags_example" + ], + [ + "@kbn/feature-usage-test-plugin", + "x-pack/test/plugin_api_integration/plugins/feature_usage_test" + ], + [ + "@kbn/features-plugin", + "x-pack/plugins/features" + ], + [ + "@kbn/features-provider-plugin", + "x-pack/test/security_api_integration/plugins/features_provider" + ], + [ + "@kbn/fec-alerts-test-plugin", + "x-pack/test/functional_execution_context/plugins/alerts" + ], + [ + "@kbn/field-formats-example-plugin", + "examples/field_formats_example" + ], + [ + "@kbn/field-formats-plugin", + "src/platform/plugins/shared/field_formats" + ], + [ + "@kbn/field-types", + "src/platform/packages/shared/kbn-field-types" + ], + [ + "@kbn/field-utils", + "src/platform/packages/shared/kbn-field-utils" + ], + [ + "@kbn/fields-metadata-plugin", + "x-pack/platform/plugins/shared/fields_metadata" + ], + [ + "@kbn/file-upload-plugin", + "x-pack/platform/plugins/private/file_upload" + ], + [ + "@kbn/files-example-plugin", + "examples/files_example" + ], + [ + "@kbn/files-management-plugin", + "src/plugins/files_management" + ], + [ + "@kbn/files-plugin", + "src/plugins/files" + ], + [ + "@kbn/find-used-node-modules", + "packages/kbn-find-used-node-modules" + ], + [ + "@kbn/fleet-plugin", + "x-pack/platform/plugins/shared/fleet" + ], + [ + "@kbn/flot-charts", + "src/platform/packages/shared/kbn-flot-charts" + ], + [ + "@kbn/foo-plugin", + "x-pack/test/ui_capabilities/common/plugins/foo_plugin" + ], + [ + "@kbn/formatters", + "packages/kbn-formatters" + ], + [ + "@kbn/ftr-apis-plugin", + "src/plugins/ftr_apis" + ], + [ + "@kbn/ftr-common-functional-services", + "packages/kbn-ftr-common-functional-services" + ], + [ + "@kbn/ftr-common-functional-ui-services", + "packages/kbn-ftr-common-functional-ui-services" + ], + [ + "@kbn/ftr-screenshot-filename", + "packages/kbn-ftr-screenshot-filename" + ], + [ + "@kbn/functional-with-es-ssl-cases-test-plugin", + "x-pack/test/functional_with_es_ssl/plugins/cases" + ], + [ + "@kbn/gen-ai-functional-testing", + "packages/kbn-gen-ai-functional-testing" + ], + [ + "@kbn/gen-ai-streaming-response-example-plugin", + "x-pack/examples/gen_ai_streaming_response_example" + ], + [ + "@kbn/generate", + "packages/kbn-generate" + ], + [ + "@kbn/generate-console-definitions", + "packages/kbn-generate-console-definitions" + ], + [ + "@kbn/generate-csv", + "packages/kbn-generate-csv" + ], + [ + "@kbn/get-repo-files", + "packages/kbn-get-repo-files" + ], + [ + "@kbn/global-search-bar-plugin", + "x-pack/plugins/global_search_bar" + ], + [ + "@kbn/global-search-plugin", + "x-pack/plugins/global_search" + ], + [ + "@kbn/global-search-providers-plugin", + "x-pack/plugins/global_search_providers" + ], + [ + "@kbn/global-search-test-plugin", + "x-pack/test/plugin_functional/plugins/global_search_test" + ], + [ + "@kbn/graph-plugin", + "x-pack/plugins/graph" + ], + [ + "@kbn/grid-example-plugin", + "examples/grid_example" + ], + [ + "@kbn/grid-layout", + "packages/kbn-grid-layout" + ], + [ + "@kbn/grokdebugger-plugin", + "x-pack/platform/plugins/private/grokdebugger" + ], + [ + "@kbn/grouping", + "src/platform/packages/shared/kbn-grouping" + ], + [ + "@kbn/guided-onboarding", + "packages/kbn-guided-onboarding" + ], + [ + "@kbn/guided-onboarding-example-plugin", + "examples/guided_onboarding_example" + ], + [ + "@kbn/guided-onboarding-plugin", + "src/plugins/guided_onboarding" + ], + [ + "@kbn/handlebars", + "packages/kbn-handlebars" + ], + [ + "@kbn/hapi-mocks", + "packages/kbn-hapi-mocks" + ], + [ + "@kbn/hardening-plugin", + "test/plugin_functional/plugins/hardening" + ], + [ + "@kbn/health-gateway-server", + "packages/kbn-health-gateway-server" + ], + [ + "@kbn/hello-world-plugin", + "examples/hello_world" + ], + [ + "@kbn/home-plugin", + "src/plugins/home" + ], + [ + "@kbn/home-sample-data-card", + "packages/home/sample_data_card" + ], + [ + "@kbn/home-sample-data-tab", + "packages/home/sample_data_tab" + ], + [ + "@kbn/home-sample-data-types", + "packages/home/sample_data_types" + ], + [ + "@kbn/i18n", + "packages/kbn-i18n" + ], + [ + "@kbn/i18n-react", + "packages/kbn-i18n-react" + ], + [ + "@kbn/iframe-embedded-plugin", + "x-pack/test/functional_embedded/plugins/iframe_embedded" + ], + [ + "@kbn/image-embeddable-plugin", + "src/plugins/image_embeddable" + ], + [ + "@kbn/import-locator", + "packages/kbn-import-locator" + ], + [ + "@kbn/import-resolver", + "packages/kbn-import-resolver" + ], + [ + "@kbn/index-adapter", + "x-pack/solutions/security/packages/index-adapter" + ], + [ + "@kbn/index-lifecycle-management-common-shared", + "x-pack/platform/packages/shared/index-lifecycle-management/index_lifecycle_management_common_shared" + ], + [ + "@kbn/index-lifecycle-management-plugin", + "x-pack/platform/plugins/private/index_lifecycle_management" + ], + [ + "@kbn/index-management-plugin", + "x-pack/platform/plugins/shared/index_management" + ], + [ + "@kbn/index-management-shared-types", + "x-pack/platform/packages/shared/index-management/index_management_shared_types" + ], + [ + "@kbn/index-patterns-test-plugin", + "test/plugin_functional/plugins/index_patterns" + ], + [ + "@kbn/inference_integration_flyout", + "x-pack/platform/packages/private/ml/inference_integration_flyout" + ], + [ + "@kbn/inference-common", + "x-pack/platform/packages/shared/ai-infra/inference-common" + ], + [ + "@kbn/inference-endpoint-ui-common", + "x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common" + ], + [ + "@kbn/inference-plugin", + "x-pack/platform/plugins/shared/inference" + ], + [ + "@kbn/infra-forge", + "x-pack/platform/packages/private/kbn-infra-forge" + ], + [ + "@kbn/infra-plugin", + "x-pack/solutions/observability/plugins/infra" + ], + [ + "@kbn/ingest-pipelines-plugin", + "x-pack/platform/plugins/shared/ingest_pipelines" + ], + [ + "@kbn/input-control-vis-plugin", + "src/platform/plugins/private/input_control_vis" + ], + [ + "@kbn/inspector-plugin", + "src/platform/plugins/shared/inspector" + ], + [ + "@kbn/integration-assistant-plugin", + "x-pack/platform/plugins/shared/integration_assistant" + ], + [ + "@kbn/interactive-setup-plugin", + "src/plugins/interactive_setup" + ], + [ + "@kbn/interactive-setup-test-endpoints-plugin", + "test/interactive_setup_api_integration/plugins/test_endpoints" + ], + [ + "@kbn/interpreter", + "packages/kbn-interpreter" + ], + [ + "@kbn/inventory-e2e", + "x-pack/solutions/observability/plugins/inventory/e2e" + ], + [ + "@kbn/inventory-plugin", + "x-pack/solutions/observability/plugins/inventory" + ], + [ + "@kbn/investigate-app-plugin", + "x-pack/solutions/observability/plugins/investigate_app" + ], + [ + "@kbn/investigate-plugin", + "x-pack/solutions/observability/plugins/investigate" + ], + [ + "@kbn/investigation-shared", + "x-pack/solutions/observability/packages/kbn-investigation-shared" + ], + [ + "@kbn/io-ts-utils", + "src/platform/packages/shared/kbn-io-ts-utils" + ], + [ + "@kbn/ipynb", + "x-pack/solutions/search/packages/kbn-ipynb" + ], + [ + "@kbn/item-buffer", + "packages/kbn-item-buffer" + ], + [ + "@kbn/jest-serializers", + "packages/kbn-jest-serializers" + ], + [ + "@kbn/journeys", + "packages/kbn-journeys" + ], + [ + "@kbn/json-ast", + "packages/kbn-json-ast" + ], + [ + "@kbn/json-schemas", + "x-pack/platform/packages/private/ml/json_schemas" + ], + [ + "@kbn/kbn-health-gateway-status-plugin", + "test/health_gateway/plugins/status" + ], + [ + "@kbn/kbn-sample-panel-action-plugin", + "test/plugin_functional/plugins/kbn_sample_panel_action" + ], + [ + "@kbn/kbn-top-nav-plugin", + "test/plugin_functional/plugins/kbn_top_nav" + ], + [ + "@kbn/kbn-tp-custom-visualizations-plugin", + "test/plugin_functional/plugins/kbn_tp_custom_visualizations" + ], + [ + "@kbn/kbn-tp-run-pipeline-plugin", + "test/interpreter_functional/plugins/kbn_tp_run_pipeline" + ], + [ + "@kbn/kibana-cors-test-plugin", + "x-pack/test/functional_cors/plugins/kibana_cors_test" + ], + [ + "@kbn/kibana-manifest-schema", + "packages/kbn-kibana-manifest-schema" + ], + [ + "@kbn/kibana-overview-plugin", + "src/plugins/kibana_overview" + ], + [ + "@kbn/kibana-react-plugin", + "src/plugins/kibana_react" + ], + [ + "@kbn/kibana-usage-collection-plugin", + "src/plugins/kibana_usage_collection" + ], + [ + "@kbn/kibana-utils-plugin", + "src/plugins/kibana_utils" + ], + [ + "@kbn/kubernetes-security-plugin", + "x-pack/solutions/security/plugins/kubernetes_security" + ], + [ + "@kbn/langchain", + "x-pack/platform/packages/shared/kbn-langchain" + ], + [ + "@kbn/language-documentation", + "src/platform/packages/private/kbn-language-documentation" + ], + [ + "@kbn/lens-config-builder-example-plugin", + "x-pack/examples/lens_config_builder_example" + ], + [ + "@kbn/lens-embeddable-utils", + "src/platform/packages/shared/kbn-lens-embeddable-utils" + ], + [ + "@kbn/lens-formula-docs", + "packages/kbn-lens-formula-docs" + ], + [ + "@kbn/lens-inline-editing-example-plugin", + "x-pack/examples/lens_embeddable_inline_editing_example" + ], + [ + "@kbn/lens-plugin", + "x-pack/plugins/lens" + ], + [ + "@kbn/license-api-guard-plugin", + "x-pack/platform/plugins/private/license_api_guard" + ], + [ + "@kbn/license-management-plugin", + "x-pack/platform/plugins/shared/license_management" + ], + [ + "@kbn/licensing-plugin", + "x-pack/plugins/licensing" + ], + [ + "@kbn/links-plugin", + "src/platform/plugins/private/links" + ], + [ + "@kbn/lint-packages-cli", + "packages/kbn-lint-packages-cli" + ], + [ + "@kbn/lint-ts-projects-cli", + "packages/kbn-lint-ts-projects-cli" + ], + [ + "@kbn/lists-plugin", + "x-pack/solutions/security/plugins/lists" + ], + [ + "@kbn/llm-tasks-plugin", + "x-pack/platform/plugins/shared/ai_infra/llm_tasks" + ], + [ + "@kbn/locator-examples-plugin", + "examples/locator_examples" + ], + [ + "@kbn/locator-explorer-plugin", + "examples/locator_explorer" + ], + [ + "@kbn/logging", + "packages/kbn-logging" + ], + [ + "@kbn/logging-mocks", + "packages/kbn-logging-mocks" + ], + [ + "@kbn/logs-data-access-plugin", + "x-pack/platform/plugins/shared/logs_data_access" + ], + [ + "@kbn/logs-explorer-plugin", + "x-pack/solutions/observability/plugins/logs_explorer" + ], + [ + "@kbn/logs-shared-plugin", + "x-pack/platform/plugins/shared/logs_shared" + ], + [ + "@kbn/logstash-plugin", + "x-pack/platform/plugins/private/logstash" + ], + [ + "@kbn/managed-content-badge", + "packages/kbn-managed-content-badge" + ], + [ + "@kbn/managed-vscode-config", + "packages/kbn-managed-vscode-config" + ], + [ + "@kbn/managed-vscode-config-cli", + "packages/kbn-managed-vscode-config-cli" + ], + [ + "@kbn/management-cards-navigation", + "src/platform/packages/shared/kbn-management/cards_navigation" + ], + [ + "@kbn/management-plugin", + "src/platform/plugins/shared/management" + ], + [ + "@kbn/management-settings-application", + "src/platform/packages/private/kbn-management/settings/application" + ], + [ + "@kbn/management-settings-components-field-category", + "src/platform/packages/private/kbn-management/settings/components/field_category" + ], + [ + "@kbn/management-settings-components-field-input", + "src/platform/packages/shared/kbn-management/settings/components/field_input" + ], + [ + "@kbn/management-settings-components-field-row", + "src/platform/packages/shared/kbn-management/settings/components/field_row" + ], + [ + "@kbn/management-settings-components-form", + "src/platform/packages/private/kbn-management/settings/components/form" + ], + [ + "@kbn/management-settings-field-definition", + "src/platform/packages/shared/kbn-management/settings/field_definition" + ], + [ + "@kbn/management-settings-ids", + "packages/kbn-management/settings/setting_ids" + ], + [ + "@kbn/management-settings-section-registry", + "packages/kbn-management/settings/section_registry" + ], + [ + "@kbn/management-settings-types", + "src/platform/packages/shared/kbn-management/settings/types" + ], + [ + "@kbn/management-settings-utilities", + "src/platform/packages/shared/kbn-management/settings/utilities" + ], + [ + "@kbn/management-storybook-config", + "packages/kbn-management/storybook/config" + ], + [ + "@kbn/management-test-plugin", + "test/plugin_functional/plugins/management_test_plugin" + ], + [ + "@kbn/manifest", + "packages/kbn-manifest" + ], + [ + "@kbn/mapbox-gl", + "src/platform/packages/private/kbn-mapbox-gl" + ], + [ + "@kbn/maps-custom-raster-source-plugin", + "x-pack/examples/third_party_maps_source_example" + ], + [ + "@kbn/maps-ems-plugin", + "src/platform/plugins/private/maps_ems" + ], + [ + "@kbn/maps-plugin", + "x-pack/platform/plugins/shared/maps" + ], + [ + "@kbn/maps-vector-tile-utils", + "x-pack/platform/packages/private/maps/vector_tile_utils" + ], + [ + "@kbn/metrics-data-access-plugin", + "x-pack/solutions/observability/plugins/metrics_data_access" + ], + [ + "@kbn/ml-agg-utils", + "x-pack/platform/packages/private/ml/agg_utils" + ], + [ + "@kbn/ml-anomaly-utils", + "x-pack/platform/packages/shared/ml/anomaly_utils" + ], + [ + "@kbn/ml-cancellable-search", + "x-pack/platform/packages/private/ml/cancellable_search" + ], + [ + "@kbn/ml-category-validator", + "x-pack/platform/packages/private/ml/category_validator" + ], + [ + "@kbn/ml-chi2test", + "x-pack/platform/packages/shared/ml/chi2test" + ], + [ + "@kbn/ml-creation-wizard-utils", + "x-pack/platform/packages/private/ml/creation_wizard_utils" + ], + [ + "@kbn/ml-data-frame-analytics-utils", + "x-pack/platform/packages/private/ml/data_frame_analytics_utils" + ], + [ + "@kbn/ml-data-grid", + "x-pack/platform/packages/private/ml/data_grid" + ], + [ + "@kbn/ml-data-view-utils", + "x-pack/platform/packages/private/ml/data_view_utils" + ], + [ + "@kbn/ml-date-picker", + "x-pack/platform/packages/private/ml/date_picker" + ], + [ + "@kbn/ml-date-utils", + "x-pack/platform/packages/private/ml/date_utils" + ], + [ + "@kbn/ml-error-utils", + "x-pack/platform/packages/shared/ml/error_utils" + ], + [ + "@kbn/ml-field-stats-flyout", + "x-pack/platform/packages/private/ml/field_stats_flyout" + ], + [ + "@kbn/ml-in-memory-table", + "x-pack/platform/packages/private/ml/in_memory_table" + ], + [ + "@kbn/ml-is-defined", + "x-pack/platform/packages/private/ml/is_defined" + ], + [ + "@kbn/ml-is-populated-object", + "x-pack/platform/packages/private/ml/is_populated_object" + ], + [ + "@kbn/ml-kibana-theme", + "x-pack/platform/packages/private/ml/kibana_theme" + ], + [ + "@kbn/ml-local-storage", + "x-pack/platform/packages/private/ml/local_storage" + ], + [ + "@kbn/ml-nested-property", + "x-pack/platform/packages/private/ml/nested_property" + ], + [ + "@kbn/ml-number-utils", + "x-pack/platform/packages/private/ml/number_utils" + ], + [ + "@kbn/ml-parse-interval", + "x-pack/platform/packages/private/ml/parse_interval" + ], + [ + "@kbn/ml-plugin", + "x-pack/platform/plugins/shared/ml" + ], + [ + "@kbn/ml-query-utils", + "x-pack/platform/packages/private/ml/query_utils" + ], + [ + "@kbn/ml-random-sampler-utils", + "x-pack/platform/packages/shared/ml/random_sampler_utils" + ], + [ + "@kbn/ml-response-stream", + "x-pack/platform/packages/shared/ml/response_stream" + ], + [ + "@kbn/ml-route-utils", + "x-pack/platform/packages/private/ml/route_utils" + ], + [ + "@kbn/ml-runtime-field-utils", + "x-pack/platform/packages/shared/ml/runtime_field_utils" + ], + [ + "@kbn/ml-string-hash", + "x-pack/platform/packages/private/ml/string_hash" + ], + [ + "@kbn/ml-time-buckets", + "x-pack/platform/packages/private/ml/time_buckets" + ], + [ + "@kbn/ml-trained-models-utils", + "x-pack/platform/packages/shared/ml/trained_models_utils" + ], + [ + "@kbn/ml-ui-actions", + "x-pack/platform/packages/private/ml/ui_actions" + ], + [ + "@kbn/ml-url-state", + "x-pack/platform/packages/private/ml/url_state" + ], + [ + "@kbn/ml-validators", + "x-pack/platform/packages/private/ml/validators" + ], + [ + "@kbn/mock-idp-plugin", + "packages/kbn-mock-idp-plugin" + ], + [ + "@kbn/mock-idp-utils", + "packages/kbn-mock-idp-utils" + ], + [ + "@kbn/monaco", + "packages/kbn-monaco" + ], + [ + "@kbn/monitoring-collection-plugin", + "x-pack/platform/plugins/private/monitoring_collection" + ], + [ + "@kbn/monitoring-plugin", + "x-pack/platform/plugins/private/monitoring" + ], + [ + "@kbn/navigation-plugin", + "src/plugins/navigation" + ], + [ + "@kbn/newsfeed-plugin", + "src/plugins/newsfeed" + ], + [ + "@kbn/newsfeed-test-plugin", + "test/common/plugins/newsfeed" + ], + [ + "@kbn/no-data-page-plugin", + "src/plugins/no_data_page" + ], + [ + "@kbn/notifications-plugin", + "x-pack/plugins/notifications" + ], + [ + "@kbn/object-versioning", + "packages/kbn-object-versioning" + ], + [ + "@kbn/object-versioning-utils", + "packages/kbn-object-versioning-utils" + ], + [ + "@kbn/observability-ai-assistant-app-plugin", + "x-pack/solutions/observability/plugins/observability_ai_assistant_app" + ], + [ + "@kbn/observability-ai-assistant-management-plugin", + "x-pack/solutions/observability/plugins/observability_ai_assistant_management" + ], + [ + "@kbn/observability-ai-assistant-plugin", + "x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant" + ], + [ + "@kbn/observability-ai-common", + "x-pack/solutions/observability/packages/observability_ai/observability_ai_common" + ], + [ + "@kbn/observability-ai-server", + "x-pack/solutions/observability/packages/observability_ai/observability_ai_server" + ], + [ + "@kbn/observability-alert-details", + "x-pack/solutions/observability/packages/alert_details" + ], + [ + "@kbn/observability-alerting-rule-utils", + "x-pack/platform/packages/shared/observability/alerting_rule_utils" + ], + [ + "@kbn/observability-alerting-test-data", + "x-pack/solutions/observability/packages/alerting_test_data" + ], + [ + "@kbn/observability-fixtures-plugin", + "x-pack/test/cases_api_integration/common/plugins/observability" + ], + [ + "@kbn/observability-get-padded-alert-time-range-util", + "x-pack/solutions/observability/packages/get_padded_alert_time_range_util" + ], + [ + "@kbn/observability-logs-explorer-plugin", + "x-pack/solutions/observability/plugins/observability_logs_explorer" + ], + [ + "@kbn/observability-logs-overview", + "x-pack/platform/packages/shared/observability/logs_overview" + ], + [ + "@kbn/observability-onboarding-e2e", + "x-pack/solutions/observability/plugins/observability_onboarding/e2e" + ], + [ + "@kbn/observability-onboarding-plugin", + "x-pack/solutions/observability/plugins/observability_onboarding" + ], + [ + "@kbn/observability-plugin", + "x-pack/solutions/observability/plugins/observability" + ], + [ + "@kbn/observability-shared-plugin", + "x-pack/solutions/observability/plugins/observability_shared" + ], + [ + "@kbn/observability-synthetics-test-data", + "x-pack/solutions/observability/packages/synthetics_test_data" + ], + [ + "@kbn/observability-utils-browser", + "x-pack/solutions/observability/packages/utils_browser" + ], + [ + "@kbn/observability-utils-common", + "x-pack/solutions/observability/packages/utils_common" + ], + [ + "@kbn/observability-utils-server", + "x-pack/solutions/observability/packages/utils_server" + ], + [ + "@kbn/oidc-provider-plugin", + "x-pack/test/security_api_integration/plugins/oidc_provider" + ], + [ + "@kbn/open-telemetry-instrumented-plugin", + "test/common/plugins/otel_metrics" + ], + [ + "@kbn/openapi-bundler", + "packages/kbn-openapi-bundler" + ], + [ + "@kbn/openapi-common", + "src/platform/packages/shared/kbn-openapi-common" + ], + [ + "@kbn/openapi-generator", + "packages/kbn-openapi-generator" + ], + [ + "@kbn/optimizer", + "packages/kbn-optimizer" + ], + [ + "@kbn/optimizer-webpack-helpers", + "packages/kbn-optimizer-webpack-helpers" + ], + [ + "@kbn/osquery-io-ts-types", + "src/platform/packages/shared/kbn-osquery-io-ts-types" + ], + [ + "@kbn/osquery-plugin", + "x-pack/platform/plugins/shared/osquery" + ], + [ + "@kbn/paertial-results-example-plugin", + "examples/partial_results_example" + ], + [ + "@kbn/painless-lab-plugin", + "x-pack/platform/plugins/private/painless_lab" + ], + [ + "@kbn/palettes", + "packages/kbn-palettes" + ], + [ + "@kbn/panel-loader", + "src/platform/packages/private/kbn-panel-loader" + ], + [ + "@kbn/peggy", + "packages/kbn-peggy" + ], + [ + "@kbn/peggy-loader", + "packages/kbn-peggy-loader" + ], + [ + "@kbn/performance-testing-dataset-extractor", + "packages/kbn-performance-testing-dataset-extractor" + ], + [ + "@kbn/picomatcher", + "packages/kbn-picomatcher" + ], + [ + "@kbn/plugin-check", + "packages/kbn-plugin-check" + ], + [ + "@kbn/plugin-generator", + "packages/kbn-plugin-generator" + ], + [ + "@kbn/plugin-helpers", + "packages/kbn-plugin-helpers" + ], + [ + "@kbn/portable-dashboards-example", + "examples/portable_dashboards_example" + ], + [ + "@kbn/preboot-example-plugin", + "examples/preboot_example" + ], + [ + "@kbn/presentation-containers", + "src/platform/packages/shared/presentation/presentation_containers" + ], + [ + "@kbn/presentation-panel-plugin", + "src/platform/plugins/private/presentation_panel" + ], + [ + "@kbn/presentation-publishing", + "src/platform/packages/shared/presentation/presentation_publishing" + ], + [ + "@kbn/presentation-util-plugin", + "src/platform/plugins/shared/presentation_util" + ], + [ + "@kbn/product-doc-artifact-builder", + "x-pack/packages/ai-infra/product-doc-artifact-builder" + ], + [ + "@kbn/product-doc-base-plugin", + "x-pack/platform/plugins/shared/ai_infra/product_doc_base" + ], + [ + "@kbn/product-doc-common", + "x-pack/platform/packages/shared/ai-infra/product-doc-common" + ], + [ + "@kbn/profiling-data-access-plugin", + "x-pack/solutions/observability/plugins/profiling_data_access" + ], + [ + "@kbn/profiling-plugin", + "x-pack/solutions/observability/plugins/profiling" + ], + [ + "@kbn/profiling-utils", + "src/platform/packages/shared/kbn-profiling-utils" + ], + [ + "@kbn/random-sampling", + "x-pack/packages/kbn-random-sampling" + ], + [ + "@kbn/react-field", + "src/platform/packages/shared/kbn-react-field" + ], + [ + "@kbn/react-hooks", + "src/platform/packages/shared/kbn-react-hooks" + ], + [ + "@kbn/react-kibana-context-common", + "packages/react/kibana_context/common" + ], + [ + "@kbn/react-kibana-context-render", + "packages/react/kibana_context/render" + ], + [ + "@kbn/react-kibana-context-root", + "packages/react/kibana_context/root" + ], + [ + "@kbn/react-kibana-context-styled", + "packages/react/kibana_context/styled" + ], + [ + "@kbn/react-kibana-context-theme", + "packages/react/kibana_context/theme" + ], + [ + "@kbn/react-kibana-mount", + "packages/react/kibana_mount" + ], + [ + "@kbn/react-mute-legacy-root-warning", + "packages/kbn-react-mute-legacy-root-warning" + ], + [ + "@kbn/recently-accessed", + "packages/kbn-recently-accessed" + ], + [ + "@kbn/relocate", + "packages/kbn-relocate" + ], + [ + "@kbn/remote-clusters-plugin", + "x-pack/platform/plugins/private/remote_clusters" + ], + [ + "@kbn/rendering-plugin", + "test/plugin_functional/plugins/rendering_plugin" + ], + [ + "@kbn/repo-file-maps", + "packages/kbn-repo-file-maps" + ], + [ + "@kbn/repo-info", + "packages/kbn-repo-info" + ], + [ + "@kbn/repo-linter", + "packages/kbn-repo-linter" + ], + [ + "@kbn/repo-packages", + "packages/kbn-repo-packages" + ], + [ + "@kbn/repo-path", + "packages/kbn-repo-path" + ], + [ + "@kbn/repo-source-classifier", + "packages/kbn-repo-source-classifier" + ], + [ + "@kbn/repo-source-classifier-cli", + "packages/kbn-repo-source-classifier-cli" + ], + [ + "@kbn/reporting-common", + "packages/kbn-reporting/common" + ], + [ + "@kbn/reporting-csv-share-panel", + "packages/kbn-reporting/get_csv_panel_actions" + ], + [ + "@kbn/reporting-export-types-csv", + "packages/kbn-reporting/export_types/csv" + ], + [ + "@kbn/reporting-export-types-csv-common", + "packages/kbn-reporting/export_types/csv_common" + ], + [ + "@kbn/reporting-export-types-pdf", + "packages/kbn-reporting/export_types/pdf" + ], + [ + "@kbn/reporting-export-types-pdf-common", + "packages/kbn-reporting/export_types/pdf_common" + ], + [ + "@kbn/reporting-export-types-png", + "packages/kbn-reporting/export_types/png" + ], + [ + "@kbn/reporting-export-types-png-common", + "packages/kbn-reporting/export_types/png_common" + ], + [ + "@kbn/reporting-mocks-server", + "packages/kbn-reporting/mocks_server" + ], + [ + "@kbn/reporting-plugin", + "x-pack/plugins/reporting" + ], + [ + "@kbn/reporting-public", + "packages/kbn-reporting/public" + ], + [ + "@kbn/reporting-server", + "packages/kbn-reporting/server" + ], + [ + "@kbn/resizable-layout", + "src/platform/packages/shared/kbn-resizable-layout" + ], + [ + "@kbn/resizable-layout-examples-plugin", + "examples/resizable_layout_examples" + ], + [ + "@kbn/resolver-test-plugin", + "x-pack/test/plugin_functional/plugins/resolver_test" + ], + [ + "@kbn/response-ops-feature-flag-service", + "packages/response-ops/feature_flag_service" + ], + [ + "@kbn/response-ops-rule-form", + "packages/response-ops/rule_form" + ], + [ + "@kbn/response-ops-rule-params", + "src/platform/packages/private/response-ops/rule_params" + ], + [ + "@kbn/response-stream-plugin", + "examples/response_stream" + ], + [ + "@kbn/rison", + "packages/kbn-rison" + ], + [ + "@kbn/rollup", + "x-pack/platform/packages/private/rollup" + ], + [ + "@kbn/rollup-plugin", + "x-pack/platform/plugins/private/rollup" + ], + [ + "@kbn/router-to-openapispec", + "packages/kbn-router-to-openapispec" + ], + [ + "@kbn/router-utils", + "src/platform/packages/shared/kbn-router-utils" + ], + [ + "@kbn/routing-example-plugin", + "examples/routing_example" + ], + [ + "@kbn/rrule", + "src/platform/packages/shared/kbn-rrule" + ], + [ + "@kbn/rule-data-utils", + "src/platform/packages/shared/kbn-rule-data-utils" + ], + [ + "@kbn/rule-registry-plugin", + "x-pack/platform/plugins/shared/rule_registry" + ], + [ + "@kbn/runtime-fields-plugin", + "x-pack/platform/plugins/private/runtime_fields" + ], + [ + "@kbn/safer-lodash-set", + "packages/kbn-safer-lodash-set" + ], + [ + "@kbn/saml-provider-plugin", + "x-pack/test/security_api_integration/plugins/saml_provider" + ], + [ + "@kbn/sample-task-plugin", + "x-pack/test/plugin_api_integration/plugins/sample_task_plugin" + ], + [ + "@kbn/sample-task-plugin-update-by-query", + "x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget" + ], + [ + "@kbn/saved-object-export-transforms-plugin", + "test/plugin_functional/plugins/saved_object_export_transforms" + ], + [ + "@kbn/saved-object-import-warnings-plugin", + "test/plugin_functional/plugins/saved_object_import_warnings" + ], + [ + "@kbn/saved-object-test-plugin", + "x-pack/test/saved_object_api_integration/common/plugins/saved_object_test_plugin" + ], + [ + "@kbn/saved-objects-finder-plugin", + "src/platform/plugins/shared/saved_objects_finder" + ], + [ + "@kbn/saved-objects-hidden-from-http-apis-type-plugin", + "test/plugin_functional/plugins/saved_objects_hidden_from_http_apis_type" + ], + [ + "@kbn/saved-objects-hidden-type-plugin", + "test/plugin_functional/plugins/saved_objects_hidden_type" + ], + [ + "@kbn/saved-objects-management-plugin", + "src/plugins/saved_objects_management" + ], + [ + "@kbn/saved-objects-plugin", + "src/plugins/saved_objects" + ], + [ + "@kbn/saved-objects-settings", + "packages/kbn-saved-objects-settings" + ], + [ + "@kbn/saved-objects-tagging-oss-plugin", + "src/plugins/saved_objects_tagging_oss" + ], + [ + "@kbn/saved-objects-tagging-plugin", + "x-pack/plugins/saved_objects_tagging" + ], + [ + "@kbn/saved-search-component", + "packages/kbn-saved-search-component" + ], + [ + "@kbn/saved-search-plugin", + "src/platform/plugins/shared/saved_search" + ], + [ + "@kbn/scout", + "packages/kbn-scout" + ], + [ + "@kbn/scout-info", + "packages/kbn-scout-info" + ], + [ + "@kbn/scout-reporting", + "packages/kbn-scout-reporting" + ], + [ + "@kbn/screenshot-mode-example-plugin", + "examples/screenshot_mode_example" + ], + [ + "@kbn/screenshot-mode-plugin", + "src/plugins/screenshot_mode" + ], + [ + "@kbn/screenshotting-example-plugin", + "x-pack/examples/screenshotting_example" + ], + [ + "@kbn/screenshotting-plugin", + "x-pack/platform/plugins/shared/screenshotting" + ], + [ + "@kbn/screenshotting-server", + "packages/kbn-screenshotting-server" + ], + [ + "@kbn/search-api-keys-components", + "x-pack/solutions/search/packages/kbn-search-api-keys-components" + ], + [ + "@kbn/search-api-keys-server", + "x-pack/solutions/search/packages/kbn-search-api-keys-server" + ], + [ + "@kbn/search-api-panels", + "src/platform/packages/shared/kbn-search-api-panels" + ], + [ + "@kbn/search-assistant", + "x-pack/solutions/search/plugins/search_assistant" + ], + [ + "@kbn/search-connectors", + "src/platform/packages/shared/kbn-search-connectors" + ], + [ + "@kbn/search-connectors-plugin", + "x-pack/solutions/search/plugins/search_connectors" + ], + [ + "@kbn/search-errors", + "src/platform/packages/shared/kbn-search-errors" + ], + [ + "@kbn/search-examples-plugin", + "examples/search_examples" + ], + [ + "@kbn/search-homepage", + "x-pack/solutions/search/plugins/search_homepage" + ], + [ + "@kbn/search-index-documents", + "x-pack/solutions/search/packages/kbn-search-index-documents" + ], + [ + "@kbn/search-indices", + "x-pack/solutions/search/plugins/search_indices" + ], + [ + "@kbn/search-inference-endpoints", + "x-pack/solutions/search/plugins/search_inference_endpoints" + ], + [ + "@kbn/search-navigation", + "x-pack/solutions/search/plugins/search_solution/search_navigation" + ], + [ + "@kbn/search-notebooks", + "x-pack/solutions/search/plugins/search_notebooks" + ], + [ + "@kbn/search-playground", + "x-pack/solutions/search/plugins/search_playground" + ], + [ + "@kbn/search-response-warnings", + "src/platform/packages/shared/kbn-search-response-warnings" + ], + [ + "@kbn/search-shared-ui", + "x-pack/solutions/search/packages/search/shared_ui" + ], + [ + "@kbn/search-types", + "src/platform/packages/shared/kbn-search-types" + ], + [ + "@kbn/searchprofiler-plugin", + "x-pack/platform/plugins/shared/searchprofiler" + ], + [ + "@kbn/security-api-integration-helpers", + "x-pack/test/security_api_integration/packages/helpers" + ], + [ + "@kbn/security-api-key-management", + "x-pack/packages/security/api_key_management" + ], + [ + "@kbn/security-authorization-core", + "x-pack/packages/security/authorization_core" + ], + [ + "@kbn/security-authorization-core-common", + "x-pack/packages/security/authorization_core_common" + ], + [ + "@kbn/security-form-components", + "x-pack/packages/security/form_components" + ], + [ + "@kbn/security-hardening", + "packages/kbn-security-hardening" + ], + [ + "@kbn/security-plugin", + "x-pack/plugins/security" + ], + [ + "@kbn/security-plugin-types-common", + "x-pack/packages/security/plugin_types_common" + ], + [ + "@kbn/security-plugin-types-public", + "x-pack/packages/security/plugin_types_public" + ], + [ + "@kbn/security-plugin-types-server", + "x-pack/packages/security/plugin_types_server" + ], + [ + "@kbn/security-role-management-model", + "x-pack/packages/security/role_management_model" + ], + [ + "@kbn/security-solution-distribution-bar", + "x-pack/solutions/security/packages/distribution_bar" + ], + [ + "@kbn/security-solution-ess", + "x-pack/solutions/security/plugins/security_solution_ess" + ], + [ + "@kbn/security-solution-features", + "x-pack/solutions/security/packages/features" + ], + [ + "@kbn/security-solution-fixtures-plugin", + "x-pack/test/cases_api_integration/common/plugins/security_solution" + ], + [ + "@kbn/security-solution-navigation", + "x-pack/solutions/security/packages/navigation" + ], + [ + "@kbn/security-solution-plugin", + "x-pack/solutions/security/plugins/security_solution" + ], + [ + "@kbn/security-solution-serverless", + "x-pack/solutions/security/plugins/security_solution_serverless" + ], + [ + "@kbn/security-solution-side-nav", + "x-pack/solutions/security/packages/side_nav" + ], + [ + "@kbn/security-solution-storybook-config", + "x-pack/solutions/security/packages/storybook/config" + ], + [ + "@kbn/security-solution-upselling", + "x-pack/solutions/security/packages/upselling" + ], + [ + "@kbn/security-test-endpoints-plugin", + "x-pack/test/security_functional/plugins/test_endpoints" + ], + [ + "@kbn/security-ui-components", + "x-pack/packages/security/ui_components" + ], + [ + "@kbn/securitysolution-autocomplete", + "x-pack/solutions/security/packages/kbn-securitysolution-autocomplete" + ], + [ + "@kbn/securitysolution-data-table", + "x-pack/solutions/security/packages/data_table" + ], + [ + "@kbn/securitysolution-ecs", + "src/platform/packages/shared/kbn-securitysolution-ecs" + ], + [ + "@kbn/securitysolution-endpoint-exceptions-common", + "x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common" + ], + [ + "@kbn/securitysolution-es-utils", + "src/platform/packages/shared/kbn-securitysolution-es-utils" + ], + [ + "@kbn/securitysolution-exception-list-components", + "x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components" + ], + [ + "@kbn/securitysolution-exceptions-common", + "x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common" + ], + [ + "@kbn/securitysolution-hook-utils", + "x-pack/solutions/security/packages/kbn-securitysolution-hook-utils" + ], + [ + "@kbn/securitysolution-io-ts-alerting-types", + "x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types" + ], + [ + "@kbn/securitysolution-io-ts-list-types", + "x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types" + ], + [ + "@kbn/securitysolution-io-ts-types", + "src/platform/packages/shared/kbn-securitysolution-io-ts-types" + ], + [ + "@kbn/securitysolution-io-ts-utils", + "src/platform/packages/shared/kbn-securitysolution-io-ts-utils" + ], + [ + "@kbn/securitysolution-list-api", + "x-pack/solutions/security/packages/kbn-securitysolution-list-api" + ], + [ + "@kbn/securitysolution-list-constants", + "x-pack/solutions/security/packages/kbn-securitysolution-list-constants" + ], + [ + "@kbn/securitysolution-list-hooks", + "x-pack/solutions/security/packages/kbn-securitysolution-list-hooks" + ], + [ + "@kbn/securitysolution-list-utils", + "x-pack/solutions/security/packages/kbn-securitysolution-list-utils" + ], + [ + "@kbn/securitysolution-lists-common", + "x-pack/solutions/security/packages/kbn-securitysolution-lists-common" + ], + [ + "@kbn/securitysolution-rules", + "src/platform/packages/shared/kbn-securitysolution-rules" + ], + [ + "@kbn/securitysolution-t-grid", + "x-pack/solutions/security/packages/kbn-securitysolution-t-grid" + ], + [ + "@kbn/securitysolution-utils", + "x-pack/solutions/security/packages/kbn-securitysolution-utils" + ], + [ + "@kbn/server-http-tools", + "packages/kbn-server-http-tools" + ], + [ + "@kbn/server-route-repository", + "src/platform/packages/shared/kbn-server-route-repository" + ], + [ + "@kbn/server-route-repository-client", + "src/platform/packages/shared/kbn-server-route-repository-client" + ], + [ + "@kbn/server-route-repository-utils", + "src/platform/packages/shared/kbn-server-route-repository-utils" + ], + [ + "@kbn/serverless", + "x-pack/plugins/serverless" + ], + [ + "@kbn/serverless-common-settings", + "packages/serverless/settings/common" + ], + [ + "@kbn/serverless-observability", + "x-pack/solutions/observability/plugins/serverless_observability" + ], + [ + "@kbn/serverless-observability-settings", + "packages/serverless/settings/observability_project" + ], + [ + "@kbn/serverless-project-switcher", + "packages/serverless/project_switcher" + ], + [ + "@kbn/serverless-search", + "x-pack/solutions/search/plugins/serverless_search" + ], + [ + "@kbn/serverless-search-settings", + "src/platform/packages/shared/serverless/settings/search_project" + ], + [ + "@kbn/serverless-security-settings", + "src/platform/packages/shared/serverless/settings/security_project" + ], + [ + "@kbn/serverless-storybook-config", + "packages/serverless/storybook/config" + ], + [ + "@kbn/serverless-types", + "packages/serverless/types" + ], + [ + "@kbn/session-notifications-plugin", + "test/plugin_functional/plugins/session_notifications" + ], + [ + "@kbn/session-view-plugin", + "x-pack/solutions/security/plugins/session_view" + ], + [ + "@kbn/set-map", + "packages/kbn-set-map" + ], + [ + "@kbn/share-examples-plugin", + "examples/share_examples" + ], + [ + "@kbn/share-plugin", + "src/plugins/share" + ], + [ + "@kbn/shared-svg", + "src/platform/packages/shared/kbn-shared-svg" + ], + [ + "@kbn/shared-ux-avatar-solution", + "packages/shared-ux/avatar/solution" + ], + [ + "@kbn/shared-ux-button-exit-full-screen", + "packages/shared-ux/button/exit_full_screen" + ], + [ + "@kbn/shared-ux-button-toolbar", + "packages/shared-ux/button_toolbar" + ], + [ + "@kbn/shared-ux-card-no-data", + "packages/shared-ux/card/no_data/impl" + ], + [ + "@kbn/shared-ux-card-no-data-mocks", + "packages/shared-ux/card/no_data/mocks" + ], + [ + "@kbn/shared-ux-card-no-data-types", + "packages/shared-ux/card/no_data/types" + ], + [ + "@kbn/shared-ux-chrome-navigation", + "packages/shared-ux/chrome/navigation" + ], + [ + "@kbn/shared-ux-error-boundary", + "packages/shared-ux/error_boundary" + ], + [ + "@kbn/shared-ux-file-context", + "packages/shared-ux/file/context" + ], + [ + "@kbn/shared-ux-file-image", + "packages/shared-ux/file/image/impl" + ], + [ + "@kbn/shared-ux-file-image-mocks", + "packages/shared-ux/file/image/mocks" + ], + [ + "@kbn/shared-ux-file-mocks", + "packages/shared-ux/file/mocks" + ], + [ + "@kbn/shared-ux-file-picker", + "packages/shared-ux/file/file_picker/impl" + ], + [ + "@kbn/shared-ux-file-types", + "packages/shared-ux/file/types" + ], + [ + "@kbn/shared-ux-file-upload", + "packages/shared-ux/file/file_upload/impl" + ], + [ + "@kbn/shared-ux-file-util", + "packages/shared-ux/file/util" + ], + [ + "@kbn/shared-ux-link-redirect-app", + "packages/shared-ux/link/redirect_app/impl" + ], + [ + "@kbn/shared-ux-link-redirect-app-mocks", + "packages/shared-ux/link/redirect_app/mocks" + ], + [ + "@kbn/shared-ux-link-redirect-app-types", + "packages/shared-ux/link/redirect_app/types" + ], + [ + "@kbn/shared-ux-markdown", + "packages/shared-ux/markdown/impl" + ], + [ + "@kbn/shared-ux-markdown-mocks", + "packages/shared-ux/markdown/mocks" + ], + [ + "@kbn/shared-ux-markdown-types", + "packages/shared-ux/markdown/types" + ], + [ + "@kbn/shared-ux-page-analytics-no-data", + "packages/shared-ux/page/analytics_no_data/impl" + ], + [ + "@kbn/shared-ux-page-analytics-no-data-mocks", + "packages/shared-ux/page/analytics_no_data/mocks" + ], + [ + "@kbn/shared-ux-page-analytics-no-data-types", + "packages/shared-ux/page/analytics_no_data/types" + ], + [ + "@kbn/shared-ux-page-kibana-no-data", + "packages/shared-ux/page/kibana_no_data/impl" + ], + [ + "@kbn/shared-ux-page-kibana-no-data-mocks", + "packages/shared-ux/page/kibana_no_data/mocks" + ], + [ + "@kbn/shared-ux-page-kibana-no-data-types", + "packages/shared-ux/page/kibana_no_data/types" + ], + [ + "@kbn/shared-ux-page-kibana-template", + "packages/shared-ux/page/kibana_template/impl" + ], + [ + "@kbn/shared-ux-page-kibana-template-mocks", + "packages/shared-ux/page/kibana_template/mocks" + ], + [ + "@kbn/shared-ux-page-kibana-template-types", + "packages/shared-ux/page/kibana_template/types" + ], + [ + "@kbn/shared-ux-page-no-data", + "packages/shared-ux/page/no_data/impl" + ], + [ + "@kbn/shared-ux-page-no-data-config", + "packages/shared-ux/page/no_data_config/impl" + ], + [ + "@kbn/shared-ux-page-no-data-config-mocks", + "packages/shared-ux/page/no_data_config/mocks" + ], + [ + "@kbn/shared-ux-page-no-data-config-types", + "packages/shared-ux/page/no_data_config/types" + ], + [ + "@kbn/shared-ux-page-no-data-mocks", + "packages/shared-ux/page/no_data/mocks" + ], + [ + "@kbn/shared-ux-page-no-data-types", + "packages/shared-ux/page/no_data/types" + ], + [ + "@kbn/shared-ux-page-solution-nav", + "packages/shared-ux/page/solution_nav" + ], + [ + "@kbn/shared-ux-prompt-no-data-views", + "packages/shared-ux/prompt/no_data_views/impl" + ], + [ + "@kbn/shared-ux-prompt-no-data-views-mocks", + "packages/shared-ux/prompt/no_data_views/mocks" + ], + [ + "@kbn/shared-ux-prompt-no-data-views-types", + "packages/shared-ux/prompt/no_data_views/types" + ], + [ + "@kbn/shared-ux-prompt-not-found", + "packages/shared-ux/prompt/not_found" + ], + [ + "@kbn/shared-ux-router", + "packages/shared-ux/router/impl" + ], + [ + "@kbn/shared-ux-router-mocks", + "packages/shared-ux/router/mocks" + ], + [ + "@kbn/shared-ux-router-types", + "packages/shared-ux/router/types" + ], + [ + "@kbn/shared-ux-storybook-config", + "packages/shared-ux/storybook/config" + ], + [ + "@kbn/shared-ux-storybook-mock", + "packages/shared-ux/storybook/mock" + ], + [ + "@kbn/shared-ux-tabbed-modal", + "packages/shared-ux/modal/tabbed" + ], + [ + "@kbn/shared-ux-table-persist", + "packages/shared-ux/table_persist" + ], + [ + "@kbn/shared-ux-utility", + "packages/kbn-shared-ux-utility" + ], + [ + "@kbn/slo-plugin", + "x-pack/solutions/observability/plugins/slo" + ], + [ + "@kbn/slo-schema", + "x-pack/platform/packages/shared/kbn-slo-schema" + ], + [ + "@kbn/snapshot-restore-plugin", + "x-pack/platform/plugins/private/snapshot_restore" + ], + [ + "@kbn/some-dev-log", + "packages/kbn-some-dev-log" + ], + [ + "@kbn/sort-package-json", + "packages/kbn-sort-package-json" + ], + [ + "@kbn/sort-predicates", + "packages/kbn-sort-predicates" + ], + [ + "@kbn/spaces-plugin", + "x-pack/plugins/spaces" + ], + [ + "@kbn/spaces-test-plugin", + "x-pack/test/spaces_api_integration/common/plugins/spaces_test_plugin" + ], + [ + "@kbn/sse-utils", + "src/platform/packages/shared/kbn-sse-utils" + ], + [ + "@kbn/sse-utils-client", + "src/platform/packages/shared/kbn-sse-utils-client" + ], + [ + "@kbn/sse-utils-server", + "src/platform/packages/shared/kbn-sse-utils-server" + ], + [ + "@kbn/stack-alerts-plugin", + "x-pack/platform/plugins/shared/stack_alerts" + ], + [ + "@kbn/stack-connectors-plugin", + "x-pack/platform/plugins/shared/stack_connectors" + ], + [ + "@kbn/stack-management-usage-test-plugin", + "x-pack/test/usage_collection/plugins/stack_management_usage_test" + ], + [ + "@kbn/state-containers-examples-plugin", + "examples/state_containers_examples" + ], + [ + "@kbn/status-plugin-a-plugin", + "test/server_integration/plugins/status_plugin_a" + ], + [ + "@kbn/status-plugin-b-plugin", + "test/server_integration/plugins/status_plugin_b" + ], + [ + "@kbn/std", + "packages/kbn-std" + ], + [ + "@kbn/stdio-dev-helpers", + "packages/kbn-stdio-dev-helpers" + ], + [ + "@kbn/storybook", + "packages/kbn-storybook" + ], + [ + "@kbn/streams-app-plugin", + "x-pack/solutions/observability/plugins/streams_app" + ], + [ + "@kbn/streams-plugin", + "x-pack/solutions/observability/plugins/streams" + ], + [ + "@kbn/streams-schema", + "x-pack/packages/kbn-streams-schema" + ], + [ + "@kbn/synthetics-e2e", + "x-pack/solutions/observability/plugins/synthetics/e2e" + ], + [ + "@kbn/synthetics-plugin", + "x-pack/solutions/observability/plugins/synthetics" + ], + [ + "@kbn/synthetics-private-location", + "x-pack/packages/kbn-synthetics-private-location" + ], + [ + "@kbn/task-manager-fixture-plugin", + "x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture" + ], + [ + "@kbn/task-manager-performance-plugin", + "x-pack/test/plugin_api_perf/plugins/task_manager_performance" + ], + [ + "@kbn/task-manager-plugin", + "x-pack/platform/plugins/shared/task_manager" + ], + [ + "@kbn/telemetry-collection-manager-plugin", + "src/plugins/telemetry_collection_manager" + ], + [ + "@kbn/telemetry-collection-xpack-plugin", + "x-pack/plugins/telemetry_collection_xpack" + ], + [ + "@kbn/telemetry-management-section-plugin", + "src/plugins/telemetry_management_section" + ], + [ + "@kbn/telemetry-plugin", + "src/plugins/telemetry" + ], + [ + "@kbn/telemetry-test-plugin", + "test/plugin_functional/plugins/telemetry" + ], + [ + "@kbn/telemetry-tools", + "packages/kbn-telemetry-tools" + ], + [ + "@kbn/test", + "packages/kbn-test" + ], + [ + "@kbn/test-eui-helpers", + "packages/kbn-test-eui-helpers" + ], + [ + "@kbn/test-feature-usage-plugin", + "x-pack/test/licensing_plugin/plugins/test_feature_usage" + ], + [ + "@kbn/test-jest-helpers", + "packages/kbn-test-jest-helpers" + ], + [ + "@kbn/test-subj-selector", + "packages/kbn-test-subj-selector" + ], + [ + "@kbn/test-suites-serverless", + "x-pack/test_serverless" + ], + [ + "@kbn/test-suites-src", + "test" + ], + [ + "@kbn/test-suites-xpack", + "x-pack/test" + ], + [ + "@kbn/test-suites-xpack-performance", + "x-pack/performance" + ], + [ + "@kbn/testing-embedded-lens-plugin", + "x-pack/examples/testing_embedded_lens" + ], + [ + "@kbn/third-party-lens-navigation-prompt-plugin", + "x-pack/examples/third_party_lens_navigation_prompt" + ], + [ + "@kbn/third-party-vis-lens-example-plugin", + "x-pack/examples/third_party_vis_lens_example" + ], + [ + "@kbn/threat-intelligence-plugin", + "x-pack/solutions/security/plugins/threat_intelligence" + ], + [ + "@kbn/timelines-plugin", + "x-pack/solutions/security/plugins/timelines" + ], + [ + "@kbn/timelion-grammar", + "packages/kbn-timelion-grammar" + ], + [ + "@kbn/timerange", + "src/platform/packages/shared/kbn-timerange" + ], + [ + "@kbn/tinymath", + "packages/kbn-tinymath" + ], + [ + "@kbn/tooling-log", + "packages/kbn-tooling-log" + ], + [ + "@kbn/transform-plugin", + "x-pack/platform/plugins/private/transform" + ], + [ + "@kbn/translations-plugin", + "x-pack/platform/plugins/private/translations" + ], + [ + "@kbn/transpose-utils", + "packages/kbn-transpose-utils" + ], + [ + "@kbn/triggers-actions-ui-example-plugin", + "x-pack/examples/triggers_actions_ui_example" + ], + [ + "@kbn/triggers-actions-ui-plugin", + "x-pack/platform/plugins/shared/triggers_actions_ui" + ], + [ + "@kbn/triggers-actions-ui-types", + "src/platform/packages/shared/kbn-triggers-actions-ui-types" + ], + [ + "@kbn/try-in-console", + "src/platform/packages/shared/kbn-try-in-console" + ], + [ + "@kbn/ts-projects", + "packages/kbn-ts-projects" + ], + [ + "@kbn/ts-type-check-cli", + "packages/kbn-ts-type-check-cli" + ], + [ + "@kbn/typed-react-router-config", + "src/platform/packages/shared/kbn-typed-react-router-config" + ], + [ + "@kbn/ui-actions-browser", + "packages/kbn-ui-actions-browser" + ], + [ + "@kbn/ui-actions-enhanced-examples-plugin", + "x-pack/examples/ui_actions_enhanced_examples" + ], + [ + "@kbn/ui-actions-enhanced-plugin", + "src/plugins/ui_actions_enhanced" + ], + [ + "@kbn/ui-actions-examples-plugin", + "examples/ui_action_examples" + ], + [ + "@kbn/ui-actions-explorer-plugin", + "examples/ui_actions_explorer" + ], + [ + "@kbn/ui-actions-plugin", + "src/plugins/ui_actions" + ], + [ + "@kbn/ui-settings-plugin", + "test/plugin_functional/plugins/ui_settings_plugin" + ], + [ + "@kbn/ui-shared-deps-npm", + "packages/kbn-ui-shared-deps-npm" + ], + [ + "@kbn/ui-shared-deps-src", + "packages/kbn-ui-shared-deps-src" + ], + [ + "@kbn/ui-theme", + "packages/kbn-ui-theme" + ], + [ + "@kbn/unified-data-table", + "src/platform/packages/shared/kbn-unified-data-table" + ], + [ + "@kbn/unified-doc-viewer", + "src/platform/packages/shared/kbn-unified-doc-viewer" + ], + [ + "@kbn/unified-doc-viewer-examples", + "examples/unified_doc_viewer" + ], + [ + "@kbn/unified-doc-viewer-plugin", + "src/platform/plugins/shared/unified_doc_viewer" + ], + [ + "@kbn/unified-field-list", + "src/platform/packages/shared/kbn-unified-field-list" + ], + [ + "@kbn/unified-field-list-examples-plugin", + "examples/unified_field_list_examples" + ], + [ + "@kbn/unified-histogram-plugin", + "src/platform/plugins/shared/unified_histogram" + ], + [ + "@kbn/unified-search-plugin", + "src/plugins/unified_search" + ], + [ + "@kbn/unsaved-changes-badge", + "src/platform/packages/private/kbn-unsaved-changes-badge" + ], + [ + "@kbn/unsaved-changes-prompt", + "src/platform/packages/shared/kbn-unsaved-changes-prompt" + ], + [ + "@kbn/upgrade-assistant-plugin", + "x-pack/plugins/upgrade_assistant" + ], + [ + "@kbn/uptime-plugin", + "x-pack/solutions/observability/plugins/uptime" + ], + [ + "@kbn/url-drilldown-plugin", + "x-pack/plugins/drilldowns/url_drilldown" + ], + [ + "@kbn/url-forwarding-plugin", + "src/plugins/url_forwarding" + ], + [ + "@kbn/usage-collection-plugin", + "src/plugins/usage_collection" + ], + [ + "@kbn/usage-collection-test-plugin", + "test/plugin_functional/plugins/usage_collection" + ], + [ + "@kbn/use-tracked-promise", + "packages/kbn-use-tracked-promise" + ], + [ + "@kbn/user-profile-components", + "packages/kbn-user-profile-components" + ], + [ + "@kbn/user-profile-examples-plugin", + "examples/user_profile_examples" + ], + [ + "@kbn/user-profiles-consumer-plugin", + "x-pack/test/security_api_integration/plugins/user_profiles_consumer" + ], + [ + "@kbn/utility-types", + "packages/kbn-utility-types" + ], + [ + "@kbn/utility-types-jest", + "packages/kbn-utility-types-jest" + ], + [ + "@kbn/utils", + "packages/kbn-utils" + ], + [ + "@kbn/ux-plugin", + "x-pack/solutions/observability/plugins/ux" + ], + [ + "@kbn/v8-profiler-examples-plugin", + "examples/v8_profiler_examples" + ], + [ + "@kbn/validate-next-docs-cli", + "packages/kbn-validate-next-docs-cli" + ], + [ + "@kbn/vis-default-editor-plugin", + "src/plugins/vis_default_editor" + ], + [ + "@kbn/vis-type-gauge-plugin", + "src/plugins/vis_types/gauge" + ], + [ + "@kbn/vis-type-heatmap-plugin", + "src/plugins/vis_types/heatmap" + ], + [ + "@kbn/vis-type-markdown-plugin", + "src/platform/plugins/private/vis_type_markdown" + ], + [ + "@kbn/vis-type-metric-plugin", + "src/plugins/vis_types/metric" + ], + [ + "@kbn/vis-type-pie-plugin", + "src/plugins/vis_types/pie" + ], + [ + "@kbn/vis-type-table-plugin", + "src/plugins/vis_types/table" + ], + [ + "@kbn/vis-type-tagcloud-plugin", + "src/plugins/vis_types/tagcloud" + ], + [ + "@kbn/vis-type-timelion-plugin", + "src/plugins/vis_types/timelion" + ], + [ + "@kbn/vis-type-timeseries-plugin", + "src/plugins/vis_types/timeseries" + ], + [ + "@kbn/vis-type-vega-plugin", + "src/plugins/vis_types/vega" + ], + [ + "@kbn/vis-type-vislib-plugin", + "src/plugins/vis_types/vislib" + ], + [ + "@kbn/vis-type-xy-plugin", + "src/plugins/vis_types/xy" + ], + [ + "@kbn/visualization-ui-components", + "packages/kbn-visualization-ui-components" + ], + [ + "@kbn/visualization-utils", + "packages/kbn-visualization-utils" + ], + [ + "@kbn/visualizations-plugin", + "src/plugins/visualizations" + ], + [ + "@kbn/watcher-plugin", + "x-pack/platform/plugins/private/watcher" + ], + [ + "@kbn/web-worker-stub", + "packages/kbn-web-worker-stub" + ], + [ + "@kbn/whereis-pkg-cli", + "packages/kbn-whereis-pkg-cli" + ], + [ + "@kbn/xstate-utils", + "src/platform/packages/shared/kbn-xstate-utils" + ], + [ + "@kbn/yarn-lock-validator", + "packages/kbn-yarn-lock-validator" + ], + [ + "@kbn/zod", + "packages/kbn-zod" + ], + [ + "@kbn/zod-helpers", + "src/platform/packages/shared/kbn-zod-helpers" + ] +] \ No newline at end of file diff --git a/src/platform/packages/shared/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx b/src/platform/packages/shared/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx index a2476c14aedd6..49b607c3bc887 100644 --- a/src/platform/packages/shared/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx +++ b/src/platform/packages/shared/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx @@ -41,7 +41,7 @@ export interface UseDataGridColumnsCellActionsProps /** * ref to the EuiDataGrid instance */ - dataGridRef: MutableRefObject; + dataGridRef?: MutableRefObject; } export type UseDataGridColumnsCellActions< P extends UseDataGridColumnsCellActionsProps = UseDataGridColumnsCellActionsProps @@ -141,7 +141,7 @@ const createColumnCellAction = ({ const onClick = useCallback(async () => { actionContext.nodeRef.current = await closeAndGetCellElement({ - dataGrid: dataGridRef.current, + dataGrid: dataGridRef?.current, isExpanded, buttonRef, }); diff --git a/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx b/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx index 5b46a3cd33407..fd1adbc3239d2 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx @@ -6,18 +6,16 @@ */ import React from 'react'; import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; -import { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/alerts_table_state'; +import type { AlertsTableProps } from '@kbn/triggers-actions-ui-plugin/public/types'; interface SandboxProps { triggersActionsUi: TriggersAndActionsUIPublicPluginStart; } export const AlertsTableSandbox = ({ triggersActionsUi }: SandboxProps) => { - const { getAlertsStateTable: AlertsTable, alertsTableConfigurationRegistry } = triggersActionsUi; - const alertStateProps: AlertsTableStateProps = { + const { getAlertsStateTable: AlertsTable } = triggersActionsUi; + const alertStateProps: AlertsTableProps = { id: 'observabilityCases', - configurationId: 'observabilityCases', - alertsTableConfigurationRegistry, ruleTypeIds: ['.es-query'], query: { bool: { diff --git a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx index ca932cb81bd6c..59d7dd254a4b6 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import React from 'react'; import { Plugin, CoreSetup, AppMountParameters, CoreStart } from '@kbn/core/public'; import { PluginSetupContract as AlertingSetup } from '@kbn/alerting-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; @@ -14,19 +13,10 @@ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; -import { get } from 'lodash'; import { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; -import { AlertTableConfigRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/alert_table_config_registry'; -import { - AlertsTableConfigurationRegistry, - AlertsTableFlyoutBaseProps, - AlertTableFlyoutComponent, -} from '@kbn/triggers-actions-ui-plugin/public/types'; -import { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; -import { EuiDataGridColumn } from '@elastic/eui'; import { getConnectorType as getSystemLogExampleConnectorType } from './connector_types/system_log_example/system_log_example'; export interface TriggersActionsUiExamplePublicSetupDeps { @@ -78,86 +68,7 @@ export class TriggersActionsUiExamplePlugin }); } - public start( - coreStart: CoreStart, - { triggersActionsUi }: TriggersActionsUiExamplePublicStartDeps - ) { - const { - alertsTableConfigurationRegistry, - }: { alertsTableConfigurationRegistry: AlertTableConfigRegistry } = triggersActionsUi; - - const columns: EuiDataGridColumn[] = [ - { - id: 'event.action', - displayAsText: 'Alert status', - initialWidth: 150, - }, - { - id: '@timestamp', - displayAsText: 'Last updated', - initialWidth: 250, - }, - { - id: 'kibana.alert.duration.us', - displayAsText: 'Duration', - initialWidth: 150, - }, - { - id: 'kibana.alert.reason', - displayAsText: 'Reason', - }, - ]; - - const FlyoutBody: AlertTableFlyoutComponent = ({ alert }: AlertsTableFlyoutBaseProps) => ( -
    - {columns.map((column) => ( -
  • - {get(alert as any, column.id, [])[0]} -
  • - ))} -
- ); - - const FlyoutHeader: AlertTableFlyoutComponent = ({ alert }: AlertsTableFlyoutBaseProps) => { - const { 'kibana.alert.rule.name': name } = alert; - return
{name}
; - }; - - const useInternalFlyout = () => ({ - body: FlyoutBody, - header: FlyoutHeader, - footer: null, - }); - - const sort: SortCombinations[] = [ - { - 'event.action': { - order: 'asc', - }, - }, - ]; - - const config: AlertsTableConfigurationRegistry = { - id: 'observabilityCases', - columns, - useInternalFlyout, - getRenderCellValue: (props: { - data?: Array<{ field: string; value: string }>; - columnId?: string; - }) => { - const value = props.data?.find((d) => d.field === props.columnId)?.value ?? []; - - if (Array.isArray(value)) { - return <>{value.length ? value.join() : '--'}; - } - - return <>{value}; - }, - sort, - }; - - alertsTableConfigurationRegistry.register(config); - + public start(_: CoreStart, { triggersActionsUi }: TriggersActionsUiExamplePublicStartDeps) { triggersActionsUi.actionTypeRegistry.register(getSystemLogExampleConnectorType()); } diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 532b3c2f6786b..2e0dff5f01423 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -31402,10 +31402,6 @@ "xpack.observability.section.errorPanel": "Une erreur est survenue lors de la tentative de récupération des données. Réessayez plus tard", "xpack.observability.serviceGroupMaxServicesUiSettingDescription": "Limiter le nombre de services dans un groupe de services donné", "xpack.observability.serviceGroupMaxServicesUiSettingName": "Nombre maximum de services dans un groupe de services", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.durationColumnDescription": "Durée", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.reasonColumnDescription": "Raison", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.sloColumnDescription": "Nom de règle", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.statusColumnDescription": "Statut", "xpack.observability.sloEdit.fieldSelector.all": "Tous", "xpack.observability.sloEmbeddable.config.sloSelector.all": "Tous les SLO", "xpack.observability.sloEmbeddable.config.sloSelector.ariaLabel": "SLO", @@ -44688,7 +44684,7 @@ "xpack.triggersActionsUI.alertsTable.actions.untrack": "Marquer comme non suivi", "xpack.triggersActionsUI.alertsTable.alertMuted": "Alerte en sourdine", "xpack.triggersActionsUI.alertsTable.alertsCountUnit": "{totalCount, plural, =1 {alerte} other {alertes}}", - "xpack.triggersActionsUI.alertsTable.alertUnuted": "Son de l'alerte réactivé", + "xpack.triggersActionsUI.alertsTable.alertUnmuted": "Son de l'alerte réactivé", "xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle": "Une erreur s’est produite lors de la récupération des données de la fenêtre de maintenance", "xpack.triggersActionsUI.alertsTable.configuration.errorBody": "Une erreur s'est produite lors du chargement du tableau d'alertes. Ce tableau ne dispose pas de la configuration nécessaire. Veuillez contacter votre administrateur pour obtenir de l'aide", "xpack.triggersActionsUI.alertsTable.configuration.errorTitle": "Impossible de charger le tableau d'alertes", @@ -45503,8 +45499,6 @@ "xpack.triggersActionsUI.toolbar.bulkActions.clearSelectionTitle": "Effacer la sélection", "xpack.triggersActionsUI.toolbar.bulkActions.selectAllAlertsTitle": "Sélectionner un total de {totalAlertsFormatted} {totalAlerts, plural, =1 {alerte} other {alertes}}", "xpack.triggersActionsUI.toolbar.bulkActions.selectedAlertsTitle": "{selectedAlertsFormatted} {selectedAlerts, plural, =1 {alerte sélectionnée} other {alertes sélectionnées}}", - "xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "Le type d'objet \"{id}\" n'est pas enregistré.", - "xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "Le type d'objet \"{id}\" est déjà enregistré.", "xpack.triggersActionsUI.unmuteAlert.error": "Erreur lors de la réactivation du son des alertes", "xpack.triggersActionsUI.updateApiKeyConfirmModal.cancelButton": "Annuler", "xpack.triggersActionsUI.updateApiKeyConfirmModal.confirmButton": "Mettre à jour", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index a1f20f7136724..d79d47d56085b 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -31267,10 +31267,6 @@ "xpack.observability.section.errorPanel": "データの取得時にエラーが発生しました。再試行してください", "xpack.observability.serviceGroupMaxServicesUiSettingDescription": "特定のサービスグループのサービス数を制限", "xpack.observability.serviceGroupMaxServicesUiSettingName": "サービスグループの最大サービス", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.durationColumnDescription": "期間", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.reasonColumnDescription": "理由", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.sloColumnDescription": "ルール名", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.statusColumnDescription": "ステータス", "xpack.observability.sloEdit.fieldSelector.all": "すべて", "xpack.observability.sloEmbeddable.config.sloSelector.all": "すべてのSLO", "xpack.observability.sloEmbeddable.config.sloSelector.ariaLabel": "SLO", @@ -44540,7 +44536,7 @@ "xpack.triggersActionsUI.alertsTable.actions.untrack": "未追跡に設定", "xpack.triggersActionsUI.alertsTable.alertMuted": "アラートがミュートされました", "xpack.triggersActionsUI.alertsTable.alertsCountUnit": "{totalCount, plural, other {アラート}}", - "xpack.triggersActionsUI.alertsTable.alertUnuted": "アラートがミュート解除されました", + "xpack.triggersActionsUI.alertsTable.alertUnmuted": "アラートがミュート解除されました", "xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle": "保守時間枠データの取得エラー", "xpack.triggersActionsUI.alertsTable.configuration.errorBody": "アラートテーブルの読み込みエラーが発生しました。このテーブルには必要な構成がありません。ヘルプについては、管理者にお問い合わせください", "xpack.triggersActionsUI.alertsTable.configuration.errorTitle": "アラートテーブルを読み込めません", @@ -45353,8 +45349,6 @@ "xpack.triggersActionsUI.toolbar.bulkActions.clearSelectionTitle": "選択した項目をクリア", "xpack.triggersActionsUI.toolbar.bulkActions.selectAllAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を選択", "xpack.triggersActionsUI.toolbar.bulkActions.selectedAlertsTitle": "Selected {selectedAlertsFormatted} {selectedAlerts, plural, other {件のアラート}}", - "xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "オブジェクトタイプ「{id}」は登録されていません。", - "xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "オブジェクトタイプ「{id}」はすでに登録されています。", "xpack.triggersActionsUI.unmuteAlert.error": "アラートのミュート解除エラー", "xpack.triggersActionsUI.updateApiKeyConfirmModal.cancelButton": "キャンセル", "xpack.triggersActionsUI.updateApiKeyConfirmModal.confirmButton": "更新", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 07d94b64f04b0..cbf6e71374494 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -30813,10 +30813,6 @@ "xpack.observability.section.errorPanel": "尝试提取数据时发生错误。请重试", "xpack.observability.serviceGroupMaxServicesUiSettingDescription": "限制给定服务组中服务的数量", "xpack.observability.serviceGroupMaxServicesUiSettingName": "服务组中的最大服务数", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.durationColumnDescription": "持续时间", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.reasonColumnDescription": "原因", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.sloColumnDescription": "规则名称", - "xpack.observability.slo.sloAlertsEmbeddable.alertsTGrid.statusColumnDescription": "状态", "xpack.observability.sloEdit.fieldSelector.all": "全部", "xpack.observability.sloEmbeddable.config.sloSelector.all": "所有 SLO", "xpack.observability.sloEmbeddable.config.sloSelector.ariaLabel": "SLO", @@ -43881,7 +43877,7 @@ "xpack.triggersActionsUI.alertsTable.actions.untrack": "标记为已取消跟踪", "xpack.triggersActionsUI.alertsTable.alertMuted": "告警已静音", "xpack.triggersActionsUI.alertsTable.alertsCountUnit": "{totalCount, plural, other {告警}}", - "xpack.triggersActionsUI.alertsTable.alertUnuted": "告警已取消静音", + "xpack.triggersActionsUI.alertsTable.alertUnmuted": "告警已取消静音", "xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle": "提取维护窗口数据时出错", "xpack.triggersActionsUI.alertsTable.configuration.errorBody": "加载告警表时出现错误。此表缺少所需配置。请联系您的管理员寻求帮助", "xpack.triggersActionsUI.alertsTable.configuration.errorTitle": "无法加载告警表", diff --git a/x-pack/platform/plugins/shared/cases/public/common/lib/kibana/kibana_react.mock.tsx b/x-pack/platform/plugins/shared/cases/public/common/lib/kibana/kibana_react.mock.tsx index 614dc56b0481b..178f09a5d4eb7 100644 --- a/x-pack/platform/plugins/shared/cases/public/common/lib/kibana/kibana_react.mock.tsx +++ b/x-pack/platform/plugins/shared/cases/public/common/lib/kibana/kibana_react.mock.tsx @@ -41,7 +41,6 @@ export const createStartServicesMock = ({ license }: StartServiceArgs = {}): Sta security: securityMock.createStart(), triggersActionsUi: { actionTypeRegistry: triggersActionsUi.actionTypeRegistry, - alertsTableConfigurationRegistry: triggersActionsUi.alertsTableConfigurationRegistry, getAlertsStateTable: jest.fn().mockReturnValue(
), }, spaces: spacesPluginMock.createStartContract(), diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx index 7366acf6e051f..2777ea9c3b7a8 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx @@ -32,11 +32,6 @@ describe('CaseUI View Page activity tab', () => { appMockRender = createAppMockRenderer(); appMockRender.coreStart.triggersActionsUi.getAlertsStateTable = getAlertsStateTableMock.mockReturnValue(
); - appMockRender.coreStart.triggersActionsUi.alertsTableConfigurationRegistry.register({ - id: 'case-details-alerts-observability', - columns: [], - ruleTypeIds: ['log-threshold'], - }); }); afterEach(() => { @@ -53,8 +48,6 @@ describe('CaseUI View Page activity tab', () => { appMockRender.render(); await waitFor(async () => { expect(getAlertsStateTableMock).toHaveBeenCalledWith({ - alertsTableConfigurationRegistry: expect.anything(), - configurationId: 'securitySolution-case', ruleTypeIds: SECURITY_SOLUTION_RULE_TYPE_IDS, id: 'case-details-alerts-securitySolution', query: { @@ -87,8 +80,6 @@ describe('CaseUI View Page activity tab', () => { await waitFor(async () => { expect(getAlertsStateTableMock).toHaveBeenCalledWith({ - alertsTableConfigurationRegistry: expect.anything(), - configurationId: 'case-details-alerts-observability', ruleTypeIds: ['log-threshold'], consumers: ['observability'], id: 'case-details-alerts-observability', diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx index 6f8223b5305f1..360a764d778c3 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx @@ -8,7 +8,6 @@ import React, { useMemo } from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiProgress } from '@elastic/eui'; -import type { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/alerts_table_state'; import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; import { SECURITY_SOLUTION_OWNER } from '../../../../common/constants'; import type { CaseUI } from '../../../../common'; @@ -18,12 +17,16 @@ import { useGetFeatureIds } from '../../../containers/use_get_feature_ids'; import { CaseViewAlertsEmpty } from './case_view_alerts_empty'; import { CaseViewTabs } from '../case_view_tabs'; import { CASE_VIEW_PAGE_TABS } from '../../../../common/types'; + interface CaseViewAlertsProps { caseData: CaseUI; onAlertsTableLoaded?: (eventIds: Array>) => void; } + export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlertsProps) => { - const { triggersActionsUi } = useKibana().services; + const { + triggersActionsUi: { getAlertsStateTable: AlertsTable }, + } = useKibana().services; const alertIds = getManualAlertIds(caseData.comments); const alertIdsQuery = useMemo( @@ -40,40 +43,6 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts caseData.owner !== SECURITY_SOLUTION_OWNER ); - const configId = - caseData.owner === SECURITY_SOLUTION_OWNER - ? `${caseData.owner}-case` - : !isLoadingAlertFeatureIds - ? triggersActionsUi.alertsTableConfigurationRegistry.getAlertConfigIdPerRuleTypes( - alertData?.ruleTypeIds ?? [] - ) - : ''; - - const alertStateProps: AlertsTableStateProps = useMemo( - () => ({ - alertsTableConfigurationRegistry: triggersActionsUi.alertsTableConfigurationRegistry, - configurationId: configId, - id: `case-details-alerts-${caseData.owner}`, - ruleTypeIds: - caseData.owner === SECURITY_SOLUTION_OWNER - ? SECURITY_SOLUTION_RULE_TYPE_IDS - : alertData?.ruleTypeIds ?? [], - consumers: alertData?.featureIds, - query: alertIdsQuery, - showAlertStatusWithFlapping: caseData.owner !== SECURITY_SOLUTION_OWNER, - onLoaded: onAlertsTableLoaded, - }), - [ - triggersActionsUi.alertsTableConfigurationRegistry, - configId, - caseData.owner, - alertData?.ruleTypeIds, - alertData?.featureIds, - alertIdsQuery, - onAlertsTableLoaded, - ] - ); - if (alertIdsQuery.ids.values.length === 0) { return ( @@ -94,7 +63,18 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts ) : ( - {triggersActionsUi.getAlertsStateTable(alertStateProps)} + ); }; diff --git a/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts b/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts new file mode 100644 index 0000000000000..42cd75f66e3c9 --- /dev/null +++ b/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts @@ -0,0 +1,3 @@ +/// +declare const _default: Cypress.ConfigOptions; +export default _default; diff --git a/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js b/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js new file mode 100644 index 0000000000000..d680a87809d2e --- /dev/null +++ b/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js @@ -0,0 +1,42 @@ +"use strict"; +/* + * 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. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +const cypress_config_1 = require("@kbn/cypress-config"); +// eslint-disable-next-line import/no-default-export +exports.default = (0, cypress_config_1.defineCypressConfig)({ + defaultCommandTimeout: 60000, + requestTimeout: 60000, + responseTimeout: 60000, + execTimeout: 120000, + pageLoadTimeout: 120000, + retries: { + runMode: 2, + }, + env: { + grepFilterSpecs: false, + }, + screenshotsFolder: '../../../../../target/kibana-fleet/cypress/screenshots', + trashAssetsBeforeRuns: false, + video: false, + videosFolder: '../../../../../target/kibana-fleet/cypress/videos', + viewportHeight: 900, + viewportWidth: 1440, + screenshotOnRunFailure: true, + e2e: { + baseUrl: 'http://localhost:5601', + experimentalRunAllSpecs: true, + experimentalMemoryManagement: true, + numTestsKeptInMemory: 3, + specPattern: './cypress/e2e/space_awareness/**/*.cy.ts', + supportFile: './cypress/support/e2e.ts', + setupNodeEvents(on, config) { + // eslint-disable-next-line @typescript-eslint/no-var-requires, @kbn/imports/no_boundary_crossing + return require('./cypress/plugins')(on, config); + }, + }, +}); diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx index 82c39ac0d8013..9449f44145709 100644 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx +++ b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx @@ -18,12 +18,12 @@ import { i18n } from '@kbn/i18n'; import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import { AttachmentType, APP_ID as CASE_APP_ID } from '@kbn/cases-plugin/common'; import { ALERT_RULE_NAME, ALERT_RULE_UUID, ALERT_UUID } from '@kbn/rule-data-utils'; -import type { AlertActionsProps } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { GetAlertsTableProp } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { PLUGIN_ID } from '../../../common/constants/app'; import { useMlKibana } from '../../application/contexts/kibana'; -export function AlertActions(props: AlertActionsProps) { +export const AlertActions: GetAlertsTableProp<'renderActionsCell'> = (props) => { const { alert, refresh } = props; const { cases, triggersActionsUi } = useMlKibana().services; const casesPrivileges = cases?.helpers.canUseCases([CASE_APP_ID]); @@ -167,4 +167,4 @@ export function AlertActions(props: AlertActionsProps) { ); -} +}; diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx new file mode 100644 index 0000000000000..8cbcb64004660 --- /dev/null +++ b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx @@ -0,0 +1,40 @@ +/* + * 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 { get } from 'lodash'; +import React from 'react'; +import { EuiDescriptionList, EuiPanel } from '@elastic/eui'; +import { isDefined } from '@kbn/ml-is-defined'; +import type { GetAlertsTableProp } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { getAlertFormatters } from './render_cell_value'; + +export const AlertsTableFlyoutBody: GetAlertsTableProp<'renderFlyoutBody'> = ({ + alert, + columns, + fieldFormats, +}) => { + const formatter = getAlertFormatters(fieldFormats); + return ( + + { + const alertFieldValue = get(alert, column.id); + const value = ( + Array.isArray(alertFieldValue) ? alertFieldValue.at(-1) : alertFieldValue + ) as string; + + return { + title: column.displayAsText as string, + description: isDefined(value) ? formatter(column.id, value) : '—', + }; + })} + type="column" + columnWidths={[1, 3]} // Same as [25, 75] + /> + + ); +}; diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/register_alerts_table_configuration.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/register_alerts_table_configuration.tsx deleted file mode 100644 index 9154a2c77bf4a..0000000000000 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/register_alerts_table_configuration.tsx +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { type TriggersAndActionsUIPublicPluginSetup } from '@kbn/triggers-actions-ui-plugin/public'; -import type { - AlertsTableConfigurationRegistry, - RenderCustomActionsRowArgs, -} from '@kbn/triggers-actions-ui-plugin/public/types'; -import type { EuiDataGridColumn } from '@elastic/eui'; -import type { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; -import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { - ALERT_DURATION, - ALERT_END, - ALERT_REASON, - ALERT_RULE_NAME, - ALERT_START, - ALERT_STATUS, -} from '@kbn/rule-data-utils'; -import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; -import { APP_ID as CASE_APP_ID, FEATURE_ID_V2 as CASE_GENERAL_ID } from '@kbn/cases-plugin/common'; -import { MANAGEMENT_APP_ID } from '@kbn/deeplinks-management/constants'; -import { getAlertFlyout } from './use_alerts_flyout'; -import { - ALERT_ANOMALY_DETECTION_JOB_ID, - ALERT_ANOMALY_SCORE, - ALERT_ANOMALY_TIMESTAMP, - ML_ALERT_TYPES, -} from '../../../common/constants/alerts'; -import { getAlertFormatters, getRenderCellValue } from './render_cell_value'; -import { AlertActions } from './alert_actions'; - -export function registerAlertsTableConfiguration( - triggersActionsUi: TriggersAndActionsUIPublicPluginSetup, - fieldFormats: FieldFormatsRegistry -) { - const columns: EuiDataGridColumn[] = [ - { - id: ALERT_STATUS, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.status', { - defaultMessage: 'Status', - }), - initialWidth: 150, - }, - { - id: ALERT_REASON, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.reason', { - defaultMessage: 'Reason', - }), - initialWidth: 150, - }, - { - id: ALERT_RULE_NAME, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.ruleName', { - defaultMessage: 'Rule name', - }), - initialWidth: 150, - }, - { - id: ALERT_ANOMALY_DETECTION_JOB_ID, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.jobId', { - defaultMessage: 'Job ID', - }), - initialWidth: 150, - }, - { - id: ALERT_ANOMALY_SCORE, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.anomalyScore', { - defaultMessage: 'Latest anomaly score', - }), - initialWidth: 150, - isSortable: true, - }, - { - id: ALERT_START, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.triggeredAt', { - defaultMessage: 'Triggered at', - }), - initialWidth: 250, - schema: 'datetime', - }, - { - id: ALERT_END, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.recoveredAt', { - defaultMessage: 'Recovered at', - }), - initialWidth: 250, - schema: 'datetime', - }, - { - id: ALERT_ANOMALY_TIMESTAMP, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.anomalyTime', { - defaultMessage: 'Latest anomaly time', - }), - initialWidth: 250, - schema: 'datetime', - }, - { - id: ALERT_DURATION, - displayAsText: i18n.translate('xpack.ml.alertsTable.columns.duration', { - defaultMessage: 'Duration', - }), - initialWidth: 150, - schema: 'numeric', - }, - ]; - - const sort: SortCombinations[] = [ - { - [ALERT_START]: { - order: 'desc' as SortOrder, - }, - }, - ]; - - const config: AlertsTableConfigurationRegistry = { - id: ML_ALERTS_CONFIG_ID, - cases: { - appId: MANAGEMENT_APP_ID, - featureId: CASE_GENERAL_ID, - owner: [CASE_APP_ID], - syncAlerts: false, - }, - columns, - useInternalFlyout: getAlertFlyout(columns, getAlertFormatters(fieldFormats)), - getRenderCellValue, - sort, - useActionsColumn: () => ({ - renderCustomActionsRow: (props: RenderCustomActionsRowArgs) => { - return ; - }, - }), - ruleTypeIds: [ML_ALERT_TYPES.ANOMALY_DETECTION], - }; - - triggersActionsUi.alertsTableConfigurationRegistry.register(config); -} - -export const ML_ALERTS_CONFIG_ID = 'mlAlerts'; diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx index afba911e5ddea..2efeac34feac1 100644 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx +++ b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx @@ -7,13 +7,14 @@ import { isEmpty } from 'lodash'; import React from 'react'; -import type { RenderCellValue } from '@elastic/eui'; import { isDefined } from '@kbn/ml-is-defined'; import { ALERT_DURATION, ALERT_END, ALERT_START } from '@kbn/rule-data-utils'; import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { getFormattedSeverityScore, getSeverityColor } from '@kbn/ml-anomaly-utils'; import { EuiHealth } from '@elastic/eui'; +import type { Alert, GetAlertsTableProp } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { alertFieldNameMap, ALERT_ANOMALY_SCORE, @@ -21,51 +22,40 @@ import { } from '../../../common/constants/alerts'; import { getFieldFormatterProvider } from '../../application/contexts/kibana/use_field_formatter'; -export const getMappedNonEcsValue = ({ - data, - fieldName, -}: { - data: any[]; - fieldName: string; -}): string[] | undefined => { - const item = data.find((d) => d.field === fieldName); - if (item != null && item.value != null) { - return item.value; - } - return undefined; -}; - -const getRenderValue = (mappedNonEcsValue: any) => { +const getAlertFieldValue = (alert: Alert, fieldName: string) => { // can be updated when working on https://github.com/elastic/kibana/issues/140819 - const value = Array.isArray(mappedNonEcsValue) ? mappedNonEcsValue.join() : mappedNonEcsValue; + const rawValue = alert[fieldName]; + const value = Array.isArray(rawValue) ? rawValue.join() : rawValue; if (!isEmpty(value)) { if (typeof value === 'object') { - return JSON.stringify(value); + try { + return JSON.stringify(value); + } catch (e) { + return 'Error: Unable to parse JSON value.'; + } } return value; } - return '—'; + return '--'; }; -export const getRenderCellValue: RenderCellValue = ({ columnId, data, fieldFormats }) => { +export const AlertsTableCellValue: GetAlertsTableProp<'renderCellValue'> = ({ + columnId, + alert, + fieldFormats, +}) => { const alertValueFormatter = getAlertFormatters(fieldFormats); - if (!isDefined(data)) return; - - const mappedNonEcsValue = getMappedNonEcsValue({ - data, - fieldName: columnId, - }); - const value = getRenderValue(mappedNonEcsValue); + const value = getAlertFieldValue(alert, columnId); return alertValueFormatter(columnId, value); }; -export function getAlertFormatters(fieldFormats: FieldFormatsRegistry) { +export function getAlertFormatters(fieldFormats: FieldFormatsStart) { const getFormatter = getFieldFormatterProvider(fieldFormats); - return (columnId: string, value: string | number | undefined): React.ReactElement => { + return (columnId: string, value: string | number | undefined) => { if (!isDefined(value)) return <>{'—'}; switch (columnId) { @@ -111,5 +101,3 @@ export function getAlertEntryFormatter(fieldFormats: FieldFormatsRegistry) { }; }; } - -export type RegisterFormatter = ReturnType; diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/use_alerts_flyout.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/use_alerts_flyout.tsx deleted file mode 100644 index f3d4163fdb9de..0000000000000 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/use_alerts_flyout.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - AlertsTableFlyoutBaseProps, - AlertTableFlyoutComponent, -} from '@kbn/triggers-actions-ui-plugin/public'; -import { get } from 'lodash'; -import React from 'react'; -import { type EuiDataGridColumn, EuiDescriptionList, EuiPanel, EuiTitle } from '@elastic/eui'; -import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; -import { isDefined } from '@kbn/ml-is-defined'; -import type { RegisterFormatter } from './render_cell_value'; - -const FlyoutHeader: AlertTableFlyoutComponent = ({ alert }: AlertsTableFlyoutBaseProps) => { - const name = alert[ALERT_RULE_NAME]; - return ( - -

{name}

-
- ); -}; - -export const getAlertFlyout = - (columns: EuiDataGridColumn[], formatter: RegisterFormatter) => () => { - const FlyoutBody: AlertTableFlyoutComponent = ({ alert, id }: AlertsTableFlyoutBaseProps) => ( - - { - const alertFieldValue = get(alert, column.id); - const value = ( - Array.isArray(alertFieldValue) ? alertFieldValue.at(-1) : alertFieldValue - ) as string; - - return { - title: column.displayAsText as string, - description: isDefined(value) ? formatter(column.id, value) : '—', - }; - })} - type="column" - columnWidths={[1, 3]} // Same as [25, 75] - /> - - ); - - return { - body: FlyoutBody, - header: FlyoutHeader, - footer: null, - }; - }; diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/register_ml_alerts.ts b/x-pack/platform/plugins/shared/ml/public/alerting/register_ml_alerts.ts index 8d47d4d18d947..64b9d85ea30fa 100644 --- a/x-pack/platform/plugins/shared/ml/public/alerting/register_ml_alerts.ts +++ b/x-pack/platform/plugins/shared/ml/public/alerting/register_ml_alerts.ts @@ -29,13 +29,6 @@ export function registerMlAlerts( if (alerting) { registerNavigation(alerting); } - - // Async import to prevent a bundle size increase - Promise.all([getStartServices(), import('./anomaly_detection_alerts_table')]).then( - ([[_, mlStartDependencies], { registerAlertsTableConfiguration }]) => { - registerAlertsTableConfiguration(triggersActionsUi, mlStartDependencies.fieldFormats); - } - ); } export function registerNavigation(alerting: AlertingSetup) { diff --git a/x-pack/platform/plugins/shared/ml/public/application/contexts/kibana/use_field_formatter.ts b/x-pack/platform/plugins/shared/ml/public/application/contexts/kibana/use_field_formatter.ts index b665f07f73b9e..edf30cd043374 100644 --- a/x-pack/platform/plugins/shared/ml/public/application/contexts/kibana/use_field_formatter.ts +++ b/x-pack/platform/plugins/shared/ml/public/application/contexts/kibana/use_field_formatter.ts @@ -5,8 +5,9 @@ * 2.0. */ -import type { FieldFormatParams, FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; +import type { FieldFormatParams } from '@kbn/field-formats-plugin/common'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { useMlKibana } from './kibana_context'; /** @@ -23,7 +24,7 @@ const defaultParam: Record = { }; export const getFieldFormatterProvider = - (fieldFormats: FieldFormatsRegistry) => + (fieldFormats: FieldFormatsStart) => (fieldType: FIELD_FORMAT_IDS, params?: FieldFormatParams) => { const fieldFormatter = fieldFormats.deserialize({ id: fieldType, diff --git a/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx b/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx index 338c5a2a2b3c5..4f9b51e5488e5 100644 --- a/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx +++ b/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React, { type FC, useState } from 'react'; import { EuiButtonGroup, EuiFlexGroup, @@ -12,53 +13,136 @@ import { EuiNotificationBadge, EuiSpacer, EuiLoadingSpinner, + type EuiDataGridColumn, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ALERT_STATUS_ACTIVE, type AlertStatus } from '@kbn/rule-data-utils'; -import type { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/alerts_table_state'; -import React, { type FC, useState } from 'react'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_REASON, + ALERT_RULE_NAME, + ALERT_START, + ALERT_STATUS, + ALERT_STATUS_ACTIVE, + + type AlertStatus, +} from '@kbn/rule-data-utils'; import useObservable from 'react-use/lib/useObservable'; import { ML_VALID_CONSUMERS } from '../../../../common/constants/alerts'; import { ML_RULE_TYPE_IDS } from '../../../../common'; -import { ML_ALERTS_CONFIG_ID } from '../../../alerting/anomaly_detection_alerts_table/register_alerts_table_configuration'; +import { MANAGEMENT_APP_ID } from '@kbn/deeplinks-management/constants'; +import { APP_ID as CASE_APP_ID, FEATURE_ID as CASE_GENERAL_ID } from '@kbn/cases-plugin/common'; +import type { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; +import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { AlertActions } from '../../../alerting/anomaly_detection_alerts_table/alert_actions'; +import { AlertsTableFlyoutBody } from '../../../alerting/anomaly_detection_alerts_table/flyout_body'; import { CollapsiblePanel } from '../../components/collapsible_panel'; import { useMlKibana } from '../../contexts/kibana'; import { useAnomalyExplorerContext } from '../anomaly_explorer_context'; import { AlertsSummary } from './alerts_summary'; import { AnomalyDetectionAlertsOverviewChart } from './chart'; import { statusNameMap } from './const'; +import { + ALERT_ANOMALY_DETECTION_JOB_ID, + ALERT_ANOMALY_SCORE, + ALERT_ANOMALY_TIMESTAMP, +} from '../../../../common/constants/alerts'; +import { AlertsTableCellValue } from '../../../alerting/anomaly_detection_alerts_table/render_cell_value'; + +const columns: EuiDataGridColumn[] = [ + { + id: ALERT_STATUS, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.status', { + defaultMessage: 'Status', + }), + initialWidth: 150, + }, + { + id: ALERT_REASON, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.reason', { + defaultMessage: 'Reason', + }), + initialWidth: 150, + }, + { + id: ALERT_RULE_NAME, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.ruleName', { + defaultMessage: 'Rule name', + }), + initialWidth: 150, + }, + { + id: ALERT_ANOMALY_DETECTION_JOB_ID, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.jobId', { + defaultMessage: 'Job ID', + }), + initialWidth: 150, + }, + { + id: ALERT_ANOMALY_SCORE, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.anomalyScore', { + defaultMessage: 'Latest anomaly score', + }), + initialWidth: 150, + isSortable: true, + }, + { + id: ALERT_START, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.triggeredAt', { + defaultMessage: 'Triggered at', + }), + initialWidth: 250, + schema: 'datetime', + }, + { + id: ALERT_END, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.recoveredAt', { + defaultMessage: 'Recovered at', + }), + initialWidth: 250, + schema: 'datetime', + }, + { + id: ALERT_ANOMALY_TIMESTAMP, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.anomalyTime', { + defaultMessage: 'Latest anomaly time', + }), + initialWidth: 250, + schema: 'datetime', + }, + { + id: ALERT_DURATION, + displayAsText: i18n.translate('xpack.ml.alertsTable.columns.duration', { + defaultMessage: 'Duration', + }), + initialWidth: 150, + schema: 'numeric', + }, +]; + +const sort: SortCombinations[] = [ + { + [ALERT_START]: { + order: 'desc' as SortOrder, + }, + }, +]; export const AlertsPanel: FC = () => { const { services: { triggersActionsUi }, } = useMlKibana(); + const { getAlertsStateTable: AlertsTable } = triggersActionsUi!; const [isOpen, setIsOpen] = useState(true); const [toggleSelected, setToggleSelected] = useState(`alertsSummary`); - const { - services: { fieldFormats }, - } = useMlKibana(); const { anomalyDetectionAlertsStateService } = useAnomalyExplorerContext(); const countByStatus = useObservable(anomalyDetectionAlertsStateService.countByStatus$); const alertsQuery = useObservable(anomalyDetectionAlertsStateService.alertsQuery$, {}); const isLoading = useObservable(anomalyDetectionAlertsStateService.isLoading$, true); - const alertStateProps: AlertsTableStateProps = { - alertsTableConfigurationRegistry: triggersActionsUi!.alertsTableConfigurationRegistry, - configurationId: ML_ALERTS_CONFIG_ID, - id: `ml-details-alerts`, - ruleTypeIds: ML_RULE_TYPE_IDS, - consumers: ML_VALID_CONSUMERS, - query: alertsQuery, - showAlertStatusWithFlapping: true, - cellContext: { - fieldFormats, - }, - }; - const alertsStateTable = triggersActionsUi!.getAlertsStateTable(alertStateProps); - const toggleButtons = [ { id: `alertsSummary`, @@ -121,7 +205,28 @@ export const AlertsPanel: FC = () => { /> - {toggleSelected === 'alertsTable' ? alertsStateTable : } + {toggleSelected === 'alertsTable' ? ( + + ) : ( + + )} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts index 60997974da2fd..0a518a750948b 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts @@ -5,4 +5,12 @@ * 2.0. */ +import React, { forwardRef, ReactElement } from 'react'; + export const nonNullable = (v: T): v is NonNullable => v != null; + +export function typedForwardRef( + render: (props: P, ref: React.Ref) => ReactElement +): (props: P & React.RefAttributes) => ReactElement { + return forwardRef(render) as any; +} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.test.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.test.ts deleted file mode 100644 index 2910a6ee6b37f..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { AlertTableConfigRegistry } from './alert_table_config_registry'; - -export const ExpressionComponent: React.FunctionComponent = () => { - return null; -}; - -const getTestAlertTableConfig = (id?: string, ruleTypeIds?: string[]) => { - return { - id: id || 'test-alert-table-config', - columns: [], - ...(ruleTypeIds ? { ruleTypeIds } : {}), - }; -}; - -beforeEach(() => jest.resetAllMocks()); - -describe('register()', () => { - test('able to register alert table config', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - alertTableConfigRegistry.register(getTestAlertTableConfig()); - expect(alertTableConfigRegistry.has('test-alert-table-config')).toEqual(true); - }); - - test('throws error if alert table config already registered', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - alertTableConfigRegistry.register(getTestAlertTableConfig('my-test-alert-type-1')); - expect(() => - alertTableConfigRegistry.register(getTestAlertTableConfig('my-test-alert-type-1')) - ).toThrowErrorMatchingInlineSnapshot( - `"Object type \\"my-test-alert-type-1\\" is already registered."` - ); - }); -}); - -describe('get()', () => { - test('returns alert table config', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - alertTableConfigRegistry.register(getTestAlertTableConfig('my-action-type-snapshot')); - const alertTableConfig = alertTableConfigRegistry.get('my-action-type-snapshot'); - expect(alertTableConfig).toMatchInlineSnapshot(` - Object { - "columns": Array [], - "id": "my-action-type-snapshot", - } - `); - }); - - test(`throw error when alert table config doesn't exist`, () => { - const actionTypeRegistry = new AlertTableConfigRegistry(); - expect(() => - actionTypeRegistry.get('not-exist-action-type') - ).toThrowErrorMatchingInlineSnapshot( - `"Object type \\"not-exist-action-type\\" is not registered."` - ); - }); -}); - -describe('list()', () => { - test('returns list of alert table config', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - const alertTableConfig = getTestAlertTableConfig(); - alertTableConfigRegistry.register(alertTableConfig); - const alertTableConfigList = alertTableConfigRegistry.list(); - expect(alertTableConfigList).toMatchInlineSnapshot(` - Array [ - Object { - "columns": Array [], - "id": "test-alert-table-config", - }, - ] - `); - }); -}); - -describe('has()', () => { - test('returns false for unregistered alert table config', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - expect(alertTableConfigRegistry.has('my-alert-type')).toEqual(false); - }); - - test('returns true after registering an alert table config', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - alertTableConfigRegistry.register(getTestAlertTableConfig()); - expect(alertTableConfigRegistry.has('test-alert-table-config')); - }); -}); - -describe('update()', () => { - test('returns object after updating for alert table config register', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - alertTableConfigRegistry.register(getTestAlertTableConfig()); - const toggleColumn = (columnId: string) => {}; - const updateObj = alertTableConfigRegistry.update('test-alert-table-config', { - ...getTestAlertTableConfig(), - actions: { - toggleColumn, - }, - }); - expect(updateObj).toMatchInlineSnapshot(` - Object { - "actions": Object { - "toggleColumn": [Function], - }, - "columns": Array [], - "id": "test-alert-table-config", - } - `); - expect(alertTableConfigRegistry.getActions('test-alert-table-config')?.toggleColumn).toEqual( - toggleColumn - ); - }); - - test('throw an error in alert table config is not registred', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - const toggleColumn = (columnId: string) => {}; - expect(() => - alertTableConfigRegistry.update('test-alert-table-config', { - ...getTestAlertTableConfig(), - actions: { - toggleColumn, - }, - }) - ).toThrowErrorMatchingInlineSnapshot( - `"Object type \\"test-alert-table-config\\" is not registered."` - ); - }); -}); - -describe('getAlertConfigIdPerRuleTypes()', () => { - const alertTableConfigRegistry = new AlertTableConfigRegistry(); - beforeAll(() => { - alertTableConfigRegistry.register( - getTestAlertTableConfig('ml-alerts-table', ['xpack-ml-anomaly']) - ); - alertTableConfigRegistry.register( - getTestAlertTableConfig('o11y-alerts-table', [ - 'o11y-custom-threshold', - 'o11y-apm-threshold', - 'o11y-log-threshold', - 'o11y-metric-threshold', - ]) - ); - alertTableConfigRegistry.register(getTestAlertTableConfig('security-alerts-table', [])); - }); - - test('should return a config ID if match one ruleTypeId match with a configuration', () => { - const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes(['xpack-ml-anomaly']); - expect(configId).toEqual('ml-alerts-table'); - }); - - test('should return a config ID if more that one ruleTypeId match with a configuration', () => { - const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes([ - 'o11y-apm-threshold', - 'o11y-metric-threshold', - ]); - expect(configId).toEqual('o11y-alerts-table'); - }); - - test('should return the generic config ID if more that one ruleTypeId match with more than one configuration', () => { - const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes([ - 'o11y-apm-threshold', - 'o11y-metric-threshold', - 'ml-alerts-table', - ]); - expect(configId).toEqual('stackAlerts-generic-alerts-table'); - }); - - test('should return the generic config ID if empty ruleTypeIds match with a configuration', () => { - const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes([]); - expect(configId).toEqual('stackAlerts-generic-alerts-table'); - }); - - test('should return the generic config ID if an unknown ruleTypeId match with NO configuration', () => { - const configId = alertTableConfigRegistry.getAlertConfigIdPerRuleTypes(['unknown-threshold']); - expect(configId).toEqual('stackAlerts-generic-alerts-table'); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.ts deleted file mode 100644 index 76e0dc5b39a38..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/alert_table_config_registry.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { noop } from 'lodash'; -import { ALERT_TABLE_GENERIC_CONFIG_ID } from './constants'; -import { AlertsTableConfigurationRegistry } from '../types'; - -export class AlertTableConfigRegistry { - private readonly objectTypes: Map = new Map(); - - /** - * Returns if the object type registry has the given type registered - */ - public has(id: string) { - return this.objectTypes.has(id); - } - - /** - * Registers an object type to the type registry - */ - public register(objectType: AlertsTableConfigurationRegistry) { - if (this.has(objectType.id)) { - throw new Error( - i18n.translate( - 'xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage', - { - defaultMessage: 'Object type "{id}" is already registered.', - values: { - id: objectType.id, - }, - } - ) - ); - } - this.objectTypes.set(objectType.id, objectType); - } - - /** - * Returns an object type, throw error if not registered - */ - public get(id: string) { - if (!this.has(id)) { - throw new Error( - i18n.translate('xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage', { - defaultMessage: 'Object type "{id}" is not registered.', - values: { - id, - }, - }) - ); - } - return this.objectTypes.get(id)!; - } - - public getActions(id: string): AlertsTableConfigurationRegistry['actions'] { - return ( - (this.objectTypes.get(id) as AlertsTableConfigurationRegistry)?.actions ?? { - toggleColumn: noop, - } - ); - } - - public list() { - return Array.from(this.objectTypes).map(([id, objectType]) => objectType); - } - - /** - * Returns an object type, throw error if not registered - */ - public update(id: string, objectType: AlertsTableConfigurationRegistry) { - if (!this.has(id)) { - throw new Error( - i18n.translate('xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage', { - defaultMessage: 'Object type "{id}" is not registered.', - values: { - id, - }, - }) - ); - } - this.objectTypes.set(id, objectType); - return this.objectTypes.get(id)!; - } - - public getAlertConfigIdPerRuleTypes(ruleTypeIds: string[]): string { - const alertConfigs: string[] = []; - Array.from(this.objectTypes).forEach(([id, objectType]) => { - if (ruleTypeIds.every((ruleTypeId) => objectType.ruleTypeIds?.includes(ruleTypeId))) { - alertConfigs.push(id); - } - }); - if (alertConfigs.length === 1) { - return alertConfigs[0]; - } - // If there is more than one, we will return the generic alert configuration id - return ALERT_TABLE_GENERIC_CONFIG_ID; - } -} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/connectors_app.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/connectors_app.tsx index f00db5879120b..40cbe13adfffc 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/connectors_app.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/connectors_app.tsx @@ -26,11 +26,7 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public'; import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; -import { - ActionTypeRegistryContract, - AlertsTableConfigurationRegistryContract, - RuleTypeRegistryContract, -} from '../types'; +import { ActionTypeRegistryContract, RuleTypeRegistryContract } from '../types'; import { setDataViewsService } from '../common/lib/data_apis'; import { KibanaContextProvider, useKibana } from '../common/lib/kibana'; @@ -56,7 +52,6 @@ export interface TriggersAndActionsUiServices extends CoreStart { setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; actionTypeRegistry: ActionTypeRegistryContract; ruleTypeRegistry: RuleTypeRegistryContract; - alertsTableConfigurationRegistry: AlertsTableConfigurationRegistryContract; history: ScopedHistory; kibanaFeatures: KibanaFeature[]; element: HTMLElement; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts index 19ecc6a198594..9031179f58d61 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts @@ -9,15 +9,15 @@ export const triggersActionsUiQueriesKeys = { all: ['triggersActionsUi'] as const, alertsTable: () => [...triggersActionsUiQueriesKeys.all, 'alertsTable'] as const, cases: () => [...triggersActionsUiQueriesKeys.alertsTable(), 'cases'] as const, - mutedAlerts: () => [...triggersActionsUiQueriesKeys.alertsTable(), 'mutedAlerts'] as const, + mutedAlerts: (ruleIds: string[]) => + [...triggersActionsUiQueriesKeys.alertsTable(), 'mutedAlerts', ruleIds] as const, casesBulkGet: (caseIds: string[]) => [...triggersActionsUiQueriesKeys.cases(), 'bulkGet', caseIds] as const, maintenanceWindows: () => [...triggersActionsUiQueriesKeys.alertsTable(), 'maintenanceWindows'] as const, - maintenanceWindowsBulkGet: (maintenanceWindowIds: string[]) => - [ - ...triggersActionsUiQueriesKeys.maintenanceWindows(), - 'bulkGet', - maintenanceWindowIds, - ] as const, + maintenanceWindowsBulkGet: (maintenanceWindowIds: string[]) => [ + ...triggersActionsUiQueriesKeys.maintenanceWindows(), + 'bulkGet', + maintenanceWindowIds, + ], }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/rules_app.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/rules_app.tsx index 2477d9d95c4b4..69651ed774c2d 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/rules_app.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/rules_app.tsx @@ -37,11 +37,7 @@ import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { CloudSetup } from '@kbn/cloud-plugin/public'; import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; -import { - ActionTypeRegistryContract, - AlertsTableConfigurationRegistryContract, - RuleTypeRegistryContract, -} from '../types'; +import { ActionTypeRegistryContract, RuleTypeRegistryContract } from '../types'; import { Section, legacyRouteToRuleDetails, @@ -78,7 +74,6 @@ export interface TriggersAndActionsUiServices extends CoreStart { setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; actionTypeRegistry: ActionTypeRegistryContract; ruleTypeRegistry: RuleTypeRegistryContract; - alertsTableConfigurationRegistry: AlertsTableConfigurationRegistryContract; history: ScopedHistory; kibanaFeatures: KibanaFeature[]; element: HTMLElement; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/action_connector_form/connector_rules_list.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/action_connector_form/connector_rules_list.test.tsx index e3d4a1c9f7d27..b0ea7c40ebe04 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/action_connector_form/connector_rules_list.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/action_connector_form/connector_rules_list.test.tsx @@ -15,7 +15,6 @@ import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { getIsExperimentalFeatureEnabled } from '../../../common/get_experimental_features'; import { ConnectorRulesList } from './connector_rules_list'; import { useKibana } from '../../../common/lib/kibana'; -import {} from '../../lib/rule_api/rules_kuery_filter'; import { ActionConnector } from '../../../types'; import { mockedRulesData, ruleTypeFromApi } from '../rules_list/components/test_helpers'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx index 6b9c1c4bbdf3d..721862f1ebae5 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx @@ -6,19 +6,18 @@ */ import * as React from 'react'; -import { waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { StackAlertsPage } from './stack_alerts_page'; import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; +import { createAppMockRenderer } from '../../test_utils'; import { ruleTypesIndex } from '../../../mock/rule_types_index'; +import { loadRuleTypes } from '../../../lib/rule_api/rule_types'; -jest.mock('../../../../common/get_experimental_features'); -jest.mock('../../../../common/lib/kibana'); - -jest.mock('../../../hooks/use_load_rule_types_query', () => ({ - useLoadRuleTypesQuery: jest.fn(), -})); +jest.mock('../../../lib/rule_api/rule_types'); +const mockLoadRuleTypes = jest + .mocked(loadRuleTypes) + .mockResolvedValue(Array.from(ruleTypesIndex.values())); jest.mock('../../alerts_search_bar/url_synced_alerts_search_bar', () => ({ UrlSyncedAlertsSearchBar: () => ( @@ -26,46 +25,30 @@ jest.mock('../../alerts_search_bar/url_synced_alerts_search_bar', () => ({ ), })); -const mockAlertsTable = jest.fn(() =>
{'Alerts table'}
); -jest.mock('../../alerts_table/alerts_table_state', () => mockAlertsTable); +jest.mock('../../alerts_table/alerts_data_grid', () => ({ + AlertsDataGrid: jest.fn(() =>
{'Alerts table'}
), +})); -describe('StackAlertsPage', () => { - let appMockRender: AppMockRenderer; +jest.mock('../../../../common/get_experimental_features'); +jest.mocked(getIsExperimentalFeatureEnabled).mockReturnValue(false); - beforeEach(() => { - appMockRender = createAppMockRenderer(); - (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); +describe('StackAlertsPage', () => { + const appMockRender = createAppMockRenderer({ + additionalServices: {}, }); it('renders the stack alerts page with the correct permissions', async () => { - const { useLoadRuleTypesQuery } = jest.requireMock('../../../hooks/use_load_rule_types_query'); - useLoadRuleTypesQuery.mockReturnValue({ - ruleTypesState: { - data: ruleTypesIndex, - initialLoad: false, - }, - authorizedToReadAnyRules: true, - }); - const { getByTestId } = appMockRender.render(); + appMockRender.render(); - await waitFor(() => { - expect(getByTestId('stackAlertsPageContent')).toBeInTheDocument(); - expect(getByTestId('alertsTable')).toBeInTheDocument(); - expect(getByTestId('urlSyncedAlertsSearchBar')).toBeInTheDocument(); - }); + expect(await screen.findByTestId('stackAlertsPageContent')).toBeInTheDocument(); + expect(await screen.findByTestId('alertsTable')).toBeInTheDocument(); + expect(await screen.findByTestId('urlSyncedAlertsSearchBar')).toBeInTheDocument(); }); it('shows the missing permission prompt if the user is not allowed to read any rules', async () => { - const { useLoadRuleTypesQuery } = jest.requireMock('../../../hooks/use_load_rule_types_query'); - useLoadRuleTypesQuery.mockReturnValue({ - ruleTypesState: { - data: ruleTypesIndex, - initialLoad: false, - }, - authorizedToReadAnyRules: false, - }); - const { getByTestId } = appMockRender.render(); + mockLoadRuleTypes.mockResolvedValue([]); + appMockRender.render(); - await waitFor(() => expect(getByTestId('noPermissionPrompt')).toBeInTheDocument()); + expect(await screen.findByTestId('noPermissionPrompt')).toBeInTheDocument(); }); }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx index f5a39ab5ac0d3..624f18c809d81 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { lazy, Suspense, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiBetaBadge, @@ -29,7 +29,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ALERTS_PAGE_ID } from '../../../../common/constants'; import { QuickFiltersMenuItem } from '../../alerts_search_bar/quick_filters'; import { NoPermissionPrompt } from '../../../components/prompts/no_permission_prompt'; -import { ALERT_TABLE_GLOBAL_CONFIG_ID } from '../../../constants'; import { useRuleStats } from '../hooks/use_rule_stats'; import { getAlertingSectionBreadcrumb } from '../../../lib/breadcrumb'; import { alertProducersData } from '../../alerts_table/constants'; @@ -53,11 +52,17 @@ import { useRuleTypeIdsByFeatureId, } from '../hooks/use_rule_type_ids_by_feature_id'; import { TECH_PREVIEW_DESCRIPTION, TECH_PREVIEW_LABEL } from '../../translations'; +import { + defaultAlertsTableColumns, + defaultAlertsTableSort, +} from '../../alerts_table/configuration'; +import { DefaultAlertsFlyoutBody } from '../../alerts_table/alerts_flyout/default_alerts_flyout'; +import { AlertActionsCell } from '../../alerts_table/row_actions/alert_actions_cell'; +import { AlertsTable } from '../../alerts_table/alerts_table'; +import { renderCellValue } from '../../alerts_table/cells/render_cell_value'; import { AlertsTableSupportedConsumers } from '../../alerts_table/types'; import { NON_SIEM_CONSUMERS } from '../../alerts_search_bar/constants'; -const AlertsTable = lazy(() => import('../../alerts_table/alerts_table_state')); - /** * A unified view for all types of alerts */ @@ -119,7 +124,6 @@ const PageContentComponent: React.FC = ({ authorizedToReadAnyRules, ruleTypeIdsByFeatureId, }) => { - const { alertsTableConfigurationRegistry } = useKibana().services; const ruleTypeIdsByFeatureIdEntries = Object.entries(ruleTypeIdsByFeatureId); const [esQuery, setEsQuery] = useState({ bool: {} } as { bool: BoolQuery }); @@ -262,13 +266,16 @@ const PageContentComponent: React.FC = ({ // columns alignment from breaking after a change in the number of columns key={ruleTypeIds.join()} id="stack-alerts-page-table" - configurationId={ALERT_TABLE_GLOBAL_CONFIG_ID} - alertsTableConfigurationRegistry={alertsTableConfigurationRegistry} ruleTypeIds={ruleTypeIds} consumers={consumers} query={esQuery} + initialSort={defaultAlertsTableSort} showAlertStatusWithFlapping + columns={defaultAlertsTableColumns} initialPageSize={20} + renderCellValue={renderCellValue} + renderFlyoutBody={DefaultAlertsFlyoutBody} + renderActionsCell={AlertActionsCell} />
diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx new file mode 100644 index 0000000000000..ede59e5fe63e6 --- /dev/null +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx @@ -0,0 +1,259 @@ +/* + * 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 { BrowserFields } from '@kbn/alerting-types'; +import { + AdditionalContext, + Alerts, + AlertsDataGridProps, + AlertsField, + FetchAlertData, + RenderContext, + RowSelectionState, +} from '../../../types'; +import { getCasesMapMock } from './cases/index.mock'; +import { getMaintenanceWindowsMock } from './maintenance_windows/index.mock'; +import { EuiButton, EuiButtonIcon, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { identity } from 'lodash'; +import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; +import { + ALERT_CASE_IDS, + ALERT_FLAPPING, + ALERT_REASON, + ALERT_RULE_NAME, + ALERT_STATUS, +} from '@kbn/rule-data-utils'; +import { FIELD_BROWSER_CUSTOM_CREATE_BTN_TEST_ID } from './constants'; + +export type BaseAlertsDataGridProps = AlertsDataGridProps; +export type TestAlertsDataGridProps = Partial> & { + renderContext?: Partial>; +}; + +export const mockFieldFormatsRegistry = { + deserialize: jest.fn(() => ({ + id: 'string', + convert: jest.fn().mockImplementation(identity), + })), +} as unknown as FieldFormatsRegistry; + +export const mockBrowserFields: BrowserFields = { + kibana: { + fields: { + [AlertsField.uuid]: { + category: 'kibana', + name: AlertsField.uuid, + }, + [AlertsField.name]: { + category: 'kibana', + name: AlertsField.name, + }, + [AlertsField.reason]: { + category: 'kibana', + name: AlertsField.reason, + }, + }, + }, +}; + +export const mockColumns = [ + { + id: ALERT_RULE_NAME, + displayAsText: 'Name', + }, + { + id: ALERT_REASON, + displayAsText: 'Reason', + }, + { + id: ALERT_STATUS, + displayAsText: 'Alert status', + }, + { + id: ALERT_CASE_IDS, + displayAsText: 'Cases', + }, +]; + +export const mockAlerts = [ + { + [ALERT_RULE_NAME]: ['one'], + [ALERT_REASON]: ['two'], + [ALERT_STATUS]: ['active'], + [ALERT_FLAPPING]: [true], + [ALERT_CASE_IDS]: ['test-id'], + }, + { + [ALERT_RULE_NAME]: ['three'], + [ALERT_REASON]: ['four'], + [ALERT_STATUS]: ['active'], + [ALERT_FLAPPING]: [false], + [ALERT_CASE_IDS]: ['test-id-2'], + }, + { + [ALERT_RULE_NAME]: ['five'], + [ALERT_REASON]: ['six'], + [ALERT_STATUS]: ['recovered'], + [ALERT_FLAPPING]: [true], + }, + { + [ALERT_RULE_NAME]: ['seven'], + [ALERT_REASON]: ['eight'], + [ALERT_STATUS]: ['recovered'], + [ALERT_FLAPPING]: [false], + }, +] as unknown as Alerts; +export const mockOldAlertsData = [ + [ + { + field: AlertsField.name, + value: ['one'], + }, + { + field: AlertsField.reason, + value: ['two'], + }, + ], + [ + { + field: AlertsField.name, + value: ['three'], + }, + { + field: AlertsField.reason, + value: ['four'], + }, + ], +] as FetchAlertData['oldAlertsData']; +export const mockEcsData = [ + [ + { + '@timestamp': ['2023-01-28T10:48:49.559Z'], + _id: 'SomeId', + _index: 'SomeIndex', + kibana: { + alert: { + rule: { + name: ['one'], + }, + reason: ['two'], + }, + }, + }, + ], + [ + { + '@timestamp': ['2023-01-27T10:48:49.559Z'], + _id: 'SomeId2', + _index: 'SomeIndex', + kibana: { + alert: { + rule: { + name: ['three'], + }, + reason: ['four'], + }, + }, + }, + ], +] as FetchAlertData['ecsAlertsData']; + +export const mockCases = getCasesMapMock(); +export const mockMaintenanceWindows = getMaintenanceWindowsMock().reduce( + (acc, val) => acc.set(val.id, val), + new Map() +); + +export const mockBulkActionsState = { + rowSelection: new Map(), + isAllSelected: false, + areAllVisibleRowsSelected: false, + rowCount: 4, + updatedAt: Date.now(), +}; + +export const mockRenderContext = { + tableId: 'test-table', + showAlertStatusWithFlapping: true, + columns: mockColumns, + refresh: jest.fn(), + isLoading: false, + isLoadingAlerts: false, + alerts: mockAlerts, + ecsAlertsData: mockEcsData, + oldAlertsData: mockOldAlertsData, + alertsCount: mockAlerts.length, + browserFields: mockBrowserFields, + isLoadingCases: false, + cases: mockCases, + isLoadingMaintenanceWindows: false, + maintenanceWindows: mockMaintenanceWindows, + isLoadingMutedAlerts: false, + mutedAlerts: {}, + pageIndex: 0, + pageSize: 1, + fieldFormats: mockFieldFormatsRegistry, + openAlertInFlyout: jest.fn(), + bulkActionsStore: [ + mockBulkActionsState, + jest.fn(), + ] as unknown as RenderContext['bulkActionsStore'], + renderCellValue: jest.fn().mockImplementation((props) => { + return `${props.colIndex}:${props.rowIndex}`; + }), + renderFlyoutHeader: jest.fn(), + renderFlyoutBody: jest.fn(), + renderFlyoutFooter: jest.fn(), + renderActionsCell: () => ( + + {}} + size="s" + data-test-subj="testAction" + aria-label="Test action" + /> + + ), +} as RenderContext; + +export const mockDataGridProps: Partial = { + pageSizeOptions: [1, 10, 20, 50, 100], + leadingControlColumns: [], + trailingControlColumns: [], + visibleColumns: mockColumns.map((c) => c.id), + 'data-test-subj': 'testTable', + onToggleColumn: jest.fn(), + onResetColumns: jest.fn(), + onChangeVisibleColumns: jest.fn(), + query: {}, + sort: [], + alertsQuerySnapshot: { request: [], response: [] }, + onSortChange: jest.fn(), + onChangePageIndex: jest.fn(), + onChangePageSize: jest.fn(), + getBulkActions: () => [ + { + id: 0, + items: [ + { + label: 'Fake Bulk Action', + key: 'fakeBulkAction', + 'data-test-subj': 'fake-bulk-action', + disableOnQuery: false, + onClick: () => {}, + }, + ], + }, + ], + fieldsBrowserOptions: { + createFieldButton: () => , + }, + casesConfiguration: { featureId: 'test-feature-id', owner: ['test-owner'] }, +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.test.tsx new file mode 100644 index 0000000000000..a89e654aac394 --- /dev/null +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.test.tsx @@ -0,0 +1,479 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { FunctionComponent, useMemo, useReducer } from 'react'; +import { fireEvent, render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import { AlertsDataGrid } from './alerts_data_grid'; +import { BulkActionsState, AdditionalContext, RenderContext } from '../../../types'; +import { EuiButtonIcon, EuiDataGridColumnCellAction, EuiFlexItem } from '@elastic/eui'; +import { bulkActionsReducer } from './bulk_actions/reducer'; +import { createAppMockRenderer, getJsDomPerformanceFix } from '../test_utils'; +import { createCasesServiceMock } from './index.mock'; +import { useCaseViewNavigation } from './cases/use_case_view_navigation'; +import { act } from 'react-dom/test-utils'; +import { AlertsTableContextProvider } from './contexts/alerts_table_context'; +import { + TestAlertsDataGridProps, + BaseAlertsDataGridProps, + mockDataGridProps, + mockRenderContext, + mockColumns, + mockAlerts, + mockBulkActionsState, +} from './alerts_data_grid.mock'; +import { + CELL_ACTIONS_EXPAND_TEST_ID, + CELL_ACTIONS_POPOVER_TEST_ID, + FIELD_BROWSER_BTN_TEST_ID, + FIELD_BROWSER_CUSTOM_CREATE_BTN_TEST_ID, + FIELD_BROWSER_TEST_ID, +} from './constants'; + +jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ + useUiSetting$: jest.fn((value: string) => ['0,0']), +})); + +// useKibana mock +const mockCaseService = createCasesServiceMock(); +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + + return { + ...original, + useKibana: () => ({ + services: { + cases: mockCaseService, + notifications: { + toasts: { + addDanger: jest.fn(), + addSuccess: jest.fn(), + }, + }, + }, + }), + }; +}); + +jest.mock('./cases/use_case_view_navigation'); + +const cellActionOnClickMockedFn = jest.fn(); + +const { fix, cleanup } = getJsDomPerformanceFix(); + +beforeAll(() => { + fix(); +}); + +afterAll(() => { + cleanup(); +}); + +describe('AlertsDataGrid', () => { + const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; + useCaseViewNavigationMock.mockReturnValue({ navigateToCaseView: jest.fn() }); + + const TestComponent: React.FunctionComponent< + Omit & { + initialBulkActionsState?: BulkActionsState; + renderContext?: Partial>; + } + > = (props) => { + const { AppWrapper } = useMemo( + () => createAppMockRenderer({ queryClientContext: AlertsQueryContext }), + [] + ); + + const bulkActionsStore = useReducer( + bulkActionsReducer, + props.initialBulkActionsState || mockBulkActionsState + ); + const renderContext = useMemo( + () => ({ + ...mockRenderContext, + bulkActionsStore, + ...props.renderContext, + }), + [bulkActionsStore, props.renderContext] + ); + + return ( + + + + + + ); + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('Alerts table UI', () => { + it('should support sorting', async () => { + const { container } = render(); + await userEvent.click(container.querySelector('.euiDataGridHeaderCell__button')!, { + pointerEventsCheck: 0, + }); + + await waitForEuiPopoverOpen(); + + await userEvent.click( + screen.getByTestId(`dataGridHeaderCellActionGroup-${mockColumns[0].id}`), + { + pointerEventsCheck: 0, + } + ); + + await userEvent.click(screen.getByTitle('Sort A-Z'), { + pointerEventsCheck: 0, + }); + + expect(mockDataGridProps.onSortChange).toHaveBeenCalledWith([ + { direction: 'asc', id: 'kibana.alert.rule.name' }, + ]); + }); + + it('should support pagination', async () => { + render(); + await userEvent.click(screen.getByTestId('pagination-button-1'), { + pointerEventsCheck: 0, + }); + + expect(mockDataGridProps.onChangePageIndex).toHaveBeenCalledWith(1); + }); + + it('should show when it was updated', () => { + render(); + expect(screen.getByTestId('toolbar-updated-at')).not.toBe(null); + }); + + it('should show alerts count', () => { + render(); + expect(screen.getByTestId('toolbar-alerts-count')).not.toBe(null); + }); + + it('should show alert status', () => { + render( + + ); + expect(screen.queryAllByTestId('alertLifecycleStatusBadge')[0].textContent).toEqual( + 'Flapping' + ); + expect(screen.queryAllByTestId('alertLifecycleStatusBadge')[1].textContent).toEqual('Active'); + expect(screen.queryAllByTestId('alertLifecycleStatusBadge')[2].textContent).toEqual( + 'Recovered' + ); + expect(screen.queryAllByTestId('alertLifecycleStatusBadge')[3].textContent).toEqual( + 'Recovered' + ); + }); + + describe('leading control columns', () => { + it('should render other leading controls', () => { + const props: TestAlertsDataGridProps = { + ...mockDataGridProps, + leadingControlColumns: [ + { + id: 'selection', + width: 67, + headerCellRender: () => Test header, + rowCellRender: () =>

Test cell

, + }, + ], + }; + render(); + expect(screen.queryByTestId('testHeader')).not.toBe(null); + expect(screen.queryByTestId('testCell')).not.toBe(null); + }); + }); + + describe('actions column', () => { + it('should render custom actions cells', () => { + render( + ( + <> + + {}} + size="s" + data-test-subj="testAction" + aria-label="testActionLabel" + /> + + + {}} + size="s" + data-test-subj="testAction2" + aria-label="testActionLabel2" + /> + + + ), + }} + /> + ); + expect(screen.queryByTestId('testAction')).toBeInTheDocument(); + expect(screen.queryByTestId('testAction2')).toBeInTheDocument(); + expect(screen.queryByTestId('expandColumnCellOpenFlyoutButton-0')).not.toBeInTheDocument(); + }); + + it('should render no action column if there is neither the action nor the expand action config is set', () => { + render( + + ); + expect(screen.queryByTestId('expandColumnHeaderLabel')).not.toBeInTheDocument(); + expect(screen.queryByTestId('expandColumnCellOpenFlyoutButton')).not.toBeInTheDocument(); + }); + + describe('row loading state on action', () => { + type ExtractFunctionComponent = T extends FunctionComponent ? T : never; + const mockRenderActionsCell = jest.fn( + mockRenderContext.renderActionsCell as ExtractFunctionComponent< + typeof mockRenderContext.renderActionsCell + > + ); + const props: TestAlertsDataGridProps = { + ...mockDataGridProps, + actionsColumnWidth: 124, + renderContext: { + pageSize: 10, + renderActionsCell: mockRenderActionsCell, + }, + }; + + it('should show the row loader when callback triggered', async () => { + render(); + fireEvent.click((await screen.findAllByTestId('testAction'))[0]); + + // the callback given to our clients to run when they want to update the loading state + act(() => { + mockRenderActionsCell.mock.calls[0][0].setIsActionLoading!(true); + }); + + expect(await screen.findAllByTestId('row-loader')).toHaveLength(1); + const selectedOptions = await screen.findAllByTestId('dataGridRowCell'); + + // first row, first column + expect(within(selectedOptions[0]).getByLabelText('Loading')).toBeDefined(); + expect( + within(selectedOptions[0]).queryByTestId('bulk-actions-row-cell') + ).not.toBeInTheDocument(); + + // second row, first column + expect(within(selectedOptions[6]).queryByLabelText('Loading')).not.toBeInTheDocument(); + expect(within(selectedOptions[6]).getByTestId('bulk-actions-row-cell')).toBeDefined(); + }); + + it('should show the row loader when callback triggered with false', async () => { + const initialBulkActionsState = { + ...mockBulkActionsState, + rowSelection: new Map([[0, { isLoading: true }]]), + }; + + render(); + fireEvent.click((await screen.findAllByTestId('testAction'))[0]); + + // the callback given to our clients to run when they want to update the loading state + act(() => { + mockRenderActionsCell.mock.calls[0][0].setIsActionLoading!(false); + }); + + expect(screen.queryByTestId('row-loader')).not.toBeInTheDocument(); + }); + }); + }); + + describe('cell Actions', () => { + const mockGetCellActionsForColumn = jest.fn( + (columnId: string): EuiDataGridColumnCellAction[] => [ + ({ rowIndex, Component }) => { + const label = 'Fake Cell First Action'; + return ( + cellActionOnClickMockedFn(columnId, rowIndex)} + data-test-subj={'fake-cell-first-action'} + iconType="refresh" + aria-label={label} + /> + ); + }, + ] + ); + const props: TestAlertsDataGridProps = { + ...mockDataGridProps, + cellActionsOptions: { + getCellActionsForColumn: mockGetCellActionsForColumn, + visibleCellActions: 2, + disabledCellActions: [], + }, + }; + + it('should render cell actions on hover', async () => { + render(); + + const reasonFirstRow = (await screen.findAllByTestId('dataGridRowCell'))[3]; + + fireEvent.mouseOver(reasonFirstRow); + + expect(await screen.findByTestId('fake-cell-first-action')).toBeInTheDocument(); + }); + + it('should render expandable cell actions', async () => { + render(); + const reasonFirstRow = (await screen.findAllByTestId('dataGridRowCell'))[3]; + + fireEvent.mouseOver(reasonFirstRow); + + expect(await screen.findByTestId(CELL_ACTIONS_EXPAND_TEST_ID)).toBeVisible(); + + fireEvent.click(await screen.findByTestId(CELL_ACTIONS_EXPAND_TEST_ID)); + + expect(await screen.findByTestId(CELL_ACTIONS_POPOVER_TEST_ID)).toBeVisible(); + expect(await screen.findAllByLabelText(/fake cell first action/i)).toHaveLength(2); + }); + }); + + describe('Fields browser', () => { + it('fields browser is working correctly', async () => { + render( + + ); + + const fieldBrowserBtn = screen.getByTestId(FIELD_BROWSER_BTN_TEST_ID); + expect(fieldBrowserBtn).toBeVisible(); + + fireEvent.click(fieldBrowserBtn); + + expect(await screen.findByTestId(FIELD_BROWSER_TEST_ID)).toBeVisible(); + + expect(await screen.findByTestId(FIELD_BROWSER_CUSTOM_CREATE_BTN_TEST_ID)).toBeVisible(); + }); + + it('syncs the columns state correctly between the column selector and the field selector', async () => { + const columnToHide = mockColumns[0]; + render( + + ); + + const fieldBrowserBtn = await screen.findByTestId(FIELD_BROWSER_BTN_TEST_ID); + const columnSelectorBtn = await screen.findByTestId('dataGridColumnSelectorButton'); + + // Open the column visibility selector and hide the column + fireEvent.click(columnSelectorBtn); + const columnVisibilityToggle = await screen.findByTestId( + `dataGridColumnSelectorToggleColumnVisibility-${columnToHide.id}` + ); + fireEvent.click(columnVisibilityToggle); + + // Open the field browser + fireEvent.click(fieldBrowserBtn); + expect(await screen.findByTestId(FIELD_BROWSER_TEST_ID)).toBeVisible(); + + // The column should be checked in the field browser, independent of its visibility status + const columnCheckbox: HTMLInputElement = await screen.findByTestId( + `field-${columnToHide.id}-checkbox` + ); + expect(columnCheckbox).toBeChecked(); + }); + }); + + describe('cases column', () => { + const props: TestAlertsDataGridProps = { + ...mockDataGridProps, + renderContext: { + pageSize: mockAlerts.length, + }, + }; + + it('should show the cases column', async () => { + render(); + expect(await screen.findByText('Cases')).toBeInTheDocument(); + }); + + it('should show the cases titles correctly', async () => { + render(); + expect(await screen.findByText('Test case')).toBeInTheDocument(); + expect(await screen.findByText('Test case 2')).toBeInTheDocument(); + }); + + it('show loading skeleton if it loads cases', async () => { + render( + + ); + + expect((await screen.findAllByTestId('cases-cell-loading')).length).toBe(4); + }); + + it('shows the cases tooltip', async () => { + render(); + expect(await screen.findByText('Test case')).toBeInTheDocument(); + + await userEvent.hover(screen.getByText('Test case')); + + expect(await screen.findByTestId('cases-components-tooltip')).toBeInTheDocument(); + }); + }); + + describe('dynamic row height mode', () => { + it('should render a non-virtualized grid body when the dynamicRowHeight option is on', async () => { + const { container } = render(); + + expect(container.querySelector('.euiDataGrid__customRenderBody')).toBeTruthy(); + }); + + it('should render a virtualized grid body when the dynamicRowHeight option is off', async () => { + const { container } = render(); + + expect(container.querySelector('.euiDataGrid__virtualized')).toBeTruthy(); + }); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx new file mode 100644 index 0000000000000..9d6a8c062d142 --- /dev/null +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx @@ -0,0 +1,680 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { + ComponentProps, + FC, + lazy, + memo, + PropsWithChildren, + Suspense, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { + EuiCodeBlock, + EuiDataGrid, + EuiDataGridControlColumn, + EuiDataGridProps, + EuiDataGridStyle, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSkeletonText, + EuiSpacer, + EuiText, + RenderCellValue, + tint, + useEuiTheme, +} from '@elastic/eui'; +import styled from '@emotion/styled'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import { EuiDataGridCellPopoverElementProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { BulkActionsCell } from './bulk_actions/components/row_cell'; +import { BulkActionsHeader } from './bulk_actions/components'; +import { useAlertsTableContext } from './contexts/alerts_table_context'; +import { useBulkActions, useSorting } from './hooks'; +import { + AdditionalContext, + Alert, + AlertsDataGridProps, + AlertsTableProps, + BulkActionsVerbs, + CellActionsOptions, + FetchAlertData, +} from '../../../types'; +import { ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL } from './translations'; +import { useGetToolbarVisibility } from './toolbar'; +import { InspectButtonContainer } from './toolbar/components/inspect'; +import { SystemCellId } from './types'; +import { SystemCellFactory, systemCells } from './cells'; +import { typedMemo } from './utils'; +import type { AlertsFlyout as AlertsFlyoutType } from './alerts_flyout/alerts_flyout'; +import { ErrorBoundary } from '../common/components/error_boundary'; + +const AlertsFlyout = lazy(() => import('./alerts_flyout')) as typeof AlertsFlyoutType; + +const DefaultGridStyle: EuiDataGridStyle = { + border: 'none', + header: 'underline', + fontSize: 's', +}; + +const defaultCellActionsOptions: CellActionsOptions = { + getCellActionsForColumn: () => [], + disabledCellActions: [], +}; +const DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50, 100]; +const DEFAULT_ACTIONS_COLUMNS_WIDTH = 75; + +const stableMappedRowClasses: EuiDataGridStyle['rowClasses'] = {}; + +interface BasicCellValueProps { + columnId: string; + alert: Alert; +} + +const BasicCellValue = memo(({ alert, columnId }: BasicCellValueProps) => { + const value = alert[columnId]; + if (Array.isArray(value)) { + return <>{value.length ? value.join() : '--'}; + } + return <>{value}; +}); + +const ControlColumnHeaderRenderCell = memo(() => { + return ( + + {ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL} + + ); +}); + +const CustomCellWrapper = ({ children }: PropsWithChildren) => ( + + {children} + +); + +const isSystemCell = (columnId: string): columnId is SystemCellId => { + return systemCells.includes(columnId as SystemCellId); +}; + +// Here we force the error callout to be the same height as the cell content +// so that the error detail gets hidden in the overflow area and only shown in +// the cell popover +const errorCalloutStyles = css` + height: 1lh; +`; + +/** + * An error callout that displays the error stack in a code block + */ +const ViewError = ({ error }: { error: Error }) => ( + <> + + + + + + + + + + + + + + {error.stack} + +); + +const Row = styled.div` + display: flex; + min-width: fit-content; +`; + +type CustomGridBodyProps = Pick< + Parameters>['0'], + 'Cell' | 'visibleColumns' +> & { + alertsData: FetchAlertData['oldAlertsData']; + isLoading: boolean; + pageIndex: number; + pageSize: number; + actualGridStyle: EuiDataGridStyle; + stripes?: boolean; +}; + +const CellValueHost: AlertsTableProps['renderCellValue'] = (props) => { + const { + columnId, + renderCellValue: CellValue, + isLoading, + alerts, + oldAlertsData, + ecsAlertsData, + cases, + maintenanceWindows, + showAlertStatusWithFlapping, + casesConfig, + rowIndex, + pageIndex, + pageSize, + } = props; + const idx = rowIndex - pageSize * pageIndex; + const alert = alerts[idx]; + const legacyAlert = oldAlertsData[idx]; + const ecsAlert = ecsAlertsData[idx]; + if (isSystemCell(columnId)) { + return ( + + ); + } else if (alert) { + if (CellValue) { + return ( + + + + ); + } else { + return ; + } + } else if (isLoading) { + return ; + } + return null; +}; + +const CellPopoverHost = (props: EuiDataGridCellPopoverElementProps) => { + const { rowIndex, DefaultCellPopover } = props; + const renderContext = useAlertsTableContext(); + const { pageSize, pageIndex, alerts, renderCellPopover: CellPopover } = renderContext; + + const idx = rowIndex - pageSize * pageIndex; + const alert = alerts[idx]; + if (alert && CellPopover) { + return ( + + + + ); + } + + return ; +}; + +const CustomGridBody = memo( + ({ + alertsData, + isLoading, + pageIndex, + pageSize, + actualGridStyle, + visibleColumns, + Cell, + stripes, + }: CustomGridBodyProps) => { + return ( + <> + {alertsData + .concat(isLoading ? Array.from({ length: pageSize - alertsData.length }) : []) + .map((_row, rowIndex) => ( + + {visibleColumns.map((_col, colIndex) => ( + + ))} + + ))} + + ); + } +); + +export const AlertsDataGrid = typedMemo( + (props: AlertsDataGridProps) => { + const { + ruleTypeIds, + query, + visibleColumns, + onToggleColumn, + onResetColumns, + onChangeVisibleColumns, + onColumnResize, + showInspectButton = false, + leadingControlColumns: additionalLeadingControlColumns, + trailingControlColumns, + onSortChange, + sort: sortingFields, + rowHeightsOptions, + dynamicRowHeight, + alertsQuerySnapshot, + additionalToolbarControls, + toolbarVisibility: toolbarVisibilityProp, + shouldHighlightRow, + renderContext, + hideBulkActions, + casesConfiguration, + flyoutAlertIndex, + setFlyoutAlertIndex, + onPaginateFlyout, + onChangePageSize, + onChangePageIndex, + actionsColumnWidth = DEFAULT_ACTIONS_COLUMNS_WIDTH, + getBulkActions, + fieldsBrowserOptions, + cellActionsOptions, + pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, + height, + ...euiDataGridProps + } = props; + const { + isLoading, + alerts, + alertsCount, + isLoadingAlerts, + oldAlertsData, + browserFields, + renderActionsCell: ActionsCell, + pageIndex, + pageSize, + refresh: refreshQueries, + columns, + dataGridRef, + } = renderContext; + + const { colorMode } = useEuiTheme(); + + const [activeRowClasses, setActiveRowClasses] = useState< + NonNullable + >({}); + + const { sortingColumns, onSort } = useSorting(onSortChange, visibleColumns, sortingFields); + + const bulkActionArgs = useMemo( + () => ({ + ruleTypeIds, + query, + alertsCount: alerts.length, + casesConfig: casesConfiguration, + getBulkActions, + refresh: refreshQueries, + hideBulkActions, + }), + [ + ruleTypeIds, + query, + alerts.length, + casesConfiguration, + getBulkActions, + refreshQueries, + hideBulkActions, + ] + ); + + const { + isBulkActionsColumnActive, + bulkActionsState, + bulkActions, + setIsBulkActionsLoading, + clearSelection, + updateBulkActionsState, + } = useBulkActions(bulkActionArgs); + + const refresh = useCallback(() => { + refreshQueries(); + clearSelection(); + }, [clearSelection, refreshQueries]); + + const toolbarVisibilityArgs = useMemo(() => { + return { + bulkActions, + alertsCount, + rowSelection: bulkActionsState.rowSelection, + alerts, + isLoading, + columnIds: columns.map((column) => column.id), + onToggleColumn, + onResetColumns, + browserFields, + additionalToolbarControls, + setIsBulkActionsLoading, + clearSelection, + refresh, + fieldsBrowserOptions, + alertsQuerySnapshot, + showInspectButton, + toolbarVisibilityProp, + }; + }, [ + bulkActions, + alertsCount, + bulkActionsState.rowSelection, + alerts, + isLoading, + columns, + onToggleColumn, + onResetColumns, + browserFields, + additionalToolbarControls, + setIsBulkActionsLoading, + clearSelection, + refresh, + fieldsBrowserOptions, + alertsQuerySnapshot, + showInspectButton, + toolbarVisibilityProp, + ]); + + const toolbarVisibility = useGetToolbarVisibility(toolbarVisibilityArgs); + + const customActionsColumn: EuiDataGridControlColumn | undefined = useMemo(() => { + if (ActionsCell) { + const RowCellRender: EuiDataGridControlColumn['rowCellRender'] = (_props) => { + const idx = _props.rowIndex - _props.pageSize * _props.pageIndex; + const alert = _props.alerts[idx]; + const legacyAlert = _props.oldAlertsData[idx]; + const ecsAlert = _props.ecsAlertsData[idx]; + const setIsActionLoading = useCallback( + (_isLoading: boolean = true) => { + updateBulkActionsState({ + action: BulkActionsVerbs.updateRowLoadingState, + rowIndex: _props.visibleRowIndex, + isLoading: _isLoading, + }); + }, + [_props.visibleRowIndex] + ); + + if (!alert) { + return null; + } + + return ( + + )} + alert={alert} + legacyAlert={legacyAlert} + ecsAlert={ecsAlert} + setIsActionLoading={setIsActionLoading} + /> + + ); + }; + return { + id: 'expandColumn', + width: actionsColumnWidth, + headerCellRender: ControlColumnHeaderRenderCell, + rowCellRender: RowCellRender, + }; + } + }, [ActionsCell, actionsColumnWidth, updateBulkActionsState]); + + const leadingControlColumns: EuiDataGridControlColumn[] | undefined = useMemo(() => { + const controlColumns = [ + ...(additionalLeadingControlColumns ?? []), + ...(isBulkActionsColumnActive + ? [ + { + id: 'bulkActions', + width: 30, + headerCellRender: BulkActionsHeader, + rowCellRender: BulkActionsCell, + }, + ] + : []), + ...(customActionsColumn ? [customActionsColumn] : []), + ]; + if (controlColumns.length) { + return controlColumns; + } + }, [additionalLeadingControlColumns, isBulkActionsColumnActive, customActionsColumn]); + + const flyoutRowIndex = flyoutAlertIndex + pageIndex * pageSize; + useEffect(() => { + // Row classes do not deal with visible row indices, so we need to handle page offset + setActiveRowClasses({ + [flyoutRowIndex]: 'alertsTableActiveRow', + }); + }, [flyoutRowIndex]); + + const handleFlyoutClose = useCallback(() => setFlyoutAlertIndex(-1), [setFlyoutAlertIndex]); + + const dataGridPagination = useMemo( + () => ({ + pageIndex, + pageSize, + pageSizeOptions, + onChangeItemsPerPage: onChangePageSize, + onChangePage: onChangePageIndex, + }), + [onChangePageIndex, onChangePageSize, pageIndex, pageSize, pageSizeOptions] + ); + + const { getCellActionsForColumn, visibleCellActions, disabledCellActions } = + cellActionsOptions ?? defaultCellActionsOptions; + + const columnsWithCellActions = useMemo(() => { + if (getCellActionsForColumn) { + return columns.map((col, idx) => ({ + ...col, + ...(!(disabledCellActions ?? []).includes(col.id) + ? { + cellActions: getCellActionsForColumn(col.id, idx) ?? [], + visibleCellActions, + } + : {}), + })); + } + return columns; + }, [getCellActionsForColumn, columns, disabledCellActions, visibleCellActions]); + + // Update highlighted rows when alerts or pagination changes + const highlightedRowClasses = useMemo(() => { + if (shouldHighlightRow) { + const emptyShouldHighlightRow: EuiDataGridStyle['rowClasses'] = {}; + return alerts.reduce>( + (rowClasses, alert, index) => { + if (shouldHighlightRow(alert)) { + rowClasses[index + pageIndex * pageSize] = 'alertsTableHighlightedRow'; + } + + return rowClasses; + }, + emptyShouldHighlightRow + ); + } else { + return stableMappedRowClasses; + } + }, [shouldHighlightRow, alerts, pageIndex, pageSize]); + + const mergedGridStyle = useMemo(() => { + const propGridStyle: NonNullable = props.gridStyle ?? {}; + // Merges default row classes, custom ones and adds the active row class style + return { + ...DefaultGridStyle, + ...propGridStyle, + rowClasses: { + // We're spreadind the highlighted row classes first, so that the active + // row classed can override the highlighted row classes. + ...highlightedRowClasses, + ...activeRowClasses, + }, + }; + }, [activeRowClasses, highlightedRowClasses, props.gridStyle]); + + // Merges the default grid style with the grid style that comes in through props. + const actualGridStyle = useMemo(() => { + const propGridStyle: NonNullable = props.gridStyle ?? {}; + // If ANY additional rowClasses have been provided, we need to merge them with our internal ones + if (propGridStyle.rowClasses) { + // Get all row indices with a rowClass. + const mergedKeys = [ + ...Object.keys(mergedGridStyle.rowClasses || {}), + ...Object.keys(propGridStyle.rowClasses || {}), + ]; + // Deduplicate keys to avoid extra iterations + const dedupedKeys = Array.from(new Set(mergedKeys)); + + // For each index, merge row classes + mergedGridStyle.rowClasses = dedupedKeys.reduce< + NonNullable + >((rowClasses, key) => { + const intKey = parseInt(key, 10); + // Use internal row classes over custom row classes. + rowClasses[intKey] = + mergedGridStyle.rowClasses?.[intKey] || propGridStyle.rowClasses?.[intKey] || ''; + return rowClasses; + }, {}); + } + return mergedGridStyle; + }, [props.gridStyle, mergedGridStyle]); + + const renderCustomGridBody = useCallback>( + ({ visibleColumns: _visibleColumns, Cell, headerRow, footerRow }) => ( + <> + {headerRow} + + {footerRow} + + ), + [ + actualGridStyle, + oldAlertsData, + pageIndex, + pageSize, + isLoadingAlerts, + props.gridStyle?.stripes, + ] + ); + + const sortProps = useMemo(() => { + return { columns: sortingColumns, onSort }; + }, [sortingColumns, onSort]); + + const columnVisibility = useMemo(() => { + return { visibleColumns, setVisibleColumns: onChangeVisibleColumns }; + }, [visibleColumns, onChangeVisibleColumns]); + + const rowStyles = useMemo( + () => css` + .alertsTableHighlightedRow { + background-color: ${euiThemeVars.euiColorHighlight}; + } + + .alertsTableActiveRow { + background-color: ${colorMode === 'LIGHT' + ? tint(euiThemeVars.euiColorLightShade, 0.5) + : euiThemeVars.euiColorLightShade}; + } + `, + [colorMode] + ); + + return ( + +
+ + {flyoutAlertIndex > -1 && ( + + )} + + {alertsCount > 0 && ( + + )} +
+
+ ); + } +); + +(AlertsDataGrid as FC).displayName = 'AlertsDataGrid'; + +// eslint-disable-next-line import/no-default-export +export { AlertsDataGrid as default }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx index 7bfb4773cca39..7ce22ab1cad53 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx @@ -4,11 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { ComponentProps } from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; import { AlertsFlyout } from './alerts_flyout'; -import { Alert, AlertsField } from '../../../../types'; +import { Alert, AlertsField, FlyoutSectionRenderer } from '../../../../types'; + +type FlyoutProps = ComponentProps; const onClose = jest.fn(); const onPaginate = jest.fn(); @@ -19,36 +21,33 @@ const props = { _id: '0123456789', _index: '.alerts-default', } as unknown as Alert, - alertsTableConfiguration: { - id: 'test', - casesFeatureId: 'testCases', - columns: [ - { - id: AlertsField.name, - displayAsText: 'Name', - initialWidth: 150, - }, - { - id: AlertsField.reason, - displayAsText: 'Reason', - initialWidth: 250, - }, - ], - useInternalFlyout: () => ({ - body: () =>

Internal flyout body

, - header: null, - footer: () => null, - }), - getRenderCellValue: jest.fn().mockImplementation((rcvProps) => { - return `${rcvProps.colIndex}:${rcvProps.rowIndex}`; - }), - }, + tableId: 'test', + columns: [ + { + id: AlertsField.name, + displayAsText: 'Name', + initialWidth: 150, + }, + { + id: AlertsField.reason, + displayAsText: 'Reason', + initialWidth: 250, + }, + ], + renderCellValue: jest.fn((rcvProps) => { + return ( + <> + `${rcvProps.colIndex}:${rcvProps.rowIndex}` + + ); + }), + renderFlyoutBody: () =>

Internal flyout body

, flyoutIndex: 0, alertsCount: 4, isLoading: false, onClose, onPaginate, -}; +} as unknown as FlyoutProps; describe('AlertsFlyout', () => { afterEach(() => { @@ -61,25 +60,15 @@ describe('AlertsFlyout', () => { await nextTick(); wrapper.update(); }); - expect(wrapper.find('h3').first().text()).toBe('Internal flyout body'); + expect(wrapper.find('[data-test-subj="test-flyout-body"]').first().text()).toBe( + 'Internal flyout body' + ); }); - const base = { - body: () => null, - header: () => null, - footer: () => null, - }; - it(`should use header from useInternalFlyout configuration`, async () => { - const customProps = { + it(`should use header from the alerts table props`, async () => { + const customProps: FlyoutProps = { ...props, - alertsTableConfiguration: { - ...props.alertsTableConfiguration, - useInternalFlyout: () => ({ - ...base, - header: () =>

Header

, - footer: () => null, - }), - }, + renderFlyoutHeader: () =>

Header

, }; const wrapper = mountWithIntl(); await act(async () => { @@ -89,16 +78,10 @@ describe('AlertsFlyout', () => { expect(wrapper.find('h4').first().text()).toBe('Header'); }); - it(`should use body from useInternalFlyout configuration`, async () => { - const customProps = { + it(`should use body the alerts table props`, async () => { + const customProps: FlyoutProps = { ...props, - alertsTableConfiguration: { - ...props.alertsTableConfiguration, - useInternalFlyout: () => ({ - ...base, - body: () =>
Body
, - }), - }, + renderFlyoutBody: () =>
Body
, }; const wrapper = mountWithIntl(); await act(async () => { @@ -108,16 +91,10 @@ describe('AlertsFlyout', () => { expect(wrapper.find('h5').first().text()).toBe('Body'); }); - it(`should use footer from useInternalFlyout configuration`, async () => { - const customProps = { + it(`should use footer from the alerts table props`, async () => { + const customProps: FlyoutProps = { ...props, - alertsTableConfiguration: { - ...props.alertsTableConfiguration, - useInternalFlyout: () => ({ - ...base, - footer: () =>
Footer
, - }), - }, + renderFlyoutFooter: () =>
Footer
, }; const wrapper = mountWithIntl(); await act(async () => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx index a25af7cb5386e..875ad15c6f708 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { Suspense, lazy, useCallback, useMemo, useRef, useEffect } from 'react'; +import React, { Suspense, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlyout, @@ -15,9 +15,10 @@ import { EuiPagination, EuiProgress, } from '@elastic/eui'; -import type { Alert, AlertsTableConfigurationRegistry } from '../../../../types'; +import usePrevious from 'react-use/lib/usePrevious'; +import { DefaultAlertsFlyoutHeader } from './default_alerts_flyout'; +import { FlyoutSectionRenderer } from '../../../../types'; -const AlertsFlyoutHeader = lazy(() => import('./alerts_flyout_header')); const PAGINATION_LABEL = i18n.translate( 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel', { @@ -25,93 +26,63 @@ const PAGINATION_LABEL = i18n.translate( } ); -function usePrevious(alert: Alert) { - const ref = useRef(null); - useEffect(() => { - if (alert) { - ref.current = alert; - } - }); - return ref.current; -} - -interface AlertsFlyoutProps { - alert: Alert; - alertsTableConfiguration: AlertsTableConfigurationRegistry; - flyoutIndex: number; - alertsCount: number; - isLoading: boolean; - onClose: () => void; - onPaginate: (pageIndex: number) => void; - id?: string; -} -export const AlertsFlyout: React.FunctionComponent = ({ - alert, - alertsTableConfiguration, - flyoutIndex, - alertsCount, - isLoading, - onClose, - onPaginate, - id, -}: AlertsFlyoutProps) => { +export const AlertsFlyout: FlyoutSectionRenderer = ({ alert, ...renderContext }) => { const { - header: Header, - body: Body, - footer: Footer, - } = alertsTableConfiguration?.useInternalFlyout?.() ?? { - header: AlertsFlyoutHeader, - body: null, - footer: null, - }; + flyoutIndex, + alertsCount, + onClose, + onPaginate, + isLoading, + renderFlyoutHeader: Header = DefaultAlertsFlyoutHeader, + renderFlyoutBody: Body, + renderFlyoutFooter: Footer, + } = renderContext; const prevAlert = usePrevious(alert); - const passedProps = useMemo( + const props = useMemo( () => ({ + ...renderContext, + // Show the previous alert while loading the next one alert: alert === undefined && prevAlert != null ? prevAlert : alert, - id, - isLoading, }), // eslint-disable-next-line react-hooks/exhaustive-deps - [alert, id, isLoading] + [alert, renderContext] ); - const FlyoutBody = useCallback( + const FlyoutHeader = useCallback( () => - Body ? ( + Header ? ( - +
) : null, - [Body, passedProps] + [Header, props] ); - const FlyoutFooter = useCallback( + const FlyoutBody = useCallback( () => - Footer ? ( + Body ? ( -
+ ) : null, - [Footer, passedProps] + [Body, props] ); - const FlyoutHeader = useCallback( + const FlyoutFooter = useCallback( () => - Header ? ( + Footer ? ( -
+
) : null, - [Header, passedProps] + [Footer, props] ); return ( {isLoading && } - - - + diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout_header.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout_header.tsx deleted file mode 100644 index 2418d07eb4db0..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout_header.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import { get } from 'lodash'; -import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; -import { EuiTitle } from '@elastic/eui'; -import { AlertsTableFlyoutBaseProps } from '../../../../types'; - -type Props = AlertsTableFlyoutBaseProps; -const AlertsFlyoutHeader = ({ alert }: Props) => { - return ( - -

{get(alert, ALERT_RULE_NAME)}

-
- ); -}; - -// eslint-disable-next-line import/no-default-export -export default AlertsFlyoutHeader; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx index b0fae16cf365d..85fe3a995f193 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx @@ -8,10 +8,11 @@ import { waitFor } from '@testing-library/react'; import { mount } from 'enzyme'; import type { ReactWrapper } from 'enzyme'; -import React from 'react'; +import React, { ComponentProps } from 'react'; -import { getDefaultAlertFlyout } from './default_alerts_flyout'; import { AlertsTableFlyoutBaseProps } from '../../../..'; +import { DefaultAlertsFlyoutBody } from './default_alerts_flyout'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; const columns = [ { @@ -87,8 +88,16 @@ const tabsData = [ describe('DefaultAlertsFlyout', () => { let wrapper: ReactWrapper; beforeAll(async () => { - const { body: FlyoutBody } = getDefaultAlertFlyout(columns, (_columnId, value) => value)(); - wrapper = mount() as ReactWrapper; + wrapper = mount( + )} + /> + ) as ReactWrapper; await waitFor(() => wrapper.update()); }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx index ac6fd3e68970d..d76e84078bc26 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx @@ -6,106 +6,93 @@ */ import React, { useCallback, useMemo, useState } from 'react'; -import { - type EuiDataGridColumn, - EuiDescriptionList, - EuiPanel, - EuiTabbedContentTab, - EuiTitle, -} from '@elastic/eui'; +import { EuiDescriptionList, EuiPanel, EuiTabbedContentTab, EuiTitle } from '@elastic/eui'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { ScrollableFlyoutTabbedContent, AlertFieldsTable } from '@kbn/alerts-ui-shared'; -import { RegisterFormatter } from '../cells/render_cell_value'; -import { AlertsTableFlyoutBaseProps, AlertTableFlyoutComponent } from '../../../..'; +import { FlyoutSectionRenderer } from '../../../../types'; +import { getAlertFormatters } from '../cells/render_cell_value'; +import { defaultAlertsTableColumns } from '../configuration'; -const FlyoutHeader: AlertTableFlyoutComponent = ({ alert }: AlertsTableFlyoutBaseProps) => { - const name = alert[ALERT_RULE_NAME]; +export const DefaultAlertsFlyoutHeader: FlyoutSectionRenderer = ({ alert }) => { return ( -

{name}

+

{alert[ALERT_RULE_NAME] ?? 'Unknown'}

); }; type TabId = 'overview' | 'table'; -export const getDefaultAlertFlyout = - (columns: EuiDataGridColumn[], formatter: RegisterFormatter) => () => { - const FlyoutBody: AlertTableFlyoutComponent = ({ alert }: AlertsTableFlyoutBaseProps) => { - const overviewTab = useMemo( - () => ({ - id: 'overview', - 'data-test-subj': 'overviewTab', - name: i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.overview', - { - defaultMessage: 'Overview', - } - ), - content: ( - - { - const value = get(alert, column.id)?.[0]; +export const DefaultAlertsFlyoutBody: FlyoutSectionRenderer = ({ + alert, + fieldFormats, + columns, +}) => { + const formatColumnValue = useMemo(() => getAlertFormatters(fieldFormats), [fieldFormats]); + const overviewTab = useMemo( + () => ({ + id: 'overview', + 'data-test-subj': 'overviewTab', + name: i18n.translate('xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.overview', { + defaultMessage: 'Overview', + }), + content: ( + + { + const value = get(alert, column.id)?.[0]; - return { - title: column.displayAsText as string, - description: value != null ? formatter(column.id, value) : '—', - }; - })} - type="column" - columnWidths={[1, 3]} - /> - - ), - }), - [alert] - ); - - const tableTab = useMemo( - () => ({ - id: 'table', - 'data-test-subj': 'tableTab', - name: i18n.translate('xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.table', { - defaultMessage: 'Table', - }), - content: ( - - - - ), - }), - [alert] - ); + return { + title: column.displayAsText as string, + description: value != null ? formatColumnValue(column.id, value) : '—', + }; + })} + type="column" + columnWidths={[1, 3]} + /> + + ), + }), + [alert, columns, formatColumnValue] + ); - const tabs = useMemo(() => [overviewTab, tableTab], [overviewTab, tableTab]); - const [selectedTabId, setSelectedTabId] = useState('overview'); - const handleTabClick = useCallback( - (tab: EuiTabbedContentTab) => setSelectedTabId(tab.id as TabId), - [] - ); + const tableTab = useMemo( + () => ({ + id: 'table', + 'data-test-subj': 'tableTab', + name: i18n.translate('xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.table', { + defaultMessage: 'Table', + }), + content: ( + + + + ), + }), + [alert] + ); - const selectedTab = useMemo( - () => tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0], - [tabs, selectedTabId] - ); + const tabs = useMemo(() => [overviewTab, tableTab], [overviewTab, tableTab]); + const [selectedTabId, setSelectedTabId] = useState('overview'); + const handleTabClick = useCallback( + (tab: EuiTabbedContentTab) => setSelectedTabId(tab.id as TabId), + [] + ); - return ( - - ); - }; + const selectedTab = useMemo( + () => tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0], + [tabs, selectedTabId] + ); - return { - header: FlyoutHeader, - body: FlyoutBody, - footer: null, - }; - }; + return ( + + ); +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss deleted file mode 100644 index 672ffa2b9d36f..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss +++ /dev/null @@ -1,7 +0,0 @@ -.alertsTableHighlightedRow { - background-color: $euiColorHighlight; -} - -.alertsTableActiveRow { - background-color: tintOrShade($euiColorLightShade, 50%, 0); -} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx index bcd9026992d15..105de06ab8d7e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx @@ -4,125 +4,90 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo, useReducer } from 'react'; -import { identity } from 'lodash'; -import { fireEvent, render, screen, within, waitFor } from '@testing-library/react'; +import React, { FunctionComponent } from 'react'; +import { BehaviorSubject } from 'rxjs'; import userEvent from '@testing-library/user-event'; -import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; -import { - ALERT_RULE_NAME, - ALERT_REASON, - ALERT_FLAPPING, - ALERT_STATUS, - ALERT_CASE_IDS, -} from '@kbn/rule-data-utils'; -import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; -import { AlertsTable } from './alerts_table'; +import { get } from 'lodash'; +import { render, waitFor, screen, act } from '@testing-library/react'; +import { ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS, ALERT_UUID } from '@kbn/rule-data-utils'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; + import { + Alerts, AlertsField, - AlertsTableConfigurationRegistry, - AlertsTableProps, - BulkActionsState, + AlertsDataGridProps, FetchAlertData, - RowSelectionState, - UseCellActions, - Alerts, + AlertsTableProps, + AdditionalContext, + Alert, + RenderContext, } from '../../../types'; -import { EuiButton, EuiButtonIcon, EuiDataGridColumnCellAction, EuiFlexItem } from '@elastic/eui'; -import { bulkActionsReducer } from './bulk_actions/reducer'; -import { BrowserFields } from '@kbn/alerting-types'; -import { getCasesMockMap } from './cases/index.mock'; -import { getMaintenanceWindowMockMap } from './maintenance_windows/index.mock'; -import { createAppMockRenderer, getJsDomPerformanceFix } from '../test_utils'; +import { PLUGIN_ID } from '../../../common/constants'; +import { AlertsTable } from './alerts_table'; +import { AlertsDataGrid } from './alerts_data_grid'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { getCasesMock } from './cases/index.mock'; import { createCasesServiceMock } from './index.mock'; -import { useCaseViewNavigation } from './cases/use_case_view_navigation'; -import { act } from 'react-dom/test-utils'; -import { AlertsTableContext } from './contexts/alerts_table_context'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; - -const mockCaseService = createCasesServiceMock(); - -const mockFieldFormatsRegistry = { - deserialize: jest.fn().mockImplementation(() => ({ - id: 'string', - convert: jest.fn().mockImplementation(identity), - })), -} as unknown as FieldFormatsRegistry; - -jest.mock('@kbn/data-plugin/public'); -jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ - useUiSetting$: jest.fn((value: string) => ['0,0']), -})); - -jest.mock('@kbn/kibana-react-plugin/public', () => { - const original = jest.requireActual('@kbn/kibana-react-plugin/public'); - - return { - ...original, - useKibana: () => ({ - services: { - cases: mockCaseService, - notifications: { - toasts: { - addDanger: jest.fn(), - addSuccess: jest.fn(), - }, - }, - }, - }), - }; -}); - -jest.mock('./cases/use_case_view_navigation'); - +import { getMaintenanceWindowsMock } from './maintenance_windows/index.mock'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { fetchAlertsFields } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'; +import { searchAlerts } from '@kbn/alerts-ui-shared/src/common/apis/search_alerts/search_alerts'; +import { bulkGetCases } from './hooks/apis/bulk_get_cases'; +import { getRulesWithMutedAlerts } from './hooks/apis/get_rules_with_muted_alerts'; +import { bulkGetMaintenanceWindows } from './hooks/apis/bulk_get_maintenance_windows'; +import { testQueryClientConfig } from '@kbn/alerts-ui-shared/src/common/test_utils/test_query_client_config'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { useLicense } from '../../hooks/use_license'; + +type BaseAlertsTableProps = AlertsTableProps; + +jest.mock('@kbn/kibana-utils-plugin/public'); + +// Search alerts mock +jest.mock('@kbn/alerts-ui-shared/src/common/apis/search_alerts/search_alerts'); +const mockSearchAlerts = jest.mocked(searchAlerts); const columns = [ { - id: ALERT_RULE_NAME, + id: AlertsField.name, displayAsText: 'Name', }, { - id: ALERT_REASON, + id: AlertsField.reason, displayAsText: 'Reason', }, - { - id: ALERT_STATUS, - displayAsText: 'Alert status', - }, { id: ALERT_CASE_IDS, displayAsText: 'Cases', }, + { + id: ALERT_MAINTENANCE_WINDOW_IDS, + displayAsText: 'Maintenance Windows', + }, ]; - const alerts = [ { - [ALERT_RULE_NAME]: ['one'], - [ALERT_REASON]: ['two'], - [ALERT_STATUS]: ['active'], - [ALERT_FLAPPING]: [true], + [AlertsField.name]: ['one'], + [AlertsField.reason]: ['two'], + [AlertsField.uuid]: ['1047d115-670d-469e-af7a-86fdd2b2f814'], + [ALERT_UUID]: ['alert-id-1'], [ALERT_CASE_IDS]: ['test-id'], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['test-mw-id-1'], }, { - [ALERT_RULE_NAME]: ['three'], - [ALERT_REASON]: ['four'], - [ALERT_STATUS]: ['active'], - [ALERT_FLAPPING]: [false], + [AlertsField.name]: ['three'], + [AlertsField.reason]: ['four'], + [AlertsField.uuid]: ['bf5f6d63-5afd-48e0-baf6-f28c2b68db46'], [ALERT_CASE_IDS]: ['test-id-2'], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['test-mw-id-2'], }, { - [ALERT_RULE_NAME]: ['five'], - [ALERT_REASON]: ['six'], - [ALERT_STATUS]: ['recovered'], - [ALERT_FLAPPING]: [true], - }, - { - [ALERT_RULE_NAME]: ['seven'], - [ALERT_REASON]: ['eight'], - [ALERT_STATUS]: ['recovered'], - [ALERT_FLAPPING]: [false], + [AlertsField.name]: ['five'], + [AlertsField.reason]: ['six'], + [AlertsField.uuid]: ['1047d115-5afd-469e-baf6-f28c2b68db46'], + [ALERT_CASE_IDS]: [], + [ALERT_MAINTENANCE_WINDOW_IDS]: [], }, ] as unknown as Alerts; - const oldAlertsData = [ [ { @@ -144,8 +109,17 @@ const oldAlertsData = [ value: ['four'], }, ], + [ + { + field: AlertsField.name, + value: ['five'], + }, + { + field: AlertsField.reason, + value: ['six'], + }, + ], ] as FetchAlertData['oldAlertsData']; - const ecsAlertsData = [ [ { @@ -177,103 +151,35 @@ const ecsAlertsData = [ }, }, ], -] as FetchAlertData['ecsAlertsData']; - -const cellActionOnClickMockedFn = jest.fn(); - -const TEST_ID = { - CELL_ACTIONS_POPOVER: 'euiDataGridExpansionPopover', - CELL_ACTIONS_EXPAND: 'euiDataGridCellExpandButton', - FIELD_BROWSER: 'fields-browser-container', - FIELD_BROWSER_BTN: 'show-field-browser', - FIELD_BROWSER_CUSTOM_CREATE_BTN: 'field-browser-custom-create-btn', -}; - -const mockedUseCellActions: UseCellActions = () => { - const mockedGetCellActions = (columnId: string): EuiDataGridColumnCellAction[] => { - const fakeCellAction: EuiDataGridColumnCellAction = ({ rowIndex, Component }) => { - const label = 'Fake Cell First Action'; - return ( - cellActionOnClickMockedFn(columnId, rowIndex)} - data-test-subj={'fake-cell-first-action'} - iconType="refresh" - aria-label={label} - /> - ); - }; - return [fakeCellAction]; - }; - return { - getCellActions: mockedGetCellActions, - visibleCellActions: 2, - disabledCellActions: [], - }; -}; - -const { fix, cleanup } = getJsDomPerformanceFix(); - -beforeAll(() => { - fix(); -}); - -afterAll(() => { - cleanup(); -}); - -describe('AlertsTable', () => { - const alertsTableConfiguration: AlertsTableConfigurationRegistry = { - id: '', - columns, - sort: [], - useInternalFlyout: jest.fn().mockImplementation(() => ({ - header: jest.fn(), - body: jest.fn(), - footer: jest.fn(), - })), - getRenderCellValue: jest.fn().mockImplementation((props) => { - return `${props.colIndex}:${props.rowIndex}`; - }), - useBulkActions: () => [ - { - id: 0, - items: [ - { - label: 'Fake Bulk Action', - key: 'fakeBulkAction', - 'data-test-subj': 'fake-bulk-action', - disableOnQuery: false, - onClick: () => {}, + [ + { + '@timestamp': ['2023-01-26T10:48:49.559Z'], + _id: 'SomeId3', + _index: 'SomeIndex', + kibana: { + alert: { + rule: { + name: ['five'], }, - ], + reason: ['six'], + }, }, - ], - useFieldBrowserOptions: () => { - return { - createFieldButton: () => ( - - ), - }; - }, - useActionsColumn: () => { - return { - renderCustomActionsRow: () => ( - - {}} - size="s" - data-test-subj="fake-action" - aria-label="fake-action" - /> - - ), - }; }, - }; + ], +] as FetchAlertData['ecsAlertsData']; +const mockSearchAlertsResponse: Awaited> = { + alerts, + ecsAlertsData, + oldAlertsData, + total: alerts.length, + querySnapshot: { request: [], response: [] }, +}; +mockSearchAlerts.mockResolvedValue(mockSearchAlertsResponse); - const browserFields: BrowserFields = { +// Alerts fields mock +jest.mock('@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'); +jest.mocked(fetchAlertsFields).mockResolvedValue({ + browserFields: { kibana: { fields: { [AlertsField.uuid]: { @@ -290,500 +196,767 @@ describe('AlertsTable', () => { }, }, }, - }; + }, + fields: [], +}); - const casesMap = getCasesMockMap(); - const maintenanceWindowsMap = getMaintenanceWindowMockMap(); +// Muted alerts mock +jest.mock('./hooks/apis/get_rules_with_muted_alerts'); +jest.mocked(getRulesWithMutedAlerts).mockResolvedValue({ + data: [], +}); - const tableProps: AlertsTableProps = { - alertsTableConfiguration, - cases: { data: casesMap, isLoading: false }, - maintenanceWindows: { data: maintenanceWindowsMap, isLoading: false }, - columns, - deletedEventIds: [], - disabledCellActions: [], - pageSizeOptions: [1, 10, 20, 50, 100], - leadingControlColumns: [], - trailingControlColumns: [], - visibleColumns: columns.map((c) => c.id), - 'data-test-subj': 'testTable', - onToggleColumn: () => {}, - onResetColumns: () => {}, - onChangeVisibleColumns: () => {}, - browserFields, - query: {}, - pageIndex: 0, - pageSize: 1, - sort: [], - isLoading: false, - alerts, - oldAlertsData, - ecsAlertsData, - querySnapshot: { request: [], response: [] }, - refetchAlerts: () => {}, - alertsCount: alerts.length, - onSortChange: jest.fn(), - onPageChange: jest.fn(), - fieldFormats: mockFieldFormatsRegistry, - }; +// Cases mock +jest.mock('./hooks/apis/bulk_get_cases'); +const mockBulkGetCases = jest.mocked(bulkGetCases); +const mockCases = getCasesMock(); +mockBulkGetCases.mockResolvedValue({ cases: mockCases, errors: [] }); + +// Maintenance windows mock +jest.mock('./hooks/apis/bulk_get_maintenance_windows'); +jest.mock('../../hooks/use_license'); +const mockBulkGetMaintenanceWindows = jest.mocked(bulkGetMaintenanceWindows); +jest.mocked(useLicense).mockReturnValue({ isAtLeastPlatinum: () => true }); +const mockMaintenanceWindows = getMaintenanceWindowsMock(); +mockBulkGetMaintenanceWindows.mockResolvedValue({ + maintenanceWindows: mockMaintenanceWindows, + errors: [], +}); - const defaultBulkActionsState = { - rowSelection: new Map(), - isAllSelected: false, - areAllVisibleRowsSelected: false, - rowCount: 4, - updatedAt: Date.now(), - }; +// AlertsDataGrid mock +jest.mock('./alerts_data_grid', () => ({ + AlertsDataGrid: jest.fn(), +})); +const mockAlertsDataGrid = jest.mocked(AlertsDataGrid); + +// useKibana mock +const mockCurrentAppId$ = new BehaviorSubject('testAppId'); +const mockCaseService = createCasesServiceMock(); +const mockHttpService = httpServiceMock.createStartContract(); +jest.mock('../../../common/lib/kibana/kibana_react', () => ({ + useKibana: () => ({ + services: { + application: { + getUrlForApp: jest.fn(() => ''), + capabilities: { + cases: { + create_cases: true, + read_cases: true, + update_cases: true, + delete_cases: true, + push_cases: true, + }, + maintenanceWindow: { + show: true, + }, + }, + currentAppId$: mockCurrentAppId$, + }, + cases: mockCaseService, + notifications: { + toasts: { + addDanger: () => {}, + }, + }, + data: {}, + http: mockHttpService, + }, + }), +})); - const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; - useCaseViewNavigationMock.mockReturnValue({ navigateToCaseView: jest.fn() }); - - const AlertsTableWithProviders: React.FunctionComponent< - AlertsTableProps & { initialBulkActionsState?: BulkActionsState } - > = (props) => { - const renderer = useMemo(() => createAppMockRenderer(AlertsQueryContext), []); - const AppWrapper = renderer.AppWrapper; - - const initialBulkActionsState = useReducer( - bulkActionsReducer, - props.initialBulkActionsState || defaultBulkActionsState - ); - - return ( - - { + return { get: jest.fn(), set: jest.fn() }; +}); + +const originalGetComputedStyle = Object.assign({}, window.getComputedStyle); +beforeAll(() => { + // The JSDOM implementation is too slow + // Especially for dropdowns that try to position themselves + // perf issue - https://github.com/jsdom/jsdom/issues/3234 + Object.defineProperty(window, 'getComputedStyle', { + value: (el: HTMLElement) => { + /** + * This is based on the jsdom implementation of getComputedStyle + * https://github.com/jsdom/jsdom/blob/9dae17bf0ad09042cfccd82e6a9d06d3a615d9f4/lib/jsdom/browser/Window.js#L779-L820 + * + * It is missing global style parsing and will only return styles applied directly to an element. + * Will not return styles that are global or from emotion + */ + const declaration = new CSSStyleDeclaration(); + const { style } = el; + + Array.prototype.forEach.call(style, (property: string) => { + declaration.setProperty( + property, + style.getPropertyValue(property), + style.getPropertyPriority(property) + ); + }); + + return declaration; + }, + configurable: true, + writable: true, + }); +}); +afterAll(() => { + Object.defineProperty(window, 'getComputedStyle', originalGetComputedStyle); +}); + +const queryClient = new QueryClient(testQueryClientConfig); +const TestComponent: FunctionComponent = (props) => ( + + + + + +); + +describe('AlertsTable', () => { + const tableProps: BaseAlertsTableProps = { + id: PLUGIN_ID, + ruleTypeIds: ['logs'], + query: {}, + columns, + initialPageSize: 10, + renderActionsCell: ({ openAlertInFlyout }) => { + return ( +
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts index 53deaf4145310..2151382a48317 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts @@ -9,11 +9,16 @@ import type { ISearchStart } from '@kbn/data-plugin/public'; import type { Filter } from '@kbn/es-query'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import type { EuiContextMenuPanelItemDescriptorEntry } from '@elastic/eui/src/components/context_menu/context_menu'; +import type { AlertsTablePropsWithRef } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { TableId } from '@kbn/securitysolution-data-table'; +import type { SourcererScopeName } from '../../../sourcerer/store/model'; +import type { AlertsUserProfilesData } from '../../configurations/security_solution_detections/fetch_page_context'; import type { Status } from '../../../../common/api/detection_engine'; import type { Note } from '../../../../common/api/timeline'; import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; import type { TimelineModel } from '../../../timelines/store/model'; import type { inputsModel } from '../../../common/store'; +import type { ControlColumnProps, RowRenderer } from '../../../../common/types'; export interface SetEventsLoadingProps { eventIds: string[]; @@ -78,3 +83,19 @@ export interface ThresholdAggregationData { } export type AlertTableContextMenuItem = EuiContextMenuPanelItemDescriptorEntry; + +export interface SecurityAlertsTableContext { + tableType: TableId; + rowRenderers: RowRenderer[]; + isDetails: boolean; + truncate: boolean; + isDraggable: boolean; + leadingControlColumn: ControlColumnProps; + userProfiles: AlertsUserProfilesData; + sourcererScope: SourcererScopeName; +} + +export type SecurityAlertsTableProps = AlertsTablePropsWithRef; +export type GetSecurityAlertsTableProp = + NonNullable; +export type { SelectedAlertWithLegacyFormats } from '@kbn/triggers-actions-ui-plugin/public/types'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx index 45614aad4e96d..81035bdf1d247 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx @@ -7,10 +7,11 @@ import { useMemo } from 'react'; import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; -import type { PreFetchPageContext } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { Alerts } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { EuiDataGridColumn } from '@elastic/eui'; import { useBulkGetUserProfiles } from '../../../common/components/user_profiles/use_bulk_get_user_profiles'; -export interface RenderCellValueContext { +export interface AlertsUserProfilesData { profiles: UserProfileWithAvatar[] | undefined; isLoading: boolean; } @@ -21,9 +22,12 @@ export const profileUidColumns = [ 'kibana.alert.workflow_user', ]; -export const useFetchPageContext: PreFetchPageContext = ({ +export const useFetchUserProfilesFromAlerts = ({ alerts, columns, +}: { + alerts: Alerts; + columns: EuiDataGridColumn[]; }) => { const uids = useMemo(() => { const ids = new Set(); @@ -38,7 +42,7 @@ export const useFetchPageContext: PreFetchPageContext = return ids; }, [alerts, columns]); const result = useBulkGetUserProfiles({ uids }); - return useMemo( + return useMemo( () => ({ profiles: result.data, isLoading: result.isLoading }), [result.data, result.isLoading] ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/index.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/index.ts index 3a96684d59dc6..712de9bdb5e52 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/index.ts @@ -6,6 +6,6 @@ */ import { getColumns } from './columns'; -import { RenderCellValue } from './render_cell_value'; +import { CellValue } from './render_cell_value'; -export { getColumns, RenderCellValue }; +export { getColumns, CellValue }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx index 6057410e1615e..dce004d452d61 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx @@ -7,6 +7,7 @@ import { mount } from 'enzyme'; import { cloneDeep } from 'lodash/fp'; +import type { ComponentProps } from 'react'; import React from 'react'; import { TableId } from '@kbn/securitysolution-data-table'; import type { ColumnHeaderOptions } from '../../../../common/types'; @@ -15,9 +16,8 @@ import { DragDropContextWrapper } from '../../../common/components/drag_and_drop import { defaultHeaders, mockTimelineData, TestProviders } from '../../../common/mock'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; import type { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; -import type { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering'; import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer'; -import { getRenderCellValueHook } from './render_cell_value'; +import { CellValue } from './render_cell_value'; import { SourcererScopeName } from '../../../sourcerer/store/model'; jest.mock('../../../common/lib/kibana'); @@ -41,7 +41,7 @@ describe('RenderCellValue', () => { let data: TimelineNonEcsData[]; let header: ColumnHeaderOptions; - let props: CellValueElementProps; + let props: ComponentProps; beforeEach(() => { data = cloneDeep(mockTimelineData[0].data); @@ -63,21 +63,17 @@ describe('RenderCellValue', () => { rowRenderers: defaultRowRenderers, asPlainText: false, ecsData: undefined, - truncate: undefined, + truncate: false, context: undefined, browserFields: {}, - }; + } as unknown as ComponentProps; }); test('it forwards the `CellValueElementProps` to the `DefaultCellRenderer`', () => { - const RenderCellValue = getRenderCellValueHook({ - scopeId: SourcererScopeName.default, - tableId: TableId.test, - }); const wrapper = mount( - + ); @@ -86,15 +82,10 @@ describe('RenderCellValue', () => { }); test('it renders a GuidedOnboardingTourStep', () => { - const RenderCellValue = getRenderCellValueHook({ - scopeId: SourcererScopeName.default, - tableId: TableId.test, - }); - const wrapper = mount( - + ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx index dedc86f50366b..05ed6114ea2f7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx @@ -6,16 +6,13 @@ */ import { EuiIcon, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { EuiDataGridCellProps } from '@elastic/eui'; import React, { useMemo, memo } from 'react'; import { find, getOr } from 'lodash/fp'; import type { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; import { tableDefaults, dataTableSelectors } from '@kbn/securitysolution-data-table'; -import type { TableId } from '@kbn/securitysolution-data-table'; import { useLicense } from '../../../common/hooks/use_license'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; -import type { SourcererScopeName } from '../../../sourcerer/store/model'; import { GuidedOnboardingTourStep } from '../../../common/components/guided_onboarding_tour/tour_step'; import { isDetectionsAlertsTable } from '../../../common/components/top_n/helpers'; import { @@ -30,6 +27,7 @@ import { SUPPRESSED_ALERT_TOOLTIP } from './translations'; import { VIEW_SELECTION } from '../../../../common/constants'; import { getAllFieldsByName } from '../../../common/containers/source'; import { eventRenderedViewColumns, getColumns } from './columns'; +import type { GetSecurityAlertsTableProp } from '../../components/alerts_table/types'; /** * This implementation of `EuiDataGrid`'s `renderCellValue` @@ -37,16 +35,17 @@ import { eventRenderedViewColumns, getColumns } from './columns'; * from the TGrid */ -export const RenderCellValue: React.FC> = memo( +export const CellValue: GetSecurityAlertsTableProp<'renderCellValue'> = memo( function RenderCellValue(props) { const { columnId, rowIndex, scopeId, tableId, + tableType, header, - data, - ecsData, + legacyAlert, + ecsAlert, linkValues, rowRenderers, isDetails, @@ -62,17 +61,17 @@ export const RenderCellValue: React.FC columnId === SIGNAL_RULE_NAME_FIELD_NAME && - isDetectionsAlertsTable(tableId) && + isDetectionsAlertsTable(tableType) && rowIndex === 0 && !props.isDetails, - [columnId, props.isDetails, rowIndex, tableId] + [columnId, props.isDetails, rowIndex, tableType] ); const { browserFields } = useSourcererDataView(scopeId); const browserFieldsByName = useMemo(() => getAllFieldsByName(browserFields), [browserFields]); const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); const license = useLicense(); const viewMode = - useDeepEqualSelector((state) => (getTable(state, tableId) ?? tableDefaults).viewMode) ?? + useDeepEqualSelector((state) => (getTable(state, tableId ?? '') ?? tableDefaults).viewMode) ?? tableDefaults.viewMode; const gridColumns = useMemo(() => { @@ -94,7 +93,7 @@ export const RenderCellValue: React.FC { - return (data as TimelineNonEcsData[]).map((field) => { + return (legacyAlert as TimelineNonEcsData[]).map((field) => { if (['_id', '_index'].includes(field.field)) { const newValue = field.value ?? ''; return { @@ -105,22 +104,24 @@ export const RenderCellValue: React.FC { - // We check both ecsData and data for the suppression count because it could be in either one, + // We check both ecsAlert and data for the suppression count because it could be in either one, // depending on where RenderCellValue is being used - when used in cases, data is populated, - // whereas in the regular security alerts table it's in ecsData - const ecsSuppressionCount = ecsData?.kibana?.alert.suppression?.docs_count?.[0]; - const dataSuppressionCount = find({ field: 'kibana.alert.suppression.docs_count' }, data) - ?.value?.[0] as number | undefined; + // whereas in the regular security alerts table it's in ecsAlert + const ecsSuppressionCount = ecsAlert?.kibana?.alert.suppression?.docs_count?.[0]; + const dataSuppressionCount = find( + { field: 'kibana.alert.suppression.docs_count' }, + legacyAlert + )?.value?.[0] as number | undefined; return ecsSuppressionCount ? parseInt(ecsSuppressionCount, 10) : dataSuppressionCount; - }, [ecsData, data]); + }, [ecsAlert, legacyAlert]); const Renderer = useMemo(() => { const myHeader = header ?? { id: columnId, ...browserFieldsByName[columnId] }; const colHeader = columnHeaders.find((col) => col.id === columnId); - const localLinkValues = getOr([], colHeader?.linkField ?? '', ecsData); + const localLinkValues = getOr([], colHeader?.linkField ?? '', ecsAlert); return ( { - const useRenderCellValue = (props: EuiDataGridCellProps['cellContext']) => { - return ; - }; - return useRenderCellValue; -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx deleted file mode 100644 index d9aa0a730da5a..0000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_actions_column.tsx +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; -import React, { useCallback, useContext, useMemo } from 'react'; -import { useSelector } from 'react-redux'; -import type { - AlertsTableConfigurationRegistry, - RenderCustomActionsRowArgs, -} from '@kbn/triggers-actions-ui-plugin/public/types'; -import { TableId } from '@kbn/securitysolution-data-table'; -import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; -import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../common/constants'; -import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; -import { StatefulEventContext } from '../../../common/components/events_viewer/stateful_event_context'; -import { eventsViewerSelector } from '../../../common/components/events_viewer/selectors'; -import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns'; -import { useLicense } from '../../../common/hooks/use_license'; -import type { TimelineItem } from '../../../../common/search_strategy'; -import { getAlertsDefaultModel } from '../../components/alerts_table/default_config'; -import type { State } from '../../../common/store'; -import { RowAction } from '../../../common/components/control_columns/row_action'; -import { useUserPrivileges } from '../../../common/components/user_privileges'; - -// we show a maximum of 6 action buttons -// - open flyout -// - investigate in timeline -// - 3-dot menu for more actions -// - add new note -// - session view -// - analyzer graph -const MAX_ACTION_BUTTON_COUNT = 6; - -export const getUseActionColumnHook = - (tableId: TableId): AlertsTableConfigurationRegistry['useActionsColumn'] => - () => { - let ACTION_BUTTON_COUNT = MAX_ACTION_BUTTON_COUNT; - - // hiding the session view icon for users without enterprise plus license - const license = useLicense(); - const isEnterprisePlus = license.isEnterprise(); - if (!isEnterprisePlus) { - ACTION_BUTTON_COUNT--; - } - - const { - timelinePrivileges: { read: canReadTimelines }, - notesPrivileges: { read: canReadNotes }, - } = useUserPrivileges(); - - // remove space if investigate timeline icon shouldn't be displayed - if (!canReadTimelines) { - ACTION_BUTTON_COUNT--; - } - - // remove space if add notes icon shouldn't be displayed - const securitySolutionNotesDisabled = useIsExperimentalFeatureEnabled( - 'securitySolutionNotesDisabled' - ); - if (!canReadNotes || securitySolutionNotesDisabled) { - ACTION_BUTTON_COUNT--; - } - - // we do not show the analyzer graph and session view icons on the cases alerts tab alerts table - // if the visualization in flyout advanced settings is disabled because these aren't supported inside the table - if (tableId === TableId.alertsOnCasePage) { - const [visualizationInFlyoutEnabled] = useUiSetting$( - ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING - ); - if (!isEnterprisePlus && !visualizationInFlyoutEnabled) { - ACTION_BUTTON_COUNT -= 1; - } else if (isEnterprisePlus && !visualizationInFlyoutEnabled) { - ACTION_BUTTON_COUNT -= 2; - } - } - - const eventContext = useContext(StatefulEventContext); - - const leadingControlColumn = useMemo( - () => getDefaultControlColumn(ACTION_BUTTON_COUNT)[0], - [ACTION_BUTTON_COUNT] - ); - - const { - dataTable: { - columns: columnHeaders, - showCheckboxes, - selectedEventIds, - loadingEventIds, - } = getAlertsDefaultModel(license), - } = useSelector((state: State) => eventsViewerSelector(state, tableId)); - - const renderCustomActionsRow = useCallback( - ({ - rowIndex, - cveProps, - setIsActionLoading, - refresh: alertsTableRefresh, - clearSelection, - ecsAlert: alert, - nonEcsData, - }: RenderCustomActionsRowArgs) => { - const timelineItem: TimelineItem = { - _id: (alert as Ecs)._id, - _index: (alert as Ecs)._index, - ecs: alert as Ecs, - data: nonEcsData, - }; - - return ( - {}} - rowIndex={cveProps.rowIndex} - colIndex={cveProps.colIndex} - pageRowIndex={rowIndex} - selectedEventIds={selectedEventIds} - setCellProps={cveProps.setCellProps} - showCheckboxes={showCheckboxes} - onRuleChange={eventContext?.onRuleChange} - tabType={'query'} - tableId={tableId} - width={0} - setEventsLoading={({ isLoading }) => { - if (!isLoading) { - clearSelection(); - return; - } - if (setIsActionLoading) setIsActionLoading(isLoading); - }} - setEventsDeleted={() => {}} - refetch={alertsTableRefresh} - /> - ); - }, - [ - columnHeaders, - loadingEventIds, - showCheckboxes, - leadingControlColumn, - selectedEventIds, - eventContext, - ] - ); - - return { - renderCustomActionsRow, - width: leadingControlColumn.width, - }; - }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_bulk_actions.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_bulk_actions.tsx index 453c268b47e66..8ba96bffae385 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_bulk_actions.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_bulk_actions.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import type { AlertsTableConfigurationRegistry } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { SerializableRecord } from '@kbn/utility-types'; import { isEqual } from 'lodash'; import type { Filter } from '@kbn/es-query'; import { useMemo, useCallback } from 'react'; import type { TableId } from '@kbn/securitysolution-data-table'; +import type { GetSecurityAlertsTableProp } from '../../components/alerts_table/types'; import { useBulkAlertAssigneesItems } from '../../../common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items'; import { useBulkAlertTagsItems } from '../../../common/components/toolbar/bulk_actions/use_bulk_alert_tags_items'; import { SourcererScopeName } from '../../../sourcerer/store/model'; @@ -60,8 +60,8 @@ function getFiltersForDSLQuery(datafeedQuery: QueryDslQueryContainer): Filter[] ]; } -export const getBulkActionHook = - (tableId: TableId): AlertsTableConfigurationRegistry['useBulkActions'] => +export const getBulkActionsByTableType = + (tableId: TableId): GetSecurityAlertsTableProp<'getBulkActions'> => (query, refresh) => { const { from, to } = useGlobalTime(); const filters = useMemo(() => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx index 618c1b1ba2cc0..3d0f646685607 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx @@ -7,7 +7,7 @@ import { createMockStore, mockGlobalState, TestProviders } from '../../../common/mock'; import { TableId } from '@kbn/securitysolution-data-table'; -import { getUseCellActionsHook } from './use_cell_actions'; +import { useCellActionsOptions } from './use_cell_actions'; import { columns as mockColumns, data as mockData } from './mock/data'; import type { EuiDataGridColumn, @@ -22,8 +22,6 @@ import React from 'react'; import { makeAction } from '../../../common/components/cell_actions/mocks'; import { VIEW_SELECTION } from '../../../../common/constants'; -const useCellActions = getUseCellActionsHook(TableId.test); - const mockDataGridRef: { current: EuiDataGridRefProps; } = { @@ -87,11 +85,10 @@ describe('getUseCellActionsHook', () => { it('should render cell actions correctly for gridView view', async () => { const { result } = renderHook( () => - useCellActions({ + useCellActionsOptions(TableId.test, { columns: mockColumns as unknown as EuiDataGridColumn[], - data: mockData, + oldAlertsData: mockData, dataGridRef: mockDataGridRef, - ecsData: [], pageSize: 10, pageIndex: 0, }), @@ -101,7 +98,7 @@ describe('getUseCellActionsHook', () => { ); await waitFor(() => { - const cellAction = result.current.getCellActions('host.name', 0)[0]; + const cellAction = result.current.getCellActionsForColumn('host.name', 0)[0]; renderCellAction(cellAction); @@ -112,11 +109,10 @@ describe('getUseCellActionsHook', () => { it('should not render cell actions correctly for eventRendered view', async () => { const { result } = renderHook( () => - useCellActions({ + useCellActionsOptions(TableId.test, { columns: mockColumns as unknown as EuiDataGridColumn[], - data: mockData, + oldAlertsData: mockData, dataGridRef: mockDataGridRef, - ecsData: [], pageSize: 10, pageIndex: 0, }), @@ -125,7 +121,7 @@ describe('getUseCellActionsHook', () => { } ); - const cellAction = result.current.getCellActions('host.name', 0); + const cellAction = result.current.getCellActionsForColumn('host.name', 0); await waitFor(() => expect(cellAction).toHaveLength(0)); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx index 71c5e62fb841a..4d74161c54153 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx @@ -6,9 +6,9 @@ */ import type { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; -import type { AlertsTableConfigurationRegistry } from '@kbn/triggers-actions-ui-plugin/public/types'; import { useCallback, useMemo } from 'react'; import { TableId, tableDefaults, dataTableSelectors } from '@kbn/securitysolution-data-table'; +import type { RenderContext } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { UseDataGridColumnsSecurityCellActionsProps } from '../../../common/components/cell_actions'; import { useDataGridColumnsSecurityCellActions } from '../../../common/components/cell_actions'; import { SecurityCellActionsTrigger, SecurityCellActionType } from '../../../app/actions/constants'; @@ -17,103 +17,106 @@ import { SourcererScopeName } from '../../../sourcerer/store/model'; import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { useGetFieldSpec } from '../../../common/hooks/use_get_field_spec'; import { useDataViewId } from '../../../common/hooks/use_data_view_id'; +import type { + SecurityAlertsTableContext, + GetSecurityAlertsTableProp, +} from '../../components/alerts_table/types'; -export const getUseCellActionsHook = (tableId: TableId) => { - const useCellActions: AlertsTableConfigurationRegistry['useCellActions'] = ({ - columns, - data, +export const useCellActionsOptions = ( + tableId: TableId, + context?: Pick< + RenderContext, + 'columns' | 'oldAlertsData' | 'pageIndex' | 'pageSize' | 'dataGridRef' + > +) => { + const { + columns = [], + oldAlertsData: data = [], + pageIndex = 0, + pageSize = 0, dataGridRef, - pageSize, - pageIndex, - }) => { - const getFieldSpec = useGetFieldSpec(SourcererScopeName.detections); - const dataViewId = useDataViewId(SourcererScopeName.detections); - /** - * There is difference between how `triggers actions` fetched data v/s - * how security solution fetches data via timelineSearchStrategy - * - * _id and _index fields are array in timelineSearchStrategy but not in - * ruleStrategy - * - * - */ + } = context ?? {}; + const getFieldSpec = useGetFieldSpec(SourcererScopeName.detections); + const dataViewId = useDataViewId(SourcererScopeName.detections); + const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); + const viewMode = + useShallowEqualSelector((state) => (getTable(state, tableId) ?? tableDefaults).viewMode) ?? + tableDefaults.viewMode; + const cellActionsMetadata = useMemo( + () => ({ scopeId: tableId, dataViewId }), + [dataViewId, tableId] + ); + const cellActionsFields: UseDataGridColumnsSecurityCellActionsProps['fields'] = useMemo( + () => + viewMode === VIEW_SELECTION.eventRenderedView + ? undefined + : columns.map( + (column) => + getFieldSpec(column.id) ?? { + name: '', + type: '', // When type is an empty string all cell actions are incompatible + aggregatable: false, + searchable: false, + } + ), + [columns, getFieldSpec, viewMode] + ); - const finalData = useMemo( - () => - (data as TimelineNonEcsData[][]).map((row) => - row.map((field) => { - let localField = field; - if (['_id', '_index'].includes(field.field)) { - const newValue = field.value ?? ''; - localField = { - field: field.field, - value: Array.isArray(newValue) ? newValue : [newValue], - }; - } - return localField; - }) - ), - [data] - ); + /** + * There is difference between how `triggers actions` fetched data v/s + * how security solution fetches data via timelineSearchStrategy + * + * _id and _index fields are array in timelineSearchStrategy but not in + * ruleStrategy + * + * + */ - const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); - - const viewMode = - useShallowEqualSelector((state) => (getTable(state, tableId) ?? tableDefaults).viewMode) ?? - tableDefaults.viewMode; - - const cellActionsMetadata = useMemo(() => ({ scopeId: tableId, dataViewId }), [dataViewId]); - - const cellActionsFields = useMemo(() => { - if (viewMode === VIEW_SELECTION.eventRenderedView) { - return undefined; - } - return columns.map( - (column) => - getFieldSpec(column.id) ?? { - name: '', - type: '', // When type is an empty string all cell actions are incompatible - aggregatable: false, - searchable: false, + const finalData = useMemo( + () => + (data as TimelineNonEcsData[][]).map((row) => + row.map((field) => { + let localField = field; + if (['_id', '_index'].includes(field.field)) { + const newValue = field.value ?? ''; + localField = { + field: field.field, + value: Array.isArray(newValue) ? newValue : [newValue], + }; } - ); - }, [getFieldSpec, columns, viewMode]); + return localField; + }) + ), + [data] + ); - const getCellValue = useCallback( - (fieldName, rowIndex) => { - const pageRowIndex = rowIndex - pageSize * pageIndex; - return finalData[pageRowIndex]?.find((rowData) => rowData.field === fieldName)?.value ?? []; - }, - [finalData, pageIndex, pageSize] - ); + const getCellValue = useCallback( + (fieldName, rowIndex) => { + const pageRowIndex = rowIndex - pageSize * pageIndex; + return finalData[pageRowIndex]?.find((rowData) => rowData.field === fieldName)?.value ?? []; + }, + [finalData, pageIndex, pageSize] + ); - const disabledActionTypes = - tableId === TableId.alertsOnCasePage ? [SecurityCellActionType.FILTER] : undefined; + const disabledActionTypes = + tableId === TableId.alertsOnCasePage ? [SecurityCellActionType.FILTER] : undefined; - const cellActions = useDataGridColumnsSecurityCellActions({ - triggerId: SecurityCellActionsTrigger.DEFAULT, - fields: cellActionsFields, - getCellValue, - metadata: cellActionsMetadata, - dataGridRef, - disabledActionTypes, - }); + const cellActions = useDataGridColumnsSecurityCellActions({ + triggerId: SecurityCellActionsTrigger.DEFAULT, + fields: cellActionsFields, + getCellValue, + metadata: cellActionsMetadata, + dataGridRef, + disabledActionTypes, + }); - const getCellActions = useCallback( - (_columnId: string, columnIndex: number) => { + return useMemo>(() => { + return { + getCellActionsForColumn: (_columnId: string, columnIndex: number) => { if (cellActions.length === 0) return []; return cellActions[columnIndex]; }, - [cellActions] - ); - - return useMemo(() => { - return { - getCellActions, - visibleCellActions: 3, - }; - }, [getCellActions]); - }; - - return useCellActions; + visibleCellActions: 3, + }; + }, [cellActions]); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx deleted file mode 100644 index 26628c860f5d2..0000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; -import type { ViewSelection } from '@kbn/securitysolution-data-table'; -import { - dataTableActions, - dataTableSelectors, - tableDefaults, - TableId, -} from '@kbn/securitysolution-data-table'; -import { useGetGroupSelectorStateless } from '@kbn/grouping/src/hooks/use_get_group_selector'; -import { getTelemetryEvent } from '@kbn/grouping/src/telemetry/const'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { SummaryViewSelector } from '../../../common/components/events_viewer/summary_view_select'; -import { groupIdSelector } from '../../../common/store/grouping/selectors'; -import { useSourcererDataView } from '../../../sourcerer/containers'; -import { SourcererScopeName } from '../../../sourcerer/store/model'; -import { updateGroups } from '../../../common/store/grouping/actions'; -import { useKibana } from '../../../common/lib/kibana'; -import { AlertsEventTypes, METRIC_TYPE, track } from '../../../common/lib/telemetry'; -import { useDataTableFilters } from '../../../common/hooks/use_data_table_filters'; -import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; -import { AdditionalFiltersAction } from '../../components/alerts_table/additional_filters_action'; - -const { changeViewMode } = dataTableActions; - -export const getPersistentControlsHook = (tableId: TableId) => { - const usePersistentControls = () => { - const dispatch = useDispatch(); - const { - services: { telemetry }, - } = useKibana(); - - const { sourcererDataView } = useSourcererDataView(SourcererScopeName.detections); - const groupId = useMemo(() => groupIdSelector(), []); - const { options } = useDeepEqualSelector((state) => groupId(state, tableId)) ?? { - options: [], - }; - - const trackGroupChange = useCallback( - (groupSelection: string) => { - track?.( - METRIC_TYPE.CLICK, - getTelemetryEvent.groupChanged({ groupingId: tableId, selected: groupSelection }) - ); - telemetry.reportEvent(AlertsEventTypes.AlertsGroupingChanged, { - groupByField: groupSelection, - tableId, - }); - }, - [telemetry] - ); - - const onGroupChange = useCallback( - (selectedGroups: string[]) => { - selectedGroups.forEach((g) => trackGroupChange(g)); - dispatch(updateGroups({ activeGroups: selectedGroups, tableId })); - }, - [dispatch, trackGroupChange] - ); - - const fields = useMemo(() => { - return Object.values(sourcererDataView.fields || {}); - }, [sourcererDataView.fields]); - - const groupSelector = useGetGroupSelectorStateless({ - groupingId: tableId, - onGroupChange, - fields, - defaultGroupingOptions: options, - maxGroupingLevels: 3, - }); - - const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); - - const tableView = useShallowEqualSelector( - (state) => (getTable(state, tableId) ?? tableDefaults).viewMode ?? tableDefaults.viewMode - ); - - const handleChangeTableView = useCallback( - (selectedView: ViewSelection) => { - dispatch( - changeViewMode({ - id: tableId, - viewMode: selectedView, - }) - ); - }, - [dispatch] - ); - - const { - showBuildingBlockAlerts, - setShowBuildingBlockAlerts, - showOnlyThreatIndicatorAlerts, - setShowOnlyThreatIndicatorAlerts, - } = useDataTableFilters(tableId); - - const additionalFiltersComponent = useMemo( - () => ( - - ), - [ - showBuildingBlockAlerts, - setShowBuildingBlockAlerts, - showOnlyThreatIndicatorAlerts, - setShowOnlyThreatIndicatorAlerts, - ] - ); - - const rightTopMenu = useMemo( - () => ( - - {[TableId.alertsOnRuleDetailsPage, TableId.alertsOnAlertsPage].includes(tableId) && ( - - - - )} - {additionalFiltersComponent} - {groupSelector} - - ), - [tableView, handleChangeTableView, additionalFiltersComponent, groupSelector] - ); - - return useMemo(() => ({ right: rightTopMenu }), [rightTopMenu]); - }; - - return usePersistentControls; -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_trigger_actions_browser_fields_options.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_trigger_actions_browser_fields_options.tsx index a627131ff05c0..327fa7f5fdf81 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_trigger_actions_browser_fields_options.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_trigger_actions_browser_fields_options.tsx @@ -5,35 +5,33 @@ * 2.0. */ import { useCallback, useMemo } from 'react'; -import type { AlertsTableConfigurationRegistry } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { EuiDataGridColumn } from '@elastic/eui'; +import { noop } from 'lodash'; import { useFieldBrowserOptions } from '../../../timelines/components/fields_browser'; import type { SourcererScopeName } from '../../../sourcerer/store/model'; -export const getUseTriggersActionsFieldBrowserOptions = (scopeId: SourcererScopeName) => { - const useTriggersActionsFieldBrowserOptions: AlertsTableConfigurationRegistry['useFieldBrowserOptions'] = - ({ onToggleColumn }) => { - const upsertColumn = useCallback( - (column: EuiDataGridColumn) => { - onToggleColumn(column.id); - }, - [onToggleColumn] - ); - const fieldBrowserArgs = useMemo(() => { - return { - sourcererScope: scopeId, - removeColumn: onToggleColumn, - upsertColumn, - }; - }, [upsertColumn, onToggleColumn]); - const options = useFieldBrowserOptions(fieldBrowserArgs); - - return useMemo(() => { - return { - createFieldButton: options.createFieldButton, - }; - }, [options.createFieldButton]); +export const useAlertsTableFieldsBrowserOptions = ( + scopeId: SourcererScopeName, + toggleColumn: (columnId: string) => void = noop +) => { + const upsertColumn = useCallback( + (column: EuiDataGridColumn) => { + toggleColumn?.(column.id); + }, + [toggleColumn] + ); + const fieldBrowserArgs = useMemo(() => { + return { + sourcererScope: scopeId, + removeColumn: toggleColumn, + upsertColumn, }; + }, [scopeId, toggleColumn, upsertColumn]); + const options = useFieldBrowserOptions(fieldBrowserArgs); - return useTriggersActionsFieldBrowserOptions; + return useMemo(() => { + return { + createFieldButton: options.createFieldButton, + }; + }, [options.createFieldButton]); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 01aab96481d5a..b0075e926207a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -37,7 +37,6 @@ import type { RunTimeMappings } from '@kbn/timelines-plugin/common/search_strate import { DetectionEngineFilters } from '../../components/detection_engine_filters/detection_engine_filters'; import { FilterByAssigneesPopover } from '../../../common/components/filter_by_assignees_popover/filter_by_assignees_popover'; import type { AssigneesIdsSelection } from '../../../common/components/assignees/types'; -import { ALERTS_TABLE_REGISTRY_CONFIG_IDS } from '../../../../common/constants'; import { useDataTableFilters } from '../../../common/hooks/use_data_table_filters'; import { InputsModelId } from '../../../common/store/inputs/constants'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; @@ -322,9 +321,8 @@ const DetectionEnginePageComponent: React.FC = () (groupingFilters: Filter[]) => { return ( ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx index 3c2900aca6f60..3e761a31c0bcf 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx @@ -21,7 +21,6 @@ import { type HostRiskScore, type UserRiskScore, } from '../../../../common/search_strategy'; -import { ALERTS_TABLE_REGISTRY_CONFIG_IDS } from '../../../../common/constants'; import { AlertsTableComponent } from '../../../detections/components/alerts_table'; import { GroupedAlertsTable } from '../../../detections/components/alerts_table/alerts_grouping'; import { useGlobalTime } from '../../../common/containers/use_global_time'; @@ -88,9 +87,8 @@ export const TopRiskScoreContributorsAlerts = ({ (groupingFilters: Filter[]) => { return ( ); }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/cypress/tasks/alerts.ts b/x-pack/solutions/security/plugins/security_solution/public/management/cypress/tasks/alerts.ts index f2e354c66f7fe..693d4ad04026c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/cypress/tasks/alerts.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/cypress/tasks/alerts.ts @@ -197,7 +197,7 @@ export const LOADING_INDICATOR = '[data-test-subj="globalLoadingIndicator"]'; export const ALERTS_URL = '/app/security/alerts'; export const GLOBAL_KQL_WRAPPER = '[data-test-subj="filters-global-container"]'; export const REFRESH_BUTTON = `${GLOBAL_KQL_WRAPPER} [data-test-subj="querySubmitButton"]`; -export const EMPTY_ALERT_TABLE = '[data-test-subj="alertsStateTableEmptyState"]'; +export const EMPTY_ALERT_TABLE = '[data-test-subj="alertsTableEmptyState"]'; export const ALERTS_TABLE_COUNT = `[data-test-subj="toolbar-alerts-count"]`; export const waitForPageFilters = () => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/plugin.tsx b/x-pack/solutions/security/plugins/security_solution/public/plugin.tsx index 5e1422d4f5f28..2dfef2a01e978 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/plugin.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/plugin.tsx @@ -19,7 +19,6 @@ import type { } from '@kbn/core/public'; import { AppStatus, DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import type { TriggersAndActionsUIPublicPluginSetup } from '@kbn/triggers-actions-ui-plugin/public'; import { uiMetricService } from '@kbn/cloud-security-posture-common/utils/ui_metrics'; import type { SecuritySolutionAppWrapperFeature, @@ -77,7 +76,6 @@ export class Plugin implements IPlugin(); @@ -100,7 +98,7 @@ export class Plugin implements IPlugin { @@ -131,7 +129,6 @@ export class Plugin implements IPlugin boolean; renderColumn: ({ className, @@ -50,6 +50,6 @@ export interface ColumnRenderer { truncate?: boolean; values: string[] | null | undefined; key?: string; - context?: RenderCellValueContext; + context?: AlertsUserProfilesData; }) => React.ReactNode; } diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_column_renderer.ts b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_column_renderer.ts index c6cf20dfcff84..049a36a61dcfd 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_column_renderer.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_column_renderer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RenderCellValueContext } from '../../../../../detections/configurations/security_solution_detections/fetch_page_context'; +import type { AlertsUserProfilesData } from '../../../../../detections/configurations/security_solution_detections/fetch_page_context'; import type { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; import type { ColumnRenderer } from './column_renderer'; @@ -17,7 +17,7 @@ export const getColumnRenderer = ( columnName: string, columnRenderers: ColumnRenderer[], data: TimelineNonEcsData[], - context?: RenderCellValueContext + context?: AlertsUserProfilesData ): ColumnRenderer => { const renderer = columnRenderers.find((columnRenderer) => columnRenderer.isInstance(columnName, data, context) diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx index 93be8036f0cea..e767685272356 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx @@ -13,7 +13,7 @@ import { UsersAvatarsPanel } from '../../../../../common/components/user_profile import type { ColumnHeaderOptions, RowRenderer } from '../../../../../../common/types'; import type { ColumnRenderer } from './column_renderer'; import { profileUidColumns } from '../../../../../detections/configurations/security_solution_detections/fetch_page_context'; -import type { RenderCellValueContext } from '../../../../../detections/configurations/security_solution_detections/fetch_page_context'; +import type { AlertsUserProfilesData } from '../../../../../detections/configurations/security_solution_detections/fetch_page_context'; export const userProfileColumnRenderer: ColumnRenderer = { isInstance: (columnName, _, context) => profileUidColumns.includes(columnName) && !!context, @@ -42,7 +42,7 @@ export const userProfileColumnRenderer: ColumnRenderer = { scopeId: string; truncate?: boolean; values: string[] | undefined | null; - context?: RenderCellValueContext; + context?: AlertsUserProfilesData; }) => { // Show spinner if loading profiles or if there are no fetched profiles yet // Do not show the loading spinner if context is not provided at all diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index b005add20ba55..4124f97df74a4 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -139,7 +139,7 @@ export function ObservabilityAlertsCommonProvider({ }; const getNoDataStateOrFail = async () => { - return await testSubjects.existOrFail('alertsStateTableEmptyState'); + return await testSubjects.existOrFail('alertsTableEmptyState'); }; // Query Bar diff --git a/x-pack/test/functional/services/observability/overview/common.ts b/x-pack/test/functional/services/observability/overview/common.ts index 9a15e2bc7eb0b..37f917bf8eaa1 100644 --- a/x-pack/test/functional/services/observability/overview/common.ts +++ b/x-pack/test/functional/services/observability/overview/common.ts @@ -21,7 +21,7 @@ const DATE_WITHOUT_DATA = { const ALERTS_TITLE = 'Alerts'; const ALERTS_ACCORDION_SELECTOR = `accordion-${ALERTS_TITLE}`; const ALERTS_SECTION_BUTTON_CSS_SELECTOR = `[data-test-subj=${ALERTS_ACCORDION_SELECTOR}] button.euiAccordion__button`; -const ALERTS_TABLE_NO_DATA_SELECTOR = 'alertsStateTableEmptyState'; +const ALERTS_TABLE_NO_DATA_SELECTOR = 'alertsTableEmptyState'; const ALERTS_TABLE_WITH_DATA_SELECTOR = 'alertsTable'; const ALERTS_TABLE_LOADING_SELECTOR = 'internalAlertsPageLoading'; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts index f7b606c27f089..1cf6e95ff3be8 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts @@ -46,7 +46,7 @@ export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="closed-alert-status"] export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; -export const EMPTY_ALERT_TABLE = '[data-test-subj="alertsStateTableEmptyState"]'; +export const EMPTY_ALERT_TABLE = '[data-test-subj="alertsTableEmptyState"]'; export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; From 4f75020be732d1325b8123807f524c0a30a6e734 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 13 Jan 2025 17:00:47 +0100 Subject: [PATCH 02/14] [ResponseOps][Alerts] Move alerts table to package (#201547) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves the alerts table code and dependencies to dedicated packages in `packages/response-ops/**`. > [!IMPORTANT] > 🚧 Merging to the `alerts-table-refactor` feature branch` - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Christos Nasikas --- .github/CODEOWNERS | 3 + package.json | 3 + packages/kbn-repo-packages/package-map.json | 4154 ----------------- packages/response-ops/alerts_apis/README.md | 3 + ...get_muted_alerts_instances_by_rule.test.ts | 17 +- .../get_muted_alerts_instances_by_rule.ts | 14 +- .../apis/mute_alert_instance.test.ts | 10 +- .../alerts_apis/apis/mute_alert_instance.ts | 25 + .../apis/unmute_alert_instance.test.ts | 10 +- .../alerts_apis/apis/unmute_alert_instance.ts | 25 + .../response-ops/alerts_apis/constants.ts | 22 + .../hooks/use_get_muted_alerts_query.test.tsx | 64 + .../hooks/use_get_muted_alerts_query.tsx | 37 +- .../hooks/use_mute_alert_instance.test.tsx | 62 + .../hooks/use_mute_alert_instance.ts | 57 + .../hooks/use_unmute_alert_instance.test.tsx | 62 + .../hooks/use_unmute_alert_instance.ts | 57 + .../response-ops/alerts_apis/jest.config.js | 15 + .../response-ops/alerts_apis/kibana.jsonc | 7 + .../response-ops/alerts_apis/package.json | 6 + .../response-ops/alerts_apis/setup_tests.ts | 11 + .../response-ops/alerts_apis/tsconfig.json | 27 + packages/response-ops/alerts_apis/types.ts | 22 + .../alerts_fields_browser/README.md | 3 + .../categories_badges.styles.ts | 17 +- .../categories_badges.test.tsx | 8 +- .../categories_badges/categories_badges.tsx | 9 +- .../components/categories_badges/index.ts | 10 +- .../categories_selector.styles.ts | 22 + .../categories_selector.test.tsx | 9 +- .../categories_selector.tsx | 11 +- .../components/categories_selector/index.ts | 10 + .../field_browser/field_browser.styles.ts | 17 + .../field_browser/field_browser.test.tsx | 14 +- .../field_browser/field_browser.tsx | 19 +- .../field_browser_modal.test.tsx | 10 +- .../field_browser_modal.tsx | 31 +- .../field_items/field_items.style.ts | 8 +- .../field_items/field_items.test.tsx | 9 +- .../components/field_items/field_items.tsx | 11 +- .../components/field_items/index.ts | 10 + .../components/field_name/field_name.test.tsx | 8 +- .../components/field_name/field_name.tsx | 8 +- .../components/field_name/index.ts | 10 + .../field_table/field_table.styles.ts | 9 +- .../field_table/field_table.test.tsx | 8 +- .../components/field_table/field_table.tsx | 11 +- .../field_table/field_table_header.styles.ts | 16 + .../field_table/field_table_header.test.tsx | 8 +- .../field_table/field_table_header.tsx | 9 +- .../components/field_table/index.ts | 11 + .../components/search/index.ts | 10 + .../components/search/search.test.tsx | 8 +- .../components/search/search.tsx | 8 +- .../alerts_fields_browser}/helpers.test.ts | 10 +- .../alerts_fields_browser}/helpers.ts | 12 +- .../alerts_fields_browser/index.ts | 13 + .../alerts_fields_browser/jest.config.js | 15 + .../alerts_fields_browser/kibana.jsonc | 7 + .../alerts_fields_browser}/mock.ts | 12 +- .../alerts_fields_browser/package.json | 6 + .../alerts_fields_browser/setup_tests.ts | 11 + .../alerts_fields_browser/translations.ts | 118 + .../alerts_fields_browser/tsconfig.json | 24 + .../alerts_fields_browser}/types.ts | 10 +- packages/response-ops/alerts_table/README.md | 174 + .../alerts_table}/apis/bulk_get_cases.test.ts | 10 +- .../alerts_table}/apis/bulk_get_cases.ts | 8 +- .../apis/bulk_get_maintenance_windows.test.ts | 10 +- .../apis/bulk_get_maintenance_windows.ts | 18 +- ...on_product_no_results_magnifying_glass.svg | 0 .../components}/alert_actions_cell.tsx | 14 +- .../alert_lifecycle_status_cell.test.tsx | 51 +- .../alert_lifecycle_status_cell.tsx | 18 +- .../alerts_table/components}/alerts_count.tsx | 8 +- .../components}/alerts_data_grid.test.tsx | 133 +- .../components}/alerts_data_grid.tsx | 378 +- .../components}/alerts_flyout.test.tsx | 22 +- .../components}/alerts_flyout.tsx | 55 +- .../alerts_query_inspector.test.tsx | 43 + .../components/alerts_query_inspector.tsx | 23 +- .../alerts_query_inspector_modal.test.tsx | 16 +- .../alerts_query_inspector_modal.tsx | 53 +- .../components}/alerts_table.test.tsx | 328 +- .../alerts_table/components}/alerts_table.tsx | 199 +- .../components/bulk_actions_cell.tsx | 14 +- .../components/bulk_actions_header_cell.tsx | 12 +- .../bulk_actions_toolbar_control.tsx | 69 +- .../components/cases_cell.test.tsx | 136 + .../alerts_table/components/cases_cell.tsx | 20 +- .../components/cell_popover_host.test.tsx | 69 + .../components/cell_popover_host.tsx | 37 + .../components/cell_value_host.test.tsx | 76 + .../components/cell_value_host.tsx | 74 + .../components/control_column_header_cell.tsx | 19 + .../components/default_alert_actions.test.tsx | 89 + .../components/default_alert_actions.tsx | 47 + .../default_alerts_flyout.test.tsx | 47 +- .../components}/default_alerts_flyout.tsx | 40 +- .../components/default_cell.test.tsx | 56 + .../alerts_table/components}/default_cell.tsx | 8 +- .../components/default_cell_value.test.tsx | 111 + .../components/default_cell_value.tsx | 102 + .../components/empty_state.stories.tsx | 24 + .../alerts_table/components}/empty_state.tsx | 18 +- .../components/error_boundary.test.tsx | 8 +- .../components/error_boundary.tsx | 8 +- .../components/error_cell.test.tsx | 28 + .../alerts_table/components/error_cell.tsx | 55 + .../components/error_fallback.test.tsx | 25 + .../components/error_fallback.tsx | 42 + .../hover_visibility_container.test.tsx | 32 +- .../components/hover_visibility_container.tsx | 12 +- .../components/last_updated_at.tsx | 10 +- .../maintenance_windows_cell.test.tsx | 38 +- .../components/maintenance_windows_cell.tsx | 20 +- .../maintenance_windows_tooltip_content.tsx | 11 +- .../mark_as_untracked_alert_action.tsx | 29 +- .../components/mute_alert_action.tsx | 73 + .../non_virtualized_grid_body.test.tsx | 62 + .../components/non_virtualized_grid_body.tsx | 74 + .../components/system_cell.test.tsx | 74 + .../alerts_table/components/system_cell.tsx | 16 +- .../view_alert_details_alert_action.tsx | 31 +- .../view_rule_details_alert_action.tsx | 55 + .../alerts_table/configuration.ts | 10 +- .../response-ops}/alerts_table/constants.ts | 41 +- .../contexts/alerts_table_context.tsx | 12 +- .../hooks}/toggle_column.test.tsx | 8 +- .../alerts_table/hooks}/toggle_column.ts | 11 +- .../hooks/use_alert_muted_state.ts | 28 + .../hooks/use_bulk_actions.test.tsx | 332 +- .../alerts_table/hooks/use_bulk_actions.ts | 75 +- .../hooks/use_bulk_get_cases.test.tsx | 95 + .../alerts_table/hooks/use_bulk_get_cases.tsx | 28 +- .../use_bulk_get_maintenance_windows.test.tsx | 94 +- .../use_bulk_get_maintenance_windows.tsx | 47 +- .../hooks/use_bulk_untrack_alerts.tsx | 32 +- .../use_bulk_untrack_alerts_by_query.tsx | 34 +- .../hooks/use_case_view_navigation.test.tsx | 72 + .../hooks}/use_case_view_navigation.ts | 17 +- .../alerts_table/hooks}/use_columns.test.tsx | 34 +- .../alerts_table/hooks}/use_columns.ts | 22 +- .../alerts_table/hooks/use_field_formatter.ts | 34 + .../alerts_table}/hooks/use_license.test.ts | 25 +- .../alerts_table}/hooks/use_license.ts | 17 +- .../alerts_table/hooks/use_pagination.test.ts | 8 +- .../alerts_table/hooks/use_pagination.ts | 12 +- .../alerts_table/hooks/use_sorting.test.ts | 9 +- .../alerts_table/hooks/use_sorting.ts | 21 +- .../hooks/use_toolbar_visibility.tsx | 45 +- packages/response-ops/alerts_table/index.ts | 14 + .../response-ops/alerts_table/jest.config.js | 15 + .../response-ops/alerts_table/kibana.jsonc | 7 + .../alerts_table/mocks/cases.mock.tsx | 61 + .../alerts_table/mocks/context.mock.tsx | 113 +- .../mocks/maintenance_windows.mock.ts | 9 +- .../response-ops/alerts_table/package.json | 6 + .../response-ops/alerts_table/query_client.ts | 12 + .../reducers/bulk_actions_reducer.test.tsx | 237 +- .../reducers/bulk_actions_reducer.ts | 10 +- .../response-ops/alerts_table/setup_tests.ts | 11 + .../response-ops/alerts_table/translations.ts | 283 ++ .../response-ops/alerts_table/tsconfig.json | 50 + packages/response-ops/alerts_table/types.ts | 569 +++ .../response-ops/alerts_table/utils/react.ts | 24 + .../alerts_table/utils/storage.test.ts | 100 + .../alerts_table/utils/storage.ts | 55 + .../response-ops/alerts_table/utils/test.ts | 76 + .../shared/kbn-alerting-types/alerts_types.ts | 40 + .../shared/kbn-alerting-types/index.ts | 2 +- .../search_strategy_types.ts | 4 +- .../shared/kbn-alerting-types/tsconfig.json | 3 +- .../src/alert_fields_table/index.tsx | 4 +- .../alert_lifecycle_status_badge/index.tsx | 14 +- .../apis/search_alerts/search_alerts.ts | 5 +- .../common/hooks/use_load_rule_types_query.ts | 9 +- .../src/common/test_utils/wrapper.tsx | 23 + .../src/common/types/index.ts | 1 - tsconfig.base.json | 6 + .../triggers_actions_ui_example/kibana.jsonc | 4 +- .../public/application.tsx | 20 +- .../components/alerts_table_sandbox.tsx | 34 +- .../public/plugin.tsx | 4 + .../triggers_actions_ui_example/tsconfig.json | 3 + .../translations/translations/fr-FR.json | 100 - .../translations/translations/ja-JP.json | 100 - .../translations/translations/zh-CN.json | 99 - .../public/common/mock/test_providers.tsx | 17 - .../components/case_view_alerts.test.tsx | 55 +- .../case_view/components/case_view_alerts.tsx | 22 +- .../shared/cases/public/plugin.test.ts | 2 + .../plugins/shared/cases/public/types.ts | 2 + .../plugins/shared/cases/tsconfig.json | 2 + .../fleet/cypress.config.space_awareness.d.ts | 3 - .../fleet/cypress.config.space_awareness.js | 42 - .../alert_actions.tsx | 40 +- .../flyout_body.tsx | 7 +- .../render_cell_value.tsx | 8 +- .../explorer/alerts/alerts_panel.tsx | 24 +- .../platform/plugins/shared/ml/tsconfig.json | 2 + .../triggers_actions_ui/common/utils.ts | 8 - .../public/application/hooks/constants.ts | 23 - .../application/lib/rule_api/mute_alert.ts | 24 - .../application/lib/rule_api/unmute_alert.ts | 24 - .../components/stack_alerts_page.test.tsx | 19 +- .../components/stack_alerts_page.tsx | 71 +- .../hooks/use_rule_type_ids_by_feature_id.ts | 2 +- .../alerts_table/alerts_flyout/index.ts | 9 - .../bulk_actions/components/index.ts | 9 - .../alerts_table/bulk_actions/translations.ts | 42 - .../sections/alerts_table/cases/cell.test.tsx | 137 - .../sections/alerts_table/cases/index.mock.ts | 28 - .../cases/use_case_view_navigation.test.ts | 61 - .../alerts_table/cells/default_cell.test.tsx | 64 - .../alerts_table/cells/index.test.tsx | 67 - .../alerts_table/cells/render_cell_value.tsx | 148 - .../alerts_table/empty_state.stories.tsx | 22 - .../hooks/alert_mute/use_alert_muted_state.ts | 26 - .../alert_mute/use_get_muted_alerts.test.tsx | 63 - .../hooks/alert_mute/use_mute_alert.test.tsx | 62 - .../hooks/alert_mute/use_mute_alert.ts | 48 - .../alert_mute/use_unmute_alert.test.tsx | 62 - .../hooks/alert_mute/use_unmute_alert.ts | 48 - .../sections/alerts_table/hooks/constants.ts | 16 - .../sections/alerts_table/hooks/index.ts | 13 - .../alerts_table/hooks/translations.ts | 65 - .../alerts_table/hooks/use_actions_column.ts | 57 - .../hooks/use_bulk_get_cases.test.tsx | 81 - .../use_bulk_untrack_alerts_by_query.test.ts | 53 - .../alerts_table/hooks/use_columns/index.ts | 8 - .../sections/alerts_table/index.mock.ts | 35 - .../sections/alerts_table/query_client.ts | 10 - .../default_alert_actions.test.tsx | 61 - .../row_actions/default_alert_actions.tsx | 35 - .../alerts_table/row_actions/index.ts | 13 - .../row_actions/mute_alert_action.tsx | 60 - .../view_rule_details_alert_action.tsx | 49 - .../toolbar/components/inspect/index.test.tsx | 39 - .../components/inspect/translations.ts | 63 - .../last_updated_at/translations.ts | 16 - .../sections/alerts_table/toolbar/index.tsx | 8 - .../sections/alerts_table/translations.ts | 65 - .../sections/alerts_table/types.ts | 87 - .../sections/alerts_table/utils.ts | 10 - .../with_bulk_rule_api_operations.tsx | 4 +- .../categories_badges.styles.ts | 15 - .../components/categories_badges/index.ts | 9 - .../categories_selector.styles.ts | 19 - .../components/categories_selector/index.ts | 8 - .../components/field_items/index.ts | 8 - .../components/field_name/index.ts | 8 - .../field_table/field_table_header.styles.ts | 14 - .../components/field_table/index.ts | 9 - .../field_browser/components/search/index.ts | 8 - .../field_browser/field_browser.styles.ts | 15 - .../sections/field_browser/index.ts | 12 - .../sections/field_browser/translations.ts | 119 - .../rule_details/components/rule.test.tsx | 10 +- .../sections/rule_details/components/rule.tsx | 35 +- .../components/rule_alert_list.tsx | 10 +- .../application/sections/translations.ts | 71 - .../public/common/get_alerts_table.tsx | 14 - .../get_alerts_table_default_row_actions.tsx | 14 - .../public/common/get_alerts_table_state.tsx | 32 - .../public/common/get_field_browser.tsx | 21 - .../triggers_actions_ui/public/index.ts | 9 - .../triggers_actions_ui/public/mocks.ts | 18 - .../triggers_actions_ui/public/plugin.ts | 37 +- .../triggers_actions_ui/public/types.ts | 493 +- .../shared/triggers_actions_ui/tsconfig.json | 7 +- .../asset_details/__stories__/decorator.tsx | 1 - .../alert_actions}/alert_actions.test.tsx | 97 +- .../alert_actions}/alert_actions.tsx | 171 +- .../alert_actions/alert_actions_lazy.tsx} | 5 +- .../helpers/map_rules_params_with_flyout.ts | 2 +- .../alert_overview/overview_columns.tsx | 2 +- .../alerts_flyout/alerts_flyout.mock.ts | 4 +- .../alerts_flyout/alerts_flyout.stories.tsx | 6 +- .../alerts_flyout/alerts_flyout.test.tsx | 2 +- .../alerts_flyout/alerts_flyout.tsx | 6 +- .../alerts_flyout/alerts_flyout_body.tsx | 5 +- .../alerts_flyout/alerts_flyout_header.tsx | 2 +- .../components/alerts_table/alerts_table.tsx | 60 +- .../alerts_table/alerts_table_lazy.tsx | 23 + .../alerts_table/common/cell_value.test.tsx | 4 +- .../alerts_table/common/cell_value.tsx | 4 +- .../public/components/alerts_table/types.ts | 8 +- .../plugins/observability/public/index.ts | 4 +- .../pages/alert_details/alert_details.tsx | 2 +- .../components/related_alerts.tsx | 2 - .../alert_details/components/status_bar.tsx | 2 +- .../public/pages/alerts/alerts.tsx | 4 +- .../public/pages/overview/overview.tsx | 3 - .../components/rule_details_tabs.tsx | 2 - .../observability/public/plugin.mock.tsx | 23 - .../plugins/observability/tsconfig.json | 4 +- .../alerts/components/slo_alerts_table.tsx | 2 +- .../observability/plugins/slo/tsconfig.json | 1 + .../common/types/header_actions/index.ts | 2 +- .../data_table/data_table.stories.tsx | 2 +- .../components/data_table/index.test.tsx | 2 +- .../components/data_table/index.tsx | 35 +- .../security/packages/data_table/kibana.jsonc | 4 +- .../packages/data_table/tsconfig.json | 4 +- .../common/types/header_actions/index.ts | 2 +- .../tabs/alerts_tab/index.tsx | 4 +- .../transform_control_columns.tsx | 2 +- .../common/components/events_viewer/index.tsx | 7 +- .../use_bulk_alert_assignees_items.test.tsx | 4 +- .../use_bulk_alert_assignees_items.tsx | 4 +- .../use_bulk_alert_tags_items.tsx | 2 +- .../containers/source/use_data_view.tsx | 2 +- .../mock/mock_triggers_actions_ui_plugin.tsx | 11 - .../severity_level_chart.tsx | 2 +- .../components/alerts_table/actions_cell.tsx | 2 +- .../components/alerts_table/index.tsx | 38 +- .../use_add_bulk_to_timeline.tsx | 2 +- .../components/alerts_table/types.ts | 4 +- .../fetch_page_context.tsx | 8 +- .../render_cell_value.test.tsx | 10 +- .../use_alert_actions.tsx | 2 +- .../use_cell_actions.test.tsx | 5 +- .../use_cell_actions.tsx | 2 +- .../hooks/use_risk_input_actions_panels.tsx | 4 +- .../document_details/right/tabs/table_tab.tsx | 2 +- .../create_field_button/index.tsx | 2 +- .../field_table_columns/index.test.tsx | 2 +- .../field_table_columns/index.tsx | 2 +- .../components/fields_browser/index.test.tsx | 2 +- .../components/fields_browser/index.tsx | 2 +- .../plugins/security_solution/tsconfig.json | 3 + .../public/mocks/test_providers.tsx | 4 +- .../components/table/field_browser.test.tsx | 20 +- .../components/table/field_browser.tsx | 26 +- .../plugins/threat_intelligence/tsconfig.json | 3 +- yarn.lock | 12 + 337 files changed, 6422 insertions(+), 9356 deletions(-) delete mode 100644 packages/kbn-repo-packages/package-map.json create mode 100644 packages/response-ops/alerts_apis/README.md rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_muted_alerts.test.ts => packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.test.ts (78%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_with_muted_alerts.ts => packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.ts (60%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.test.ts => packages/response-ops/alerts_apis/apis/mute_alert_instance.test.ts (57%) create mode 100644 packages/response-ops/alerts_apis/apis/mute_alert_instance.ts rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.test.ts => packages/response-ops/alerts_apis/apis/unmute_alert_instance.test.ts (57%) create mode 100644 packages/response-ops/alerts_apis/apis/unmute_alert_instance.ts create mode 100644 packages/response-ops/alerts_apis/constants.ts create mode 100644 packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.test.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.tsx => packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.tsx (50%) create mode 100644 packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.test.tsx create mode 100644 packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.ts create mode 100644 packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.test.tsx create mode 100644 packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.ts create mode 100644 packages/response-ops/alerts_apis/jest.config.js create mode 100644 packages/response-ops/alerts_apis/kibana.jsonc create mode 100644 packages/response-ops/alerts_apis/package.json create mode 100644 packages/response-ops/alerts_apis/setup_tests.ts create mode 100644 packages/response-ops/alerts_apis/tsconfig.json create mode 100644 packages/response-ops/alerts_apis/types.ts create mode 100644 packages/response-ops/alerts_fields_browser/README.md rename src/platform/packages/shared/kbn-alerting-types/alert_type.ts => packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.styles.ts (64%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/categories_badges/categories_badges.test.tsx (80%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/categories_badges/categories_badges.tsx (80%) rename src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/alerts_types.ts => packages/response-ops/alerts_fields_browser/components/categories_badges/index.ts (75%) create mode 100644 packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.styles.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/categories_selector/categories_selector.test.tsx (88%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/categories_selector/categories_selector.tsx (91%) create mode 100644 packages/response-ops/alerts_fields_browser/components/categories_selector/index.ts create mode 100644 packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.styles.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops/alerts_fields_browser/components}/field_browser/field_browser.test.tsx (90%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops/alerts_fields_browser/components}/field_browser/field_browser.tsx (89%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser/components/field_browser_modal}/field_browser_modal.test.tsx (89%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser/components/field_browser_modal}/field_browser_modal.tsx (87%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_items/field_items.style.ts (56%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_items/field_items.test.tsx (96%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_items/field_items.tsx (92%) create mode 100644 packages/response-ops/alerts_fields_browser/components/field_items/index.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_name/field_name.test.tsx (70%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_name/field_name.tsx (59%) create mode 100644 packages/response-ops/alerts_fields_browser/components/field_name/index.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_table/field_table.styles.ts (54%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_table/field_table.test.tsx (93%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_table/field_table.tsx (90%) create mode 100644 packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.styles.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_table/field_table_header.test.tsx (90%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/field_table/field_table_header.tsx (89%) create mode 100644 packages/response-ops/alerts_fields_browser/components/field_table/index.ts create mode 100644 packages/response-ops/alerts_fields_browser/components/search/index.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/search/search.test.tsx (81%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/components/search/search.tsx (67%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/helpers.test.ts (96%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/helpers.ts (92%) create mode 100644 packages/response-ops/alerts_fields_browser/index.ts create mode 100644 packages/response-ops/alerts_fields_browser/jest.config.js create mode 100644 packages/response-ops/alerts_fields_browser/kibana.jsonc rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/mock.ts (97%) create mode 100644 packages/response-ops/alerts_fields_browser/package.json create mode 100644 packages/response-ops/alerts_fields_browser/setup_tests.ts create mode 100644 packages/response-ops/alerts_fields_browser/translations.ts create mode 100644 packages/response-ops/alerts_fields_browser/tsconfig.json rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser => packages/response-ops/alerts_fields_browser}/types.ts (78%) create mode 100644 packages/response-ops/alerts_table/README.md rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks => packages/response-ops/alerts_table}/apis/bulk_get_cases.test.ts (68%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks => packages/response-ops/alerts_table}/apis/bulk_get_cases.ts (72%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks => packages/response-ops/alerts_table}/apis/bulk_get_maintenance_windows.test.ts (70%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks => packages/response-ops/alerts_table}/apis/bulk_get_maintenance_windows.ts (75%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/assets/illustration_product_no_results_magnifying_glass.svg (100%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions => packages/response-ops/alerts_table/components}/alert_actions_cell.tsx (76%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells => packages/response-ops/alerts_table/components}/alert_lifecycle_status_cell.test.tsx (57%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells => packages/response-ops/alerts_table/components}/alert_lifecycle_status_cell.tsx (67%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/alerts_count => packages/response-ops/alerts_table/components}/alerts_count.tsx (73%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table => packages/response-ops/alerts_table/components}/alerts_data_grid.test.tsx (81%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table => packages/response-ops/alerts_table/components}/alerts_data_grid.tsx (56%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout => packages/response-ops/alerts_table/components}/alerts_flyout.test.tsx (86%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout => packages/response-ops/alerts_table/components}/alerts_flyout.tsx (59%) create mode 100644 packages/response-ops/alerts_table/components/alerts_query_inspector.test.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.tsx => packages/response-ops/alerts_table/components/alerts_query_inspector.tsx (66%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.test.tsx => packages/response-ops/alerts_table/components/alerts_query_inspector_modal.test.tsx (90%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.tsx => packages/response-ops/alerts_table/components/alerts_query_inspector_modal.tsx (81%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table => packages/response-ops/alerts_table/components}/alerts_table.test.tsx (80%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table => packages/response-ops/alerts_table/components}/alerts_table.tsx (78%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/row_cell.tsx => packages/response-ops/alerts_table/components/bulk_actions_cell.tsx (76%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/column_header.tsx => packages/response-ops/alerts_table/components/bulk_actions_header_cell.tsx (68%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/toolbar.tsx => packages/response-ops/alerts_table/components/bulk_actions_toolbar_control.tsx (82%) create mode 100644 packages/response-ops/alerts_table/components/cases_cell.test.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.tsx => packages/response-ops/alerts_table/components/cases_cell.tsx (67%) create mode 100644 packages/response-ops/alerts_table/components/cell_popover_host.test.tsx create mode 100644 packages/response-ops/alerts_table/components/cell_popover_host.tsx create mode 100644 packages/response-ops/alerts_table/components/cell_value_host.test.tsx create mode 100644 packages/response-ops/alerts_table/components/cell_value_host.tsx create mode 100644 packages/response-ops/alerts_table/components/control_column_header_cell.tsx create mode 100644 packages/response-ops/alerts_table/components/default_alert_actions.test.tsx create mode 100644 packages/response-ops/alerts_table/components/default_alert_actions.tsx rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout => packages/response-ops/alerts_table/components}/default_alerts_flyout.test.tsx (77%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout => packages/response-ops/alerts_table/components}/default_alerts_flyout.tsx (67%) create mode 100644 packages/response-ops/alerts_table/components/default_cell.test.tsx rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells => packages/response-ops/alerts_table/components}/default_cell.tsx (52%) create mode 100644 packages/response-ops/alerts_table/components/default_cell_value.test.tsx create mode 100644 packages/response-ops/alerts_table/components/default_cell_value.tsx create mode 100644 packages/response-ops/alerts_table/components/empty_state.stories.tsx rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table => packages/response-ops/alerts_table/components}/empty_state.tsx (78%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common => packages/response-ops/alerts_table}/components/error_boundary.test.tsx (71%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common => packages/response-ops/alerts_table}/components/error_boundary.tsx (68%) create mode 100644 packages/response-ops/alerts_table/components/error_cell.test.tsx create mode 100644 packages/response-ops/alerts_table/components/error_cell.tsx create mode 100644 packages/response-ops/alerts_table/components/error_fallback.test.tsx create mode 100644 packages/response-ops/alerts_table/components/error_fallback.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.test.tsx => packages/response-ops/alerts_table/components/hover_visibility_container.test.tsx (50%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx => packages/response-ops/alerts_table/components/hover_visibility_container.tsx (73%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx => packages/response-ops/alerts_table/components/last_updated_at.tsx (82%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.test.tsx => packages/response-ops/alerts_table/components/maintenance_windows_cell.test.tsx (60%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx => packages/response-ops/alerts_table/components/maintenance_windows_cell.tsx (74%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx => packages/response-ops/alerts_table/components/maintenance_windows_tooltip_content.tsx (84%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions => packages/response-ops/alerts_table/components}/mark_as_untracked_alert_action.tsx (54%) create mode 100644 packages/response-ops/alerts_table/components/mute_alert_action.tsx create mode 100644 packages/response-ops/alerts_table/components/non_virtualized_grid_body.test.tsx create mode 100644 packages/response-ops/alerts_table/components/non_virtualized_grid_body.tsx create mode 100644 packages/response-ops/alerts_table/components/system_cell.test.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.tsx => packages/response-ops/alerts_table/components/system_cell.tsx (61%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions => packages/response-ops/alerts_table/components}/view_alert_details_alert_action.tsx (61%) create mode 100644 packages/response-ops/alerts_table/components/view_rule_details_alert_action.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx => packages/response-ops/alerts_table/configuration.ts (87%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/constants.ts (64%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/contexts/alerts_table_context.tsx (58%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns => packages/response-ops/alerts_table/hooks}/toggle_column.test.tsx (81%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns => packages/response-ops/alerts_table/hooks}/toggle_column.ts (87%) create mode 100644 packages/response-ops/alerts_table/hooks/use_alert_muted_state.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_bulk_actions.test.tsx (61%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_bulk_actions.ts (84%) create mode 100644 packages/response-ops/alerts_table/hooks/use_bulk_get_cases.test.tsx rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_bulk_get_cases.tsx (61%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.test.ts => packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.test.tsx (67%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx (59%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_bulk_untrack_alerts.tsx (62%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx (60%) create mode 100644 packages/response-ops/alerts_table/hooks/use_case_view_navigation.test.tsx rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases => packages/response-ops/alerts_table/hooks}/use_case_view_navigation.ts (58%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns => packages/response-ops/alerts_table/hooks}/use_columns.test.tsx (93%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns => packages/response-ops/alerts_table/hooks}/use_columns.ts (92%) create mode 100644 packages/response-ops/alerts_table/hooks/use_field_formatter.ts rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application => packages/response-ops/alerts_table}/hooks/use_license.test.ts (64%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application => packages/response-ops/alerts_table}/hooks/use_license.ts (56%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_pagination.test.ts (92%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_pagination.ts (87%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_sorting.test.ts (82%) rename {x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections => packages/response-ops}/alerts_table/hooks/use_sorting.ts (76%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx => packages/response-ops/alerts_table/hooks/use_toolbar_visibility.tsx (86%) create mode 100644 packages/response-ops/alerts_table/index.ts create mode 100644 packages/response-ops/alerts_table/jest.config.js create mode 100644 packages/response-ops/alerts_table/kibana.jsonc create mode 100644 packages/response-ops/alerts_table/mocks/cases.mock.tsx rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx => packages/response-ops/alerts_table/mocks/context.mock.tsx (66%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/index.mock.ts => packages/response-ops/alerts_table/mocks/maintenance_windows.mock.ts (76%) create mode 100644 packages/response-ops/alerts_table/package.json create mode 100644 packages/response-ops/alerts_table/query_client.ts rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx => packages/response-ops/alerts_table/reducers/bulk_actions_reducer.test.tsx (83%) rename x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/reducer.ts => packages/response-ops/alerts_table/reducers/bulk_actions_reducer.ts (83%) create mode 100644 packages/response-ops/alerts_table/setup_tests.ts create mode 100644 packages/response-ops/alerts_table/translations.ts create mode 100644 packages/response-ops/alerts_table/tsconfig.json create mode 100644 packages/response-ops/alerts_table/types.ts create mode 100644 packages/response-ops/alerts_table/utils/react.ts create mode 100644 packages/response-ops/alerts_table/utils/storage.test.ts create mode 100644 packages/response-ops/alerts_table/utils/storage.ts create mode 100644 packages/response-ops/alerts_table/utils/test.ts create mode 100644 src/platform/packages/shared/kbn-alerting-types/alerts_types.ts create mode 100644 src/platform/packages/shared/kbn-alerts-ui-shared/src/common/test_utils/wrapper.tsx delete mode 100644 x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts delete mode 100644 x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/translations.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/index.mock.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.test.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/render_cell_value.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.stories.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_alert_muted_state.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/constants.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/translations.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_actions_column.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.test.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.mock.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/query_client.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mute_alert_action.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_rule_details_alert_action.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.test.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/translations.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/index.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/translations.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/types.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/utils.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.styles.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.styles.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.styles.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.styles.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/index.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/translations.ts delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_default_row_actions.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_state.tsx delete mode 100644 x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_field_browser.tsx rename x-pack/solutions/observability/plugins/observability/public/{pages/alerts/components => components/alert_actions}/alert_actions.test.tsx (74%) rename x-pack/solutions/observability/plugins/observability/public/{pages/alerts/components => components/alert_actions}/alert_actions.tsx (60%) rename x-pack/{platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.ts => solutions/observability/plugins/observability/public/components/alert_actions/alert_actions_lazy.tsx} (60%) create mode 100644 x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table_lazy.tsx delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_triggers_actions_ui_plugin.tsx diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dd94614283438..c7d0b318858cc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -140,6 +140,9 @@ packages/kbn-validate-next-docs-cli @elastic/kibana-operations packages/kbn-web-worker-stub @elastic/kibana-operations packages/kbn-whereis-pkg-cli @elastic/kibana-operations packages/kbn-yarn-lock-validator @elastic/kibana-operations +packages/response-ops/alerts_apis @elastic/response-ops +packages/response-ops/alerts_fields_browser @elastic/response-ops +packages/response-ops/alerts_table @elastic/response-ops packages/serverless/storybook/config @elastic/appex-sharedux src/core @elastic/kibana-core src/core/packages/analytics/browser @elastic/kibana-core diff --git a/package.json b/package.json index 7123281838ad9..bc871b80a8ddb 100644 --- a/package.json +++ b/package.json @@ -761,6 +761,9 @@ "@kbn/resizable-layout": "link:src/platform/packages/shared/kbn-resizable-layout", "@kbn/resizable-layout-examples-plugin": "link:examples/resizable_layout_examples", "@kbn/resolver-test-plugin": "link:x-pack/test/plugin_functional/plugins/resolver_test", + "@kbn/response-ops-alerts-apis": "link:packages/response-ops/alerts_apis", + "@kbn/response-ops-alerts-fields-browser": "link:packages/response-ops/alerts_fields_browser", + "@kbn/response-ops-alerts-table": "link:packages/response-ops/alerts_table", "@kbn/response-ops-rule-form": "link:src/platform/packages/shared/response-ops/rule_form", "@kbn/response-ops-rule-params": "link:src/platform/packages/shared/response-ops/rule_params", "@kbn/response-stream-plugin": "link:examples/response_stream", diff --git a/packages/kbn-repo-packages/package-map.json b/packages/kbn-repo-packages/package-map.json deleted file mode 100644 index 4e76377822453..0000000000000 --- a/packages/kbn-repo-packages/package-map.json +++ /dev/null @@ -1,4154 +0,0 @@ -[ - [ - "@kbn/aad-fixtures-plugin", - "x-pack/test/alerting_api_integration/common/plugins/aad" - ], - [ - "@kbn/actions-plugin", - "x-pack/platform/plugins/shared/actions" - ], - [ - "@kbn/actions-simulators-plugin", - "x-pack/test/alerting_api_integration/common/plugins/actions_simulators" - ], - [ - "@kbn/actions-types", - "src/platform/packages/shared/kbn-actions-types" - ], - [ - "@kbn/advanced-settings-plugin", - "src/plugins/advanced_settings" - ], - [ - "@kbn/ai-assistant", - "x-pack/platform/packages/shared/kbn-ai-assistant" - ], - [ - "@kbn/ai-assistant-common", - "x-pack/platform/packages/shared/ai-assistant/common" - ], - [ - "@kbn/ai-assistant-icon", - "x-pack/platform/packages/shared/ai-assistant/icon" - ], - [ - "@kbn/ai-assistant-management-plugin", - "src/platform/plugins/shared/ai_assistant_management/selection" - ], - [ - "@kbn/aiops-change-point-detection", - "x-pack/platform/packages/private/ml/aiops_change_point_detection" - ], - [ - "@kbn/aiops-common", - "x-pack/platform/packages/shared/ml/aiops_common" - ], - [ - "@kbn/aiops-components", - "x-pack/platform/packages/private/ml/aiops_components" - ], - [ - "@kbn/aiops-log-pattern-analysis", - "x-pack/platform/packages/shared/ml/aiops_log_pattern_analysis" - ], - [ - "@kbn/aiops-log-rate-analysis", - "x-pack/platform/packages/shared/ml/aiops_log_rate_analysis" - ], - [ - "@kbn/aiops-plugin", - "x-pack/platform/plugins/shared/aiops" - ], - [ - "@kbn/aiops-test-utils", - "x-pack/platform/packages/private/ml/aiops_test_utils" - ], - [ - "@kbn/alerting-api-integration-helpers", - "x-pack/test/alerting_api_integration/packages/helpers" - ], - [ - "@kbn/alerting-api-integration-test-plugin", - "x-pack/test/alerting_api_integration/common/plugins/alerts" - ], - [ - "@kbn/alerting-comparators", - "x-pack/platform/packages/shared/kbn-alerting-comparators" - ], - [ - "@kbn/alerting-example-plugin", - "x-pack/examples/alerting_example" - ], - [ - "@kbn/alerting-fixture-plugin", - "x-pack/test/functional_with_es_ssl/plugins/alerts" - ], - [ - "@kbn/alerting-plugin", - "x-pack/platform/plugins/shared/alerting" - ], - [ - "@kbn/alerting-state-types", - "x-pack/platform/packages/private/kbn-alerting-state-types" - ], - [ - "@kbn/alerting-types", - "src/platform/packages/shared/kbn-alerting-types" - ], - [ - "@kbn/alerts-as-data-utils", - "src/platform/packages/shared/kbn-alerts-as-data-utils" - ], - [ - "@kbn/alerts-grouping", - "x-pack/solutions/observability/packages/kbn-alerts-grouping" - ], - [ - "@kbn/alerts-restricted-fixtures-plugin", - "x-pack/test/alerting_api_integration/common/plugins/alerts_restricted" - ], - [ - "@kbn/alerts-ui-shared", - "src/platform/packages/shared/kbn-alerts-ui-shared" - ], - [ - "@kbn/ambient-common-types", - "packages/kbn-ambient-common-types" - ], - [ - "@kbn/ambient-ftr-types", - "packages/kbn-ambient-ftr-types" - ], - [ - "@kbn/ambient-storybook-types", - "packages/kbn-ambient-storybook-types" - ], - [ - "@kbn/ambient-ui-types", - "packages/kbn-ambient-ui-types" - ], - [ - "@kbn/analytics", - "packages/kbn-analytics" - ], - [ - "@kbn/analytics-collection-utils", - "packages/analytics/utils/analytics_collection_utils" - ], - [ - "@kbn/analytics-ftr-helpers-plugin", - "test/analytics/plugins/analytics_ftr_helpers" - ], - [ - "@kbn/analytics-plugin-a-plugin", - "test/analytics/plugins/analytics_plugin_a" - ], - [ - "@kbn/apm-config-loader", - "packages/kbn-apm-config-loader" - ], - [ - "@kbn/apm-data-access-plugin", - "x-pack/solutions/observability/plugins/apm_data_access" - ], - [ - "@kbn/apm-data-view", - "src/platform/packages/shared/kbn-apm-data-view" - ], - [ - "@kbn/apm-ftr-e2e", - "x-pack/solutions/observability/plugins/apm/ftr_e2e" - ], - [ - "@kbn/apm-plugin", - "x-pack/solutions/observability/plugins/apm" - ], - [ - "@kbn/apm-synthtrace", - "packages/kbn-apm-synthtrace" - ], - [ - "@kbn/apm-synthtrace-client", - "packages/kbn-apm-synthtrace-client" - ], - [ - "@kbn/apm-types", - "x-pack/solutions/observability/packages/kbn-apm-types" - ], - [ - "@kbn/apm-utils", - "src/platform/packages/shared/kbn-apm-utils" - ], - [ - "@kbn/app-link-test-plugin", - "test/plugin_functional/plugins/app_link_test" - ], - [ - "@kbn/application-usage-test-plugin", - "x-pack/test/usage_collection/plugins/application_usage_test" - ], - [ - "@kbn/asset-inventory-plugin", - "x-pack/solutions/security/plugins/asset_inventory" - ], - [ - "@kbn/audit-log-plugin", - "x-pack/test/security_api_integration/plugins/audit_log" - ], - [ - "@kbn/avc-banner", - "src/platform/packages/shared/kbn-avc-banner" - ], - [ - "@kbn/axe-config", - "packages/kbn-axe-config" - ], - [ - "@kbn/babel-preset", - "packages/kbn-babel-preset" - ], - [ - "@kbn/babel-register", - "packages/kbn-babel-register" - ], - [ - "@kbn/babel-transform", - "packages/kbn-babel-transform" - ], - [ - "@kbn/banners-plugin", - "x-pack/plugins/banners" - ], - [ - "@kbn/bazel-runner", - "packages/kbn-bazel-runner" - ], - [ - "@kbn/calculate-auto", - "packages/kbn-calculate-auto" - ], - [ - "@kbn/calculate-width-from-char-count", - "packages/kbn-calculate-width-from-char-count" - ], - [ - "@kbn/canvas-plugin", - "x-pack/platform/plugins/private/canvas" - ], - [ - "@kbn/capture-oas-snapshot-cli", - "packages/kbn-capture-oas-snapshot-cli" - ], - [ - "@kbn/cases-api-integration-test-plugin", - "x-pack/test/cases_api_integration/common/plugins/cases" - ], - [ - "@kbn/cases-components", - "src/platform/packages/shared/kbn-cases-components" - ], - [ - "@kbn/cases-plugin", - "x-pack/platform/plugins/shared/cases" - ], - [ - "@kbn/cbor", - "packages/kbn-cbor" - ], - [ - "@kbn/cell-actions", - "src/platform/packages/shared/kbn-cell-actions" - ], - [ - "@kbn/chart-expressions-common", - "src/plugins/chart_expressions/common" - ], - [ - "@kbn/chart-icons", - "packages/kbn-chart-icons" - ], - [ - "@kbn/charts-plugin", - "src/plugins/charts" - ], - [ - "@kbn/charts-theme", - "packages/kbn-charts-theme" - ], - [ - "@kbn/check-mappings-update-cli", - "packages/kbn-check-mappings-update-cli" - ], - [ - "@kbn/check-prod-native-modules-cli", - "packages/kbn-check-prod-native-modules-cli" - ], - [ - "@kbn/ci-stats-core", - "packages/kbn-ci-stats-core" - ], - [ - "@kbn/ci-stats-performance-metrics", - "packages/kbn-ci-stats-performance-metrics" - ], - [ - "@kbn/ci-stats-reporter", - "packages/kbn-ci-stats-reporter" - ], - [ - "@kbn/ci-stats-shipper-cli", - "packages/kbn-ci-stats-shipper-cli" - ], - [ - "@kbn/cli-dev-mode", - "packages/kbn-cli-dev-mode" - ], - [ - "@kbn/cloud", - "packages/cloud" - ], - [ - "@kbn/cloud-chat-plugin", - "x-pack/plugins/cloud_integrations/cloud_chat" - ], - [ - "@kbn/cloud-data-migration-plugin", - "x-pack/platform/plugins/private/cloud_integrations/cloud_data_migration" - ], - [ - "@kbn/cloud-defend-plugin", - "x-pack/solutions/security/plugins/cloud_defend" - ], - [ - "@kbn/cloud-experiments-plugin", - "x-pack/plugins/cloud_integrations/cloud_experiments" - ], - [ - "@kbn/cloud-full-story-plugin", - "x-pack/plugins/cloud_integrations/cloud_full_story" - ], - [ - "@kbn/cloud-integration-saml-provider-plugin", - "x-pack/test/cloud_integration/plugins/saml_provider" - ], - [ - "@kbn/cloud-links-plugin", - "x-pack/plugins/cloud_integrations/cloud_links" - ], - [ - "@kbn/cloud-plugin", - "x-pack/plugins/cloud" - ], - [ - "@kbn/cloud-security-posture", - "x-pack/solutions/security/packages/kbn-cloud-security-posture/public" - ], - [ - "@kbn/cloud-security-posture-common", - "x-pack/platform/packages/shared/kbn-cloud-security-posture/common" - ], - [ - "@kbn/cloud-security-posture-graph", - "x-pack/solutions/security/packages/kbn-cloud-security-posture/graph" - ], - [ - "@kbn/cloud-security-posture-plugin", - "x-pack/solutions/security/plugins/cloud_security_posture" - ], - [ - "@kbn/code-editor", - "packages/shared-ux/code_editor/impl" - ], - [ - "@kbn/code-editor-mock", - "packages/shared-ux/code_editor/mocks" - ], - [ - "@kbn/code-owners", - "packages/kbn-code-owners" - ], - [ - "@kbn/coloring", - "packages/kbn-coloring" - ], - [ - "@kbn/config", - "packages/kbn-config" - ], - [ - "@kbn/config-mocks", - "packages/kbn-config-mocks" - ], - [ - "@kbn/config-schema", - "packages/kbn-config-schema" - ], - [ - "@kbn/console-plugin", - "src/platform/plugins/shared/console" - ], - [ - "@kbn/content-management-content-editor", - "packages/content-management/content_editor" - ], - [ - "@kbn/content-management-content-insights-public", - "packages/content-management/content_insights/content_insights_public" - ], - [ - "@kbn/content-management-content-insights-server", - "packages/content-management/content_insights/content_insights_server" - ], - [ - "@kbn/content-management-examples-plugin", - "examples/content_management_examples" - ], - [ - "@kbn/content-management-favorites-common", - "packages/content-management/favorites/favorites_common" - ], - [ - "@kbn/content-management-favorites-public", - "packages/content-management/favorites/favorites_public" - ], - [ - "@kbn/content-management-favorites-server", - "packages/content-management/favorites/favorites_server" - ], - [ - "@kbn/content-management-plugin", - "src/plugins/content_management" - ], - [ - "@kbn/content-management-tabbed-table-list-view", - "packages/content-management/tabbed_table_list_view" - ], - [ - "@kbn/content-management-table-list-view", - "packages/content-management/table_list_view" - ], - [ - "@kbn/content-management-table-list-view-common", - "packages/content-management/table_list_view_common" - ], - [ - "@kbn/content-management-table-list-view-table", - "packages/content-management/table_list_view_table" - ], - [ - "@kbn/content-management-user-profiles", - "packages/content-management/user_profiles" - ], - [ - "@kbn/content-management-utils", - "src/platform/packages/shared/kbn-content-management-utils" - ], - [ - "@kbn/controls-example-plugin", - "examples/controls_example" - ], - [ - "@kbn/controls-plugin", - "src/platform/plugins/shared/controls" - ], - [ - "@kbn/core", - "src/core" - ], - [ - "@kbn/core-analytics-browser", - "src/core/packages/analytics/browser" - ], - [ - "@kbn/core-analytics-browser-internal", - "src/core/packages/analytics/browser-internal" - ], - [ - "@kbn/core-analytics-browser-mocks", - "packages/core/analytics/core-analytics-browser-mocks" - ], - [ - "@kbn/core-analytics-server", - "src/core/packages/analytics/server" - ], - [ - "@kbn/core-analytics-server-internal", - "src/core/packages/analytics/server-internal" - ], - [ - "@kbn/core-analytics-server-mocks", - "packages/core/analytics/core-analytics-server-mocks" - ], - [ - "@kbn/core-app-status-plugin", - "test/plugin_functional/plugins/core_app_status" - ], - [ - "@kbn/core-application-browser", - "src/core/packages/application/browser" - ], - [ - "@kbn/core-application-browser-internal", - "src/core/packages/application/browser-internal" - ], - [ - "@kbn/core-application-browser-mocks", - "packages/core/application/core-application-browser-mocks" - ], - [ - "@kbn/core-application-common", - "src/core/packages/application/common" - ], - [ - "@kbn/core-apps-browser-internal", - "src/core/packages/apps/browser-internal" - ], - [ - "@kbn/core-apps-browser-mocks", - "packages/core/apps/core-apps-browser-mocks" - ], - [ - "@kbn/core-apps-server-internal", - "src/core/packages/apps/server-internal" - ], - [ - "@kbn/core-base-browser-internal", - "src/core/packages/base/browser-internal" - ], - [ - "@kbn/core-base-browser-mocks", - "packages/core/base/core-base-browser-mocks" - ], - [ - "@kbn/core-base-common", - "src/core/packages/base/common" - ], - [ - "@kbn/core-base-common-internal", - "packages/core/base/core-base-common-internal" - ], - [ - "@kbn/core-base-server-internal", - "packages/core/base/core-base-server-internal" - ], - [ - "@kbn/core-base-server-mocks", - "packages/core/base/core-base-server-mocks" - ], - [ - "@kbn/core-capabilities-browser-internal", - "packages/core/capabilities/core-capabilities-browser-internal" - ], - [ - "@kbn/core-capabilities-browser-mocks", - "packages/core/capabilities/core-capabilities-browser-mocks" - ], - [ - "@kbn/core-capabilities-common", - "packages/core/capabilities/core-capabilities-common" - ], - [ - "@kbn/core-capabilities-server", - "packages/core/capabilities/core-capabilities-server" - ], - [ - "@kbn/core-capabilities-server-internal", - "packages/core/capabilities/core-capabilities-server-internal" - ], - [ - "@kbn/core-capabilities-server-mocks", - "packages/core/capabilities/core-capabilities-server-mocks" - ], - [ - "@kbn/core-chrome-browser", - "packages/core/chrome/core-chrome-browser" - ], - [ - "@kbn/core-chrome-browser-internal", - "packages/core/chrome/core-chrome-browser-internal" - ], - [ - "@kbn/core-chrome-browser-mocks", - "packages/core/chrome/core-chrome-browser-mocks" - ], - [ - "@kbn/core-config-server-internal", - "packages/core/config/core-config-server-internal" - ], - [ - "@kbn/core-custom-branding-browser", - "packages/core/custom-branding/core-custom-branding-browser" - ], - [ - "@kbn/core-custom-branding-browser-internal", - "packages/core/custom-branding/core-custom-branding-browser-internal" - ], - [ - "@kbn/core-custom-branding-browser-mocks", - "packages/core/custom-branding/core-custom-branding-browser-mocks" - ], - [ - "@kbn/core-custom-branding-common", - "packages/core/custom-branding/core-custom-branding-common" - ], - [ - "@kbn/core-custom-branding-server", - "packages/core/custom-branding/core-custom-branding-server" - ], - [ - "@kbn/core-custom-branding-server-internal", - "packages/core/custom-branding/core-custom-branding-server-internal" - ], - [ - "@kbn/core-custom-branding-server-mocks", - "packages/core/custom-branding/core-custom-branding-server-mocks" - ], - [ - "@kbn/core-deprecations-browser", - "packages/core/deprecations/core-deprecations-browser" - ], - [ - "@kbn/core-deprecations-browser-internal", - "packages/core/deprecations/core-deprecations-browser-internal" - ], - [ - "@kbn/core-deprecations-browser-mocks", - "packages/core/deprecations/core-deprecations-browser-mocks" - ], - [ - "@kbn/core-deprecations-common", - "packages/core/deprecations/core-deprecations-common" - ], - [ - "@kbn/core-deprecations-server", - "packages/core/deprecations/core-deprecations-server" - ], - [ - "@kbn/core-deprecations-server-internal", - "packages/core/deprecations/core-deprecations-server-internal" - ], - [ - "@kbn/core-deprecations-server-mocks", - "packages/core/deprecations/core-deprecations-server-mocks" - ], - [ - "@kbn/core-doc-links-browser", - "packages/core/doc-links/core-doc-links-browser" - ], - [ - "@kbn/core-doc-links-browser-internal", - "packages/core/doc-links/core-doc-links-browser-internal" - ], - [ - "@kbn/core-doc-links-browser-mocks", - "packages/core/doc-links/core-doc-links-browser-mocks" - ], - [ - "@kbn/core-doc-links-server", - "packages/core/doc-links/core-doc-links-server" - ], - [ - "@kbn/core-doc-links-server-internal", - "packages/core/doc-links/core-doc-links-server-internal" - ], - [ - "@kbn/core-doc-links-server-mocks", - "packages/core/doc-links/core-doc-links-server-mocks" - ], - [ - "@kbn/core-elasticsearch-client-server-internal", - "packages/core/elasticsearch/core-elasticsearch-client-server-internal" - ], - [ - "@kbn/core-elasticsearch-client-server-mocks", - "packages/core/elasticsearch/core-elasticsearch-client-server-mocks" - ], - [ - "@kbn/core-elasticsearch-server", - "packages/core/elasticsearch/core-elasticsearch-server" - ], - [ - "@kbn/core-elasticsearch-server-internal", - "packages/core/elasticsearch/core-elasticsearch-server-internal" - ], - [ - "@kbn/core-elasticsearch-server-mocks", - "packages/core/elasticsearch/core-elasticsearch-server-mocks" - ], - [ - "@kbn/core-environment-server-internal", - "packages/core/environment/core-environment-server-internal" - ], - [ - "@kbn/core-environment-server-mocks", - "packages/core/environment/core-environment-server-mocks" - ], - [ - "@kbn/core-execution-context-browser", - "packages/core/execution-context/core-execution-context-browser" - ], - [ - "@kbn/core-execution-context-browser-internal", - "packages/core/execution-context/core-execution-context-browser-internal" - ], - [ - "@kbn/core-execution-context-browser-mocks", - "packages/core/execution-context/core-execution-context-browser-mocks" - ], - [ - "@kbn/core-execution-context-common", - "packages/core/execution-context/core-execution-context-common" - ], - [ - "@kbn/core-execution-context-server", - "packages/core/execution-context/core-execution-context-server" - ], - [ - "@kbn/core-execution-context-server-internal", - "packages/core/execution-context/core-execution-context-server-internal" - ], - [ - "@kbn/core-execution-context-server-mocks", - "packages/core/execution-context/core-execution-context-server-mocks" - ], - [ - "@kbn/core-fatal-errors-browser", - "packages/core/fatal-errors/core-fatal-errors-browser" - ], - [ - "@kbn/core-fatal-errors-browser-internal", - "packages/core/fatal-errors/core-fatal-errors-browser-internal" - ], - [ - "@kbn/core-fatal-errors-browser-mocks", - "packages/core/fatal-errors/core-fatal-errors-browser-mocks" - ], - [ - "@kbn/core-feature-flags-browser", - "packages/core/feature-flags/core-feature-flags-browser" - ], - [ - "@kbn/core-feature-flags-browser-internal", - "packages/core/feature-flags/core-feature-flags-browser-internal" - ], - [ - "@kbn/core-feature-flags-browser-mocks", - "packages/core/feature-flags/core-feature-flags-browser-mocks" - ], - [ - "@kbn/core-feature-flags-server", - "packages/core/feature-flags/core-feature-flags-server" - ], - [ - "@kbn/core-feature-flags-server-internal", - "packages/core/feature-flags/core-feature-flags-server-internal" - ], - [ - "@kbn/core-feature-flags-server-mocks", - "packages/core/feature-flags/core-feature-flags-server-mocks" - ], - [ - "@kbn/core-history-block-plugin", - "test/plugin_functional/plugins/core_history_block" - ], - [ - "@kbn/core-http-browser", - "packages/core/http/core-http-browser" - ], - [ - "@kbn/core-http-browser-internal", - "packages/core/http/core-http-browser-internal" - ], - [ - "@kbn/core-http-browser-mocks", - "packages/core/http/core-http-browser-mocks" - ], - [ - "@kbn/core-http-common", - "packages/core/http/core-http-common" - ], - [ - "@kbn/core-http-context-server-internal", - "packages/core/http/core-http-context-server-internal" - ], - [ - "@kbn/core-http-context-server-mocks", - "packages/core/http/core-http-context-server-mocks" - ], - [ - "@kbn/core-http-plugin", - "test/plugin_functional/plugins/core_http" - ], - [ - "@kbn/core-http-request-handler-context-server", - "packages/core/http/core-http-request-handler-context-server" - ], - [ - "@kbn/core-http-request-handler-context-server-internal", - "packages/core/http/core-http-request-handler-context-server-internal" - ], - [ - "@kbn/core-http-resources-server", - "packages/core/http/core-http-resources-server" - ], - [ - "@kbn/core-http-resources-server-internal", - "packages/core/http/core-http-resources-server-internal" - ], - [ - "@kbn/core-http-resources-server-mocks", - "packages/core/http/core-http-resources-server-mocks" - ], - [ - "@kbn/core-http-router-server-internal", - "packages/core/http/core-http-router-server-internal" - ], - [ - "@kbn/core-http-router-server-mocks", - "packages/core/http/core-http-router-server-mocks" - ], - [ - "@kbn/core-http-server", - "packages/core/http/core-http-server" - ], - [ - "@kbn/core-http-server-internal", - "packages/core/http/core-http-server-internal" - ], - [ - "@kbn/core-http-server-mocks", - "packages/core/http/core-http-server-mocks" - ], - [ - "@kbn/core-http-server-utils", - "packages/core/http/core-http-server-utils" - ], - [ - "@kbn/core-i18n-browser", - "packages/core/i18n/core-i18n-browser" - ], - [ - "@kbn/core-i18n-browser-internal", - "packages/core/i18n/core-i18n-browser-internal" - ], - [ - "@kbn/core-i18n-browser-mocks", - "packages/core/i18n/core-i18n-browser-mocks" - ], - [ - "@kbn/core-i18n-server", - "packages/core/i18n/core-i18n-server" - ], - [ - "@kbn/core-i18n-server-internal", - "packages/core/i18n/core-i18n-server-internal" - ], - [ - "@kbn/core-i18n-server-mocks", - "packages/core/i18n/core-i18n-server-mocks" - ], - [ - "@kbn/core-injected-metadata-browser-internal", - "packages/core/injected-metadata/core-injected-metadata-browser-internal" - ], - [ - "@kbn/core-injected-metadata-browser-mocks", - "packages/core/injected-metadata/core-injected-metadata-browser-mocks" - ], - [ - "@kbn/core-injected-metadata-common-internal", - "packages/core/injected-metadata/core-injected-metadata-common-internal" - ], - [ - "@kbn/core-integrations-browser-internal", - "packages/core/integrations/core-integrations-browser-internal" - ], - [ - "@kbn/core-integrations-browser-mocks", - "packages/core/integrations/core-integrations-browser-mocks" - ], - [ - "@kbn/core-lifecycle-browser", - "packages/core/lifecycle/core-lifecycle-browser" - ], - [ - "@kbn/core-lifecycle-browser-internal", - "packages/core/lifecycle/core-lifecycle-browser-internal" - ], - [ - "@kbn/core-lifecycle-browser-mocks", - "packages/core/lifecycle/core-lifecycle-browser-mocks" - ], - [ - "@kbn/core-lifecycle-server", - "packages/core/lifecycle/core-lifecycle-server" - ], - [ - "@kbn/core-lifecycle-server-internal", - "packages/core/lifecycle/core-lifecycle-server-internal" - ], - [ - "@kbn/core-lifecycle-server-mocks", - "packages/core/lifecycle/core-lifecycle-server-mocks" - ], - [ - "@kbn/core-logging-browser-internal", - "packages/core/logging/core-logging-browser-internal" - ], - [ - "@kbn/core-logging-browser-mocks", - "packages/core/logging/core-logging-browser-mocks" - ], - [ - "@kbn/core-logging-common-internal", - "packages/core/logging/core-logging-common-internal" - ], - [ - "@kbn/core-logging-server", - "packages/core/logging/core-logging-server" - ], - [ - "@kbn/core-logging-server-internal", - "packages/core/logging/core-logging-server-internal" - ], - [ - "@kbn/core-logging-server-mocks", - "packages/core/logging/core-logging-server-mocks" - ], - [ - "@kbn/core-metrics-collectors-server-internal", - "packages/core/metrics/core-metrics-collectors-server-internal" - ], - [ - "@kbn/core-metrics-collectors-server-mocks", - "packages/core/metrics/core-metrics-collectors-server-mocks" - ], - [ - "@kbn/core-metrics-server", - "packages/core/metrics/core-metrics-server" - ], - [ - "@kbn/core-metrics-server-internal", - "packages/core/metrics/core-metrics-server-internal" - ], - [ - "@kbn/core-metrics-server-mocks", - "packages/core/metrics/core-metrics-server-mocks" - ], - [ - "@kbn/core-mount-utils-browser", - "packages/core/mount-utils/core-mount-utils-browser" - ], - [ - "@kbn/core-mount-utils-browser-internal", - "packages/core/mount-utils/core-mount-utils-browser-internal" - ], - [ - "@kbn/core-node-server", - "packages/core/node/core-node-server" - ], - [ - "@kbn/core-node-server-internal", - "packages/core/node/core-node-server-internal" - ], - [ - "@kbn/core-node-server-mocks", - "packages/core/node/core-node-server-mocks" - ], - [ - "@kbn/core-notifications-browser", - "packages/core/notifications/core-notifications-browser" - ], - [ - "@kbn/core-notifications-browser-internal", - "packages/core/notifications/core-notifications-browser-internal" - ], - [ - "@kbn/core-notifications-browser-mocks", - "packages/core/notifications/core-notifications-browser-mocks" - ], - [ - "@kbn/core-overlays-browser", - "packages/core/overlays/core-overlays-browser" - ], - [ - "@kbn/core-overlays-browser-internal", - "packages/core/overlays/core-overlays-browser-internal" - ], - [ - "@kbn/core-overlays-browser-mocks", - "packages/core/overlays/core-overlays-browser-mocks" - ], - [ - "@kbn/core-plugin-a-plugin", - "test/plugin_functional/plugins/core_plugin_a" - ], - [ - "@kbn/core-plugin-appleave-plugin", - "test/plugin_functional/plugins/core_plugin_appleave" - ], - [ - "@kbn/core-plugin-b-plugin", - "test/plugin_functional/plugins/core_plugin_b" - ], - [ - "@kbn/core-plugin-chromeless-plugin", - "test/plugin_functional/plugins/core_plugin_chromeless" - ], - [ - "@kbn/core-plugin-deep-links-plugin", - "test/plugin_functional/plugins/core_plugin_deep_links" - ], - [ - "@kbn/core-plugin-deprecations-plugin", - "test/plugin_functional/plugins/core_plugin_deprecations" - ], - [ - "@kbn/core-plugin-dynamic-resolving-a", - "test/plugin_functional/plugins/core_dynamic_resolving_a" - ], - [ - "@kbn/core-plugin-dynamic-resolving-b", - "test/plugin_functional/plugins/core_dynamic_resolving_b" - ], - [ - "@kbn/core-plugin-execution-context-plugin", - "test/plugin_functional/plugins/core_plugin_execution_context" - ], - [ - "@kbn/core-plugin-helpmenu-plugin", - "test/plugin_functional/plugins/core_plugin_helpmenu" - ], - [ - "@kbn/core-plugin-initializer-context-plugin", - "test/node_roles_functional/plugins/core_plugin_initializer_context" - ], - [ - "@kbn/core-plugin-route-timeouts-plugin", - "test/plugin_functional/plugins/core_plugin_route_timeouts" - ], - [ - "@kbn/core-plugin-static-assets-plugin", - "test/plugin_functional/plugins/core_plugin_static_assets" - ], - [ - "@kbn/core-plugins-base-server-internal", - "packages/core/plugins/core-plugins-base-server-internal" - ], - [ - "@kbn/core-plugins-browser", - "packages/core/plugins/core-plugins-browser" - ], - [ - "@kbn/core-plugins-browser-internal", - "packages/core/plugins/core-plugins-browser-internal" - ], - [ - "@kbn/core-plugins-browser-mocks", - "packages/core/plugins/core-plugins-browser-mocks" - ], - [ - "@kbn/core-plugins-contracts-browser", - "packages/core/plugins/core-plugins-contracts-browser" - ], - [ - "@kbn/core-plugins-contracts-server", - "packages/core/plugins/core-plugins-contracts-server" - ], - [ - "@kbn/core-plugins-server", - "packages/core/plugins/core-plugins-server" - ], - [ - "@kbn/core-plugins-server-internal", - "packages/core/plugins/core-plugins-server-internal" - ], - [ - "@kbn/core-plugins-server-mocks", - "packages/core/plugins/core-plugins-server-mocks" - ], - [ - "@kbn/core-preboot-server", - "packages/core/preboot/core-preboot-server" - ], - [ - "@kbn/core-preboot-server-internal", - "packages/core/preboot/core-preboot-server-internal" - ], - [ - "@kbn/core-preboot-server-mocks", - "packages/core/preboot/core-preboot-server-mocks" - ], - [ - "@kbn/core-provider-plugin", - "test/plugin_functional/plugins/core_provider_plugin" - ], - [ - "@kbn/core-rendering-browser", - "packages/core/rendering/core-rendering-browser" - ], - [ - "@kbn/core-rendering-browser-internal", - "packages/core/rendering/core-rendering-browser-internal" - ], - [ - "@kbn/core-rendering-browser-mocks", - "packages/core/rendering/core-rendering-browser-mocks" - ], - [ - "@kbn/core-rendering-server-internal", - "packages/core/rendering/core-rendering-server-internal" - ], - [ - "@kbn/core-rendering-server-mocks", - "packages/core/rendering/core-rendering-server-mocks" - ], - [ - "@kbn/core-root-browser-internal", - "packages/core/root/core-root-browser-internal" - ], - [ - "@kbn/core-root-server-internal", - "packages/core/root/core-root-server-internal" - ], - [ - "@kbn/core-saved-objects-api-browser", - "packages/core/saved-objects/core-saved-objects-api-browser" - ], - [ - "@kbn/core-saved-objects-api-server", - "packages/core/saved-objects/core-saved-objects-api-server" - ], - [ - "@kbn/core-saved-objects-api-server-internal", - "packages/core/saved-objects/core-saved-objects-api-server-internal" - ], - [ - "@kbn/core-saved-objects-api-server-mocks", - "packages/core/saved-objects/core-saved-objects-api-server-mocks" - ], - [ - "@kbn/core-saved-objects-base-server-internal", - "packages/core/saved-objects/core-saved-objects-base-server-internal" - ], - [ - "@kbn/core-saved-objects-base-server-mocks", - "packages/core/saved-objects/core-saved-objects-base-server-mocks" - ], - [ - "@kbn/core-saved-objects-browser", - "packages/core/saved-objects/core-saved-objects-browser" - ], - [ - "@kbn/core-saved-objects-browser-internal", - "packages/core/saved-objects/core-saved-objects-browser-internal" - ], - [ - "@kbn/core-saved-objects-browser-mocks", - "packages/core/saved-objects/core-saved-objects-browser-mocks" - ], - [ - "@kbn/core-saved-objects-common", - "packages/core/saved-objects/core-saved-objects-common" - ], - [ - "@kbn/core-saved-objects-import-export-server-internal", - "packages/core/saved-objects/core-saved-objects-import-export-server-internal" - ], - [ - "@kbn/core-saved-objects-import-export-server-mocks", - "packages/core/saved-objects/core-saved-objects-import-export-server-mocks" - ], - [ - "@kbn/core-saved-objects-migration-server-internal", - "packages/core/saved-objects/core-saved-objects-migration-server-internal" - ], - [ - "@kbn/core-saved-objects-migration-server-mocks", - "packages/core/saved-objects/core-saved-objects-migration-server-mocks" - ], - [ - "@kbn/core-saved-objects-server", - "packages/core/saved-objects/core-saved-objects-server" - ], - [ - "@kbn/core-saved-objects-server-internal", - "packages/core/saved-objects/core-saved-objects-server-internal" - ], - [ - "@kbn/core-saved-objects-server-mocks", - "packages/core/saved-objects/core-saved-objects-server-mocks" - ], - [ - "@kbn/core-saved-objects-utils-server", - "packages/core/saved-objects/core-saved-objects-utils-server" - ], - [ - "@kbn/core-security-browser", - "packages/core/security/core-security-browser" - ], - [ - "@kbn/core-security-browser-internal", - "packages/core/security/core-security-browser-internal" - ], - [ - "@kbn/core-security-browser-mocks", - "packages/core/security/core-security-browser-mocks" - ], - [ - "@kbn/core-security-common", - "packages/core/security/core-security-common" - ], - [ - "@kbn/core-security-server", - "packages/core/security/core-security-server" - ], - [ - "@kbn/core-security-server-internal", - "packages/core/security/core-security-server-internal" - ], - [ - "@kbn/core-security-server-mocks", - "packages/core/security/core-security-server-mocks" - ], - [ - "@kbn/core-status-common", - "packages/core/status/core-status-common" - ], - [ - "@kbn/core-status-server", - "packages/core/status/core-status-server" - ], - [ - "@kbn/core-status-server-internal", - "packages/core/status/core-status-server-internal" - ], - [ - "@kbn/core-status-server-mocks", - "packages/core/status/core-status-server-mocks" - ], - [ - "@kbn/core-test-helpers-deprecations-getters", - "packages/core/test-helpers/core-test-helpers-deprecations-getters" - ], - [ - "@kbn/core-test-helpers-http-setup-browser", - "packages/core/test-helpers/core-test-helpers-http-setup-browser" - ], - [ - "@kbn/core-test-helpers-kbn-server", - "packages/core/test-helpers/core-test-helpers-kbn-server" - ], - [ - "@kbn/core-test-helpers-model-versions", - "packages/core/test-helpers/core-test-helpers-model-versions" - ], - [ - "@kbn/core-test-helpers-so-type-serializer", - "packages/core/test-helpers/core-test-helpers-so-type-serializer" - ], - [ - "@kbn/core-test-helpers-test-utils", - "packages/core/test-helpers/core-test-helpers-test-utils" - ], - [ - "@kbn/core-theme-browser", - "packages/core/theme/core-theme-browser" - ], - [ - "@kbn/core-theme-browser-internal", - "packages/core/theme/core-theme-browser-internal" - ], - [ - "@kbn/core-theme-browser-mocks", - "packages/core/theme/core-theme-browser-mocks" - ], - [ - "@kbn/core-ui-settings-browser", - "packages/core/ui-settings/core-ui-settings-browser" - ], - [ - "@kbn/core-ui-settings-browser-internal", - "packages/core/ui-settings/core-ui-settings-browser-internal" - ], - [ - "@kbn/core-ui-settings-browser-mocks", - "packages/core/ui-settings/core-ui-settings-browser-mocks" - ], - [ - "@kbn/core-ui-settings-common", - "packages/core/ui-settings/core-ui-settings-common" - ], - [ - "@kbn/core-ui-settings-server", - "packages/core/ui-settings/core-ui-settings-server" - ], - [ - "@kbn/core-ui-settings-server-internal", - "packages/core/ui-settings/core-ui-settings-server-internal" - ], - [ - "@kbn/core-ui-settings-server-mocks", - "packages/core/ui-settings/core-ui-settings-server-mocks" - ], - [ - "@kbn/core-usage-data-base-server-internal", - "packages/core/usage-data/core-usage-data-base-server-internal" - ], - [ - "@kbn/core-usage-data-server", - "packages/core/usage-data/core-usage-data-server" - ], - [ - "@kbn/core-usage-data-server-internal", - "packages/core/usage-data/core-usage-data-server-internal" - ], - [ - "@kbn/core-usage-data-server-mocks", - "packages/core/usage-data/core-usage-data-server-mocks" - ], - [ - "@kbn/core-user-profile-browser", - "packages/core/user-profile/core-user-profile-browser" - ], - [ - "@kbn/core-user-profile-browser-internal", - "packages/core/user-profile/core-user-profile-browser-internal" - ], - [ - "@kbn/core-user-profile-browser-mocks", - "packages/core/user-profile/core-user-profile-browser-mocks" - ], - [ - "@kbn/core-user-profile-common", - "packages/core/user-profile/core-user-profile-common" - ], - [ - "@kbn/core-user-profile-server", - "packages/core/user-profile/core-user-profile-server" - ], - [ - "@kbn/core-user-profile-server-internal", - "packages/core/user-profile/core-user-profile-server-internal" - ], - [ - "@kbn/core-user-profile-server-mocks", - "packages/core/user-profile/core-user-profile-server-mocks" - ], - [ - "@kbn/core-user-settings-server", - "packages/core/user-settings/core-user-settings-server" - ], - [ - "@kbn/core-user-settings-server-internal", - "packages/core/user-settings/core-user-settings-server-internal" - ], - [ - "@kbn/core-user-settings-server-mocks", - "packages/core/user-settings/core-user-settings-server-mocks" - ], - [ - "@kbn/cross-cluster-replication-plugin", - "x-pack/platform/plugins/private/cross_cluster_replication" - ], - [ - "@kbn/crypto", - "packages/kbn-crypto" - ], - [ - "@kbn/crypto-browser", - "packages/kbn-crypto-browser" - ], - [ - "@kbn/custom-branding-plugin", - "x-pack/plugins/custom_branding" - ], - [ - "@kbn/custom-icons", - "src/platform/packages/shared/kbn-custom-icons" - ], - [ - "@kbn/custom-integrations", - "x-pack/solutions/observability/packages/kbn-custom-integrations" - ], - [ - "@kbn/custom-integrations-plugin", - "src/platform/plugins/shared/custom_integrations" - ], - [ - "@kbn/cypress-config", - "packages/kbn-cypress-config" - ], - [ - "@kbn/dashboard-enhanced-plugin", - "x-pack/platform/plugins/shared/dashboard_enhanced" - ], - [ - "@kbn/dashboard-plugin", - "src/platform/plugins/shared/dashboard" - ], - [ - "@kbn/data-forge", - "x-pack/platform/packages/shared/kbn-data-forge" - ], - [ - "@kbn/data-plugin", - "src/plugins/data" - ], - [ - "@kbn/data-quality-plugin", - "x-pack/platform/plugins/shared/data_quality" - ], - [ - "@kbn/data-search-plugin", - "test/plugin_functional/plugins/data_search" - ], - [ - "@kbn/data-service", - "packages/kbn-data-service" - ], - [ - "@kbn/data-stream-adapter", - "x-pack/solutions/security/packages/data-stream-adapter" - ], - [ - "@kbn/data-usage-plugin", - "x-pack/platform/plugins/private/data_usage" - ], - [ - "@kbn/data-view-editor-plugin", - "src/platform/plugins/shared/data_view_editor" - ], - [ - "@kbn/data-view-field-editor-example-plugin", - "examples/data_view_field_editor_example" - ], - [ - "@kbn/data-view-field-editor-plugin", - "src/platform/plugins/shared/data_view_field_editor" - ], - [ - "@kbn/data-view-management-plugin", - "src/platform/plugins/shared/data_view_management" - ], - [ - "@kbn/data-view-utils", - "src/platform/packages/shared/kbn-data-view-utils" - ], - [ - "@kbn/data-views-plugin", - "src/platform/plugins/shared/data_views" - ], - [ - "@kbn/data-visualizer-plugin", - "x-pack/platform/plugins/private/data_visualizer" - ], - [ - "@kbn/dataset-quality-plugin", - "x-pack/platform/plugins/shared/dataset_quality" - ], - [ - "@kbn/datemath", - "src/platform/packages/shared/kbn-datemath" - ], - [ - "@kbn/deeplinks-analytics", - "src/platform/packages/shared/deeplinks/analytics" - ], - [ - "@kbn/deeplinks-devtools", - "src/platform/packages/shared/deeplinks/devtools" - ], - [ - "@kbn/deeplinks-fleet", - "src/platform/packages/shared/deeplinks/fleet" - ], - [ - "@kbn/deeplinks-management", - "src/platform/packages/shared/deeplinks/management" - ], - [ - "@kbn/deeplinks-ml", - "src/platform/packages/shared/deeplinks/ml" - ], - [ - "@kbn/deeplinks-observability", - "src/platform/packages/shared/deeplinks/observability" - ], - [ - "@kbn/deeplinks-search", - "src/platform/packages/shared/deeplinks/search" - ], - [ - "@kbn/deeplinks-security", - "src/platform/packages/shared/deeplinks/security" - ], - [ - "@kbn/deeplinks-shared", - "packages/deeplinks/shared" - ], - [ - "@kbn/default-nav-analytics", - "src/platform/packages/private/default-nav/analytics" - ], - [ - "@kbn/default-nav-devtools", - "src/platform/packages/private/default-nav/devtools" - ], - [ - "@kbn/default-nav-management", - "src/platform/packages/private/default-nav/management" - ], - [ - "@kbn/default-nav-ml", - "src/platform/packages/private/default-nav/ml" - ], - [ - "@kbn/dependency-ownership", - "packages/kbn-dependency-ownership" - ], - [ - "@kbn/dependency-usage", - "packages/kbn-dependency-usage" - ], - [ - "@kbn/dev-cli-errors", - "packages/kbn-dev-cli-errors" - ], - [ - "@kbn/dev-cli-runner", - "packages/kbn-dev-cli-runner" - ], - [ - "@kbn/dev-proc-runner", - "packages/kbn-dev-proc-runner" - ], - [ - "@kbn/dev-tools-plugin", - "src/platform/plugins/shared/dev_tools" - ], - [ - "@kbn/dev-utils", - "packages/kbn-dev-utils" - ], - [ - "@kbn/developer-examples-plugin", - "examples/developer_examples" - ], - [ - "@kbn/discover-contextual-components", - "src/platform/packages/shared/kbn-discover-contextual-components" - ], - [ - "@kbn/discover-customization-examples-plugin", - "examples/discover_customization_examples" - ], - [ - "@kbn/discover-enhanced-plugin", - "x-pack/platform/plugins/private/discover_enhanced" - ], - [ - "@kbn/discover-plugin", - "src/platform/plugins/shared/discover" - ], - [ - "@kbn/discover-shared-plugin", - "src/platform/plugins/shared/discover_shared" - ], - [ - "@kbn/discover-utils", - "src/platform/packages/shared/kbn-discover-utils" - ], - [ - "@kbn/doc-links", - "src/platform/packages/shared/kbn-doc-links" - ], - [ - "@kbn/docs-utils", - "packages/kbn-docs-utils" - ], - [ - "@kbn/dom-drag-drop", - "packages/kbn-dom-drag-drop" - ], - [ - "@kbn/ebt-tools", - "packages/kbn-ebt-tools" - ], - [ - "@kbn/ecs-data-quality-dashboard", - "x-pack/solutions/security/packages/ecs_data_quality_dashboard" - ], - [ - "@kbn/ecs-data-quality-dashboard-plugin", - "x-pack/solutions/security/plugins/ecs_data_quality_dashboard" - ], - [ - "@kbn/elastic-agent-utils", - "src/platform/packages/shared/kbn-elastic-agent-utils" - ], - [ - "@kbn/elastic-assistant", - "x-pack/platform/packages/shared/kbn-elastic-assistant" - ], - [ - "@kbn/elastic-assistant-common", - "x-pack/platform/packages/shared/kbn-elastic-assistant-common" - ], - [ - "@kbn/elastic-assistant-plugin", - "x-pack/solutions/security/plugins/elastic_assistant" - ], - [ - "@kbn/elasticsearch-client-plugin", - "test/plugin_functional/plugins/elasticsearch_client_plugin" - ], - [ - "@kbn/elasticsearch-client-xpack-plugin", - "x-pack/test/plugin_api_integration/plugins/elasticsearch_client" - ], - [ - "@kbn/embeddable-enhanced-plugin", - "x-pack/platform/plugins/shared/embeddable_enhanced" - ], - [ - "@kbn/embeddable-examples-plugin", - "examples/embeddable_examples" - ], - [ - "@kbn/embeddable-plugin", - "src/platform/plugins/shared/embeddable" - ], - [ - "@kbn/embedded-lens-example-plugin", - "x-pack/examples/embedded_lens_example" - ], - [ - "@kbn/encrypted-saved-objects-plugin", - "x-pack/plugins/encrypted_saved_objects" - ], - [ - "@kbn/enterprise-search-plugin", - "x-pack/solutions/search/plugins/enterprise_search" - ], - [ - "@kbn/entities-data-access-plugin", - "x-pack/solutions/observability/plugins/observability_solution/entities_data_access" - ], - [ - "@kbn/entities-schema", - "x-pack/platform/packages/shared/kbn-entities-schema" - ], - [ - "@kbn/entity-manager-fixture-plugin", - "x-pack/test/api_integration/apis/entity_manager/fixture_plugin" - ], - [ - "@kbn/entityManager-app-plugin", - "x-pack/solutions/observability/plugins/observability_solution/entity_manager_app" - ], - [ - "@kbn/entityManager-plugin", - "x-pack/platform/plugins/shared/entity_manager" - ], - [ - "@kbn/error-boundary-example-plugin", - "examples/error_boundary" - ], - [ - "@kbn/es", - "packages/kbn-es" - ], - [ - "@kbn/es-archiver", - "packages/kbn-es-archiver" - ], - [ - "@kbn/es-errors", - "packages/kbn-es-errors" - ], - [ - "@kbn/es-query", - "src/platform/packages/shared/kbn-es-query" - ], - [ - "@kbn/es-types", - "packages/kbn-es-types" - ], - [ - "@kbn/es-ui-shared-plugin", - "src/platform/plugins/shared/es_ui_shared" - ], - [ - "@kbn/eslint-config", - "packages/kbn-eslint-config" - ], - [ - "@kbn/eslint-plugin-css", - "packages/kbn-eslint-plugin-css" - ], - [ - "@kbn/eslint-plugin-disable", - "packages/kbn-eslint-plugin-disable" - ], - [ - "@kbn/eslint-plugin-eslint", - "packages/kbn-eslint-plugin-eslint" - ], - [ - "@kbn/eslint-plugin-i18n", - "packages/kbn-eslint-plugin-i18n" - ], - [ - "@kbn/eslint-plugin-imports", - "packages/kbn-eslint-plugin-imports" - ], - [ - "@kbn/eslint-plugin-telemetry", - "packages/kbn-eslint-plugin-telemetry" - ], - [ - "@kbn/eso-model-version-example", - "examples/eso_model_version_example" - ], - [ - "@kbn/eso-plugin", - "x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin" - ], - [ - "@kbn/esql", - "src/platform/plugins/shared/esql" - ], - [ - "@kbn/esql-ast", - "src/platform/packages/shared/kbn-esql-ast" - ], - [ - "@kbn/esql-ast-inspector-plugin", - "examples/esql_ast_inspector" - ], - [ - "@kbn/esql-datagrid", - "src/platform/plugins/shared/esql_datagrid" - ], - [ - "@kbn/esql-editor", - "src/platform/packages/private/kbn-esql-editor" - ], - [ - "@kbn/esql-utils", - "src/platform/packages/shared/kbn-esql-utils" - ], - [ - "@kbn/esql-validation-autocomplete", - "src/platform/packages/shared/kbn-esql-validation-autocomplete" - ], - [ - "@kbn/esql-validation-example-plugin", - "examples/esql_validation_example" - ], - [ - "@kbn/eui-provider-dev-warning", - "test/plugin_functional/plugins/eui_provider_dev_warning" - ], - [ - "@kbn/event-annotation-common", - "packages/kbn-event-annotation-common" - ], - [ - "@kbn/event-annotation-components", - "packages/kbn-event-annotation-components" - ], - [ - "@kbn/event-annotation-listing-plugin", - "src/plugins/event_annotation_listing" - ], - [ - "@kbn/event-annotation-plugin", - "src/plugins/event_annotation" - ], - [ - "@kbn/event-log-fixture-plugin", - "x-pack/test/plugin_api_integration/plugins/event_log" - ], - [ - "@kbn/event-log-plugin", - "x-pack/platform/plugins/shared/event_log" - ], - [ - "@kbn/expandable-flyout", - "x-pack/solutions/security/packages/expandable-flyout" - ], - [ - "@kbn/expect", - "packages/kbn-expect" - ], - [ - "@kbn/exploratory-view-example-plugin", - "x-pack/examples/exploratory_view_example" - ], - [ - "@kbn/exploratory-view-plugin", - "x-pack/solutions/observability/plugins/exploratory_view" - ], - [ - "@kbn/expression-error-plugin", - "src/platform/plugins/shared/expression_error" - ], - [ - "@kbn/expression-gauge-plugin", - "src/plugins/chart_expressions/expression_gauge" - ], - [ - "@kbn/expression-heatmap-plugin", - "src/plugins/chart_expressions/expression_heatmap" - ], - [ - "@kbn/expression-image-plugin", - "src/platform/plugins/shared/expression_image" - ], - [ - "@kbn/expression-legacy-metric-vis-plugin", - "src/plugins/chart_expressions/expression_legacy_metric" - ], - [ - "@kbn/expression-metric-plugin", - "src/platform/plugins/shared/expression_metric" - ], - [ - "@kbn/expression-metric-vis-plugin", - "src/plugins/chart_expressions/expression_metric" - ], - [ - "@kbn/expression-partition-vis-plugin", - "src/plugins/chart_expressions/expression_partition_vis" - ], - [ - "@kbn/expression-repeat-image-plugin", - "src/platform/plugins/shared/expression_repeat_image" - ], - [ - "@kbn/expression-reveal-image-plugin", - "src/platform/plugins/shared/expression_reveal_image" - ], - [ - "@kbn/expression-shape-plugin", - "src/platform/plugins/shared/expression_shape" - ], - [ - "@kbn/expression-tagcloud-plugin", - "src/plugins/chart_expressions/expression_tagcloud" - ], - [ - "@kbn/expression-xy-plugin", - "src/plugins/chart_expressions/expression_xy" - ], - [ - "@kbn/expressions-explorer-plugin", - "examples/expressions_explorer" - ], - [ - "@kbn/expressions-plugin", - "src/plugins/expressions" - ], - [ - "@kbn/failed-test-reporter-cli", - "packages/kbn-failed-test-reporter-cli" - ], - [ - "@kbn/feature-controls-examples-plugin", - "examples/feature_control_examples" - ], - [ - "@kbn/feature-flags-example-plugin", - "examples/feature_flags_example" - ], - [ - "@kbn/feature-usage-test-plugin", - "x-pack/test/plugin_api_integration/plugins/feature_usage_test" - ], - [ - "@kbn/features-plugin", - "x-pack/plugins/features" - ], - [ - "@kbn/features-provider-plugin", - "x-pack/test/security_api_integration/plugins/features_provider" - ], - [ - "@kbn/fec-alerts-test-plugin", - "x-pack/test/functional_execution_context/plugins/alerts" - ], - [ - "@kbn/field-formats-example-plugin", - "examples/field_formats_example" - ], - [ - "@kbn/field-formats-plugin", - "src/platform/plugins/shared/field_formats" - ], - [ - "@kbn/field-types", - "src/platform/packages/shared/kbn-field-types" - ], - [ - "@kbn/field-utils", - "src/platform/packages/shared/kbn-field-utils" - ], - [ - "@kbn/fields-metadata-plugin", - "x-pack/platform/plugins/shared/fields_metadata" - ], - [ - "@kbn/file-upload-plugin", - "x-pack/platform/plugins/private/file_upload" - ], - [ - "@kbn/files-example-plugin", - "examples/files_example" - ], - [ - "@kbn/files-management-plugin", - "src/plugins/files_management" - ], - [ - "@kbn/files-plugin", - "src/plugins/files" - ], - [ - "@kbn/find-used-node-modules", - "packages/kbn-find-used-node-modules" - ], - [ - "@kbn/fleet-plugin", - "x-pack/platform/plugins/shared/fleet" - ], - [ - "@kbn/flot-charts", - "src/platform/packages/shared/kbn-flot-charts" - ], - [ - "@kbn/foo-plugin", - "x-pack/test/ui_capabilities/common/plugins/foo_plugin" - ], - [ - "@kbn/formatters", - "packages/kbn-formatters" - ], - [ - "@kbn/ftr-apis-plugin", - "src/plugins/ftr_apis" - ], - [ - "@kbn/ftr-common-functional-services", - "packages/kbn-ftr-common-functional-services" - ], - [ - "@kbn/ftr-common-functional-ui-services", - "packages/kbn-ftr-common-functional-ui-services" - ], - [ - "@kbn/ftr-screenshot-filename", - "packages/kbn-ftr-screenshot-filename" - ], - [ - "@kbn/functional-with-es-ssl-cases-test-plugin", - "x-pack/test/functional_with_es_ssl/plugins/cases" - ], - [ - "@kbn/gen-ai-functional-testing", - "packages/kbn-gen-ai-functional-testing" - ], - [ - "@kbn/gen-ai-streaming-response-example-plugin", - "x-pack/examples/gen_ai_streaming_response_example" - ], - [ - "@kbn/generate", - "packages/kbn-generate" - ], - [ - "@kbn/generate-console-definitions", - "packages/kbn-generate-console-definitions" - ], - [ - "@kbn/generate-csv", - "packages/kbn-generate-csv" - ], - [ - "@kbn/get-repo-files", - "packages/kbn-get-repo-files" - ], - [ - "@kbn/global-search-bar-plugin", - "x-pack/plugins/global_search_bar" - ], - [ - "@kbn/global-search-plugin", - "x-pack/plugins/global_search" - ], - [ - "@kbn/global-search-providers-plugin", - "x-pack/plugins/global_search_providers" - ], - [ - "@kbn/global-search-test-plugin", - "x-pack/test/plugin_functional/plugins/global_search_test" - ], - [ - "@kbn/graph-plugin", - "x-pack/plugins/graph" - ], - [ - "@kbn/grid-example-plugin", - "examples/grid_example" - ], - [ - "@kbn/grid-layout", - "packages/kbn-grid-layout" - ], - [ - "@kbn/grokdebugger-plugin", - "x-pack/platform/plugins/private/grokdebugger" - ], - [ - "@kbn/grouping", - "src/platform/packages/shared/kbn-grouping" - ], - [ - "@kbn/guided-onboarding", - "packages/kbn-guided-onboarding" - ], - [ - "@kbn/guided-onboarding-example-plugin", - "examples/guided_onboarding_example" - ], - [ - "@kbn/guided-onboarding-plugin", - "src/plugins/guided_onboarding" - ], - [ - "@kbn/handlebars", - "packages/kbn-handlebars" - ], - [ - "@kbn/hapi-mocks", - "packages/kbn-hapi-mocks" - ], - [ - "@kbn/hardening-plugin", - "test/plugin_functional/plugins/hardening" - ], - [ - "@kbn/health-gateway-server", - "packages/kbn-health-gateway-server" - ], - [ - "@kbn/hello-world-plugin", - "examples/hello_world" - ], - [ - "@kbn/home-plugin", - "src/plugins/home" - ], - [ - "@kbn/home-sample-data-card", - "packages/home/sample_data_card" - ], - [ - "@kbn/home-sample-data-tab", - "packages/home/sample_data_tab" - ], - [ - "@kbn/home-sample-data-types", - "packages/home/sample_data_types" - ], - [ - "@kbn/i18n", - "packages/kbn-i18n" - ], - [ - "@kbn/i18n-react", - "packages/kbn-i18n-react" - ], - [ - "@kbn/iframe-embedded-plugin", - "x-pack/test/functional_embedded/plugins/iframe_embedded" - ], - [ - "@kbn/image-embeddable-plugin", - "src/plugins/image_embeddable" - ], - [ - "@kbn/import-locator", - "packages/kbn-import-locator" - ], - [ - "@kbn/import-resolver", - "packages/kbn-import-resolver" - ], - [ - "@kbn/index-adapter", - "x-pack/solutions/security/packages/index-adapter" - ], - [ - "@kbn/index-lifecycle-management-common-shared", - "x-pack/platform/packages/shared/index-lifecycle-management/index_lifecycle_management_common_shared" - ], - [ - "@kbn/index-lifecycle-management-plugin", - "x-pack/platform/plugins/private/index_lifecycle_management" - ], - [ - "@kbn/index-management-plugin", - "x-pack/platform/plugins/shared/index_management" - ], - [ - "@kbn/index-management-shared-types", - "x-pack/platform/packages/shared/index-management/index_management_shared_types" - ], - [ - "@kbn/index-patterns-test-plugin", - "test/plugin_functional/plugins/index_patterns" - ], - [ - "@kbn/inference_integration_flyout", - "x-pack/platform/packages/private/ml/inference_integration_flyout" - ], - [ - "@kbn/inference-common", - "x-pack/platform/packages/shared/ai-infra/inference-common" - ], - [ - "@kbn/inference-endpoint-ui-common", - "x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common" - ], - [ - "@kbn/inference-plugin", - "x-pack/platform/plugins/shared/inference" - ], - [ - "@kbn/infra-forge", - "x-pack/platform/packages/private/kbn-infra-forge" - ], - [ - "@kbn/infra-plugin", - "x-pack/solutions/observability/plugins/infra" - ], - [ - "@kbn/ingest-pipelines-plugin", - "x-pack/platform/plugins/shared/ingest_pipelines" - ], - [ - "@kbn/input-control-vis-plugin", - "src/platform/plugins/private/input_control_vis" - ], - [ - "@kbn/inspector-plugin", - "src/platform/plugins/shared/inspector" - ], - [ - "@kbn/integration-assistant-plugin", - "x-pack/platform/plugins/shared/integration_assistant" - ], - [ - "@kbn/interactive-setup-plugin", - "src/plugins/interactive_setup" - ], - [ - "@kbn/interactive-setup-test-endpoints-plugin", - "test/interactive_setup_api_integration/plugins/test_endpoints" - ], - [ - "@kbn/interpreter", - "packages/kbn-interpreter" - ], - [ - "@kbn/inventory-e2e", - "x-pack/solutions/observability/plugins/inventory/e2e" - ], - [ - "@kbn/inventory-plugin", - "x-pack/solutions/observability/plugins/inventory" - ], - [ - "@kbn/investigate-app-plugin", - "x-pack/solutions/observability/plugins/investigate_app" - ], - [ - "@kbn/investigate-plugin", - "x-pack/solutions/observability/plugins/investigate" - ], - [ - "@kbn/investigation-shared", - "x-pack/solutions/observability/packages/kbn-investigation-shared" - ], - [ - "@kbn/io-ts-utils", - "src/platform/packages/shared/kbn-io-ts-utils" - ], - [ - "@kbn/ipynb", - "x-pack/solutions/search/packages/kbn-ipynb" - ], - [ - "@kbn/item-buffer", - "packages/kbn-item-buffer" - ], - [ - "@kbn/jest-serializers", - "packages/kbn-jest-serializers" - ], - [ - "@kbn/journeys", - "packages/kbn-journeys" - ], - [ - "@kbn/json-ast", - "packages/kbn-json-ast" - ], - [ - "@kbn/json-schemas", - "x-pack/platform/packages/private/ml/json_schemas" - ], - [ - "@kbn/kbn-health-gateway-status-plugin", - "test/health_gateway/plugins/status" - ], - [ - "@kbn/kbn-sample-panel-action-plugin", - "test/plugin_functional/plugins/kbn_sample_panel_action" - ], - [ - "@kbn/kbn-top-nav-plugin", - "test/plugin_functional/plugins/kbn_top_nav" - ], - [ - "@kbn/kbn-tp-custom-visualizations-plugin", - "test/plugin_functional/plugins/kbn_tp_custom_visualizations" - ], - [ - "@kbn/kbn-tp-run-pipeline-plugin", - "test/interpreter_functional/plugins/kbn_tp_run_pipeline" - ], - [ - "@kbn/kibana-cors-test-plugin", - "x-pack/test/functional_cors/plugins/kibana_cors_test" - ], - [ - "@kbn/kibana-manifest-schema", - "packages/kbn-kibana-manifest-schema" - ], - [ - "@kbn/kibana-overview-plugin", - "src/plugins/kibana_overview" - ], - [ - "@kbn/kibana-react-plugin", - "src/plugins/kibana_react" - ], - [ - "@kbn/kibana-usage-collection-plugin", - "src/plugins/kibana_usage_collection" - ], - [ - "@kbn/kibana-utils-plugin", - "src/plugins/kibana_utils" - ], - [ - "@kbn/kubernetes-security-plugin", - "x-pack/solutions/security/plugins/kubernetes_security" - ], - [ - "@kbn/langchain", - "x-pack/platform/packages/shared/kbn-langchain" - ], - [ - "@kbn/language-documentation", - "src/platform/packages/private/kbn-language-documentation" - ], - [ - "@kbn/lens-config-builder-example-plugin", - "x-pack/examples/lens_config_builder_example" - ], - [ - "@kbn/lens-embeddable-utils", - "src/platform/packages/shared/kbn-lens-embeddable-utils" - ], - [ - "@kbn/lens-formula-docs", - "packages/kbn-lens-formula-docs" - ], - [ - "@kbn/lens-inline-editing-example-plugin", - "x-pack/examples/lens_embeddable_inline_editing_example" - ], - [ - "@kbn/lens-plugin", - "x-pack/plugins/lens" - ], - [ - "@kbn/license-api-guard-plugin", - "x-pack/platform/plugins/private/license_api_guard" - ], - [ - "@kbn/license-management-plugin", - "x-pack/platform/plugins/shared/license_management" - ], - [ - "@kbn/licensing-plugin", - "x-pack/plugins/licensing" - ], - [ - "@kbn/links-plugin", - "src/platform/plugins/private/links" - ], - [ - "@kbn/lint-packages-cli", - "packages/kbn-lint-packages-cli" - ], - [ - "@kbn/lint-ts-projects-cli", - "packages/kbn-lint-ts-projects-cli" - ], - [ - "@kbn/lists-plugin", - "x-pack/solutions/security/plugins/lists" - ], - [ - "@kbn/llm-tasks-plugin", - "x-pack/platform/plugins/shared/ai_infra/llm_tasks" - ], - [ - "@kbn/locator-examples-plugin", - "examples/locator_examples" - ], - [ - "@kbn/locator-explorer-plugin", - "examples/locator_explorer" - ], - [ - "@kbn/logging", - "packages/kbn-logging" - ], - [ - "@kbn/logging-mocks", - "packages/kbn-logging-mocks" - ], - [ - "@kbn/logs-data-access-plugin", - "x-pack/platform/plugins/shared/logs_data_access" - ], - [ - "@kbn/logs-explorer-plugin", - "x-pack/solutions/observability/plugins/logs_explorer" - ], - [ - "@kbn/logs-shared-plugin", - "x-pack/platform/plugins/shared/logs_shared" - ], - [ - "@kbn/logstash-plugin", - "x-pack/platform/plugins/private/logstash" - ], - [ - "@kbn/managed-content-badge", - "packages/kbn-managed-content-badge" - ], - [ - "@kbn/managed-vscode-config", - "packages/kbn-managed-vscode-config" - ], - [ - "@kbn/managed-vscode-config-cli", - "packages/kbn-managed-vscode-config-cli" - ], - [ - "@kbn/management-cards-navigation", - "src/platform/packages/shared/kbn-management/cards_navigation" - ], - [ - "@kbn/management-plugin", - "src/platform/plugins/shared/management" - ], - [ - "@kbn/management-settings-application", - "src/platform/packages/private/kbn-management/settings/application" - ], - [ - "@kbn/management-settings-components-field-category", - "src/platform/packages/private/kbn-management/settings/components/field_category" - ], - [ - "@kbn/management-settings-components-field-input", - "src/platform/packages/shared/kbn-management/settings/components/field_input" - ], - [ - "@kbn/management-settings-components-field-row", - "src/platform/packages/shared/kbn-management/settings/components/field_row" - ], - [ - "@kbn/management-settings-components-form", - "src/platform/packages/private/kbn-management/settings/components/form" - ], - [ - "@kbn/management-settings-field-definition", - "src/platform/packages/shared/kbn-management/settings/field_definition" - ], - [ - "@kbn/management-settings-ids", - "packages/kbn-management/settings/setting_ids" - ], - [ - "@kbn/management-settings-section-registry", - "packages/kbn-management/settings/section_registry" - ], - [ - "@kbn/management-settings-types", - "src/platform/packages/shared/kbn-management/settings/types" - ], - [ - "@kbn/management-settings-utilities", - "src/platform/packages/shared/kbn-management/settings/utilities" - ], - [ - "@kbn/management-storybook-config", - "packages/kbn-management/storybook/config" - ], - [ - "@kbn/management-test-plugin", - "test/plugin_functional/plugins/management_test_plugin" - ], - [ - "@kbn/manifest", - "packages/kbn-manifest" - ], - [ - "@kbn/mapbox-gl", - "src/platform/packages/private/kbn-mapbox-gl" - ], - [ - "@kbn/maps-custom-raster-source-plugin", - "x-pack/examples/third_party_maps_source_example" - ], - [ - "@kbn/maps-ems-plugin", - "src/platform/plugins/private/maps_ems" - ], - [ - "@kbn/maps-plugin", - "x-pack/platform/plugins/shared/maps" - ], - [ - "@kbn/maps-vector-tile-utils", - "x-pack/platform/packages/private/maps/vector_tile_utils" - ], - [ - "@kbn/metrics-data-access-plugin", - "x-pack/solutions/observability/plugins/metrics_data_access" - ], - [ - "@kbn/ml-agg-utils", - "x-pack/platform/packages/private/ml/agg_utils" - ], - [ - "@kbn/ml-anomaly-utils", - "x-pack/platform/packages/shared/ml/anomaly_utils" - ], - [ - "@kbn/ml-cancellable-search", - "x-pack/platform/packages/private/ml/cancellable_search" - ], - [ - "@kbn/ml-category-validator", - "x-pack/platform/packages/private/ml/category_validator" - ], - [ - "@kbn/ml-chi2test", - "x-pack/platform/packages/shared/ml/chi2test" - ], - [ - "@kbn/ml-creation-wizard-utils", - "x-pack/platform/packages/private/ml/creation_wizard_utils" - ], - [ - "@kbn/ml-data-frame-analytics-utils", - "x-pack/platform/packages/private/ml/data_frame_analytics_utils" - ], - [ - "@kbn/ml-data-grid", - "x-pack/platform/packages/private/ml/data_grid" - ], - [ - "@kbn/ml-data-view-utils", - "x-pack/platform/packages/private/ml/data_view_utils" - ], - [ - "@kbn/ml-date-picker", - "x-pack/platform/packages/private/ml/date_picker" - ], - [ - "@kbn/ml-date-utils", - "x-pack/platform/packages/private/ml/date_utils" - ], - [ - "@kbn/ml-error-utils", - "x-pack/platform/packages/shared/ml/error_utils" - ], - [ - "@kbn/ml-field-stats-flyout", - "x-pack/platform/packages/private/ml/field_stats_flyout" - ], - [ - "@kbn/ml-in-memory-table", - "x-pack/platform/packages/private/ml/in_memory_table" - ], - [ - "@kbn/ml-is-defined", - "x-pack/platform/packages/private/ml/is_defined" - ], - [ - "@kbn/ml-is-populated-object", - "x-pack/platform/packages/private/ml/is_populated_object" - ], - [ - "@kbn/ml-kibana-theme", - "x-pack/platform/packages/private/ml/kibana_theme" - ], - [ - "@kbn/ml-local-storage", - "x-pack/platform/packages/private/ml/local_storage" - ], - [ - "@kbn/ml-nested-property", - "x-pack/platform/packages/private/ml/nested_property" - ], - [ - "@kbn/ml-number-utils", - "x-pack/platform/packages/private/ml/number_utils" - ], - [ - "@kbn/ml-parse-interval", - "x-pack/platform/packages/private/ml/parse_interval" - ], - [ - "@kbn/ml-plugin", - "x-pack/platform/plugins/shared/ml" - ], - [ - "@kbn/ml-query-utils", - "x-pack/platform/packages/private/ml/query_utils" - ], - [ - "@kbn/ml-random-sampler-utils", - "x-pack/platform/packages/shared/ml/random_sampler_utils" - ], - [ - "@kbn/ml-response-stream", - "x-pack/platform/packages/shared/ml/response_stream" - ], - [ - "@kbn/ml-route-utils", - "x-pack/platform/packages/private/ml/route_utils" - ], - [ - "@kbn/ml-runtime-field-utils", - "x-pack/platform/packages/shared/ml/runtime_field_utils" - ], - [ - "@kbn/ml-string-hash", - "x-pack/platform/packages/private/ml/string_hash" - ], - [ - "@kbn/ml-time-buckets", - "x-pack/platform/packages/private/ml/time_buckets" - ], - [ - "@kbn/ml-trained-models-utils", - "x-pack/platform/packages/shared/ml/trained_models_utils" - ], - [ - "@kbn/ml-ui-actions", - "x-pack/platform/packages/private/ml/ui_actions" - ], - [ - "@kbn/ml-url-state", - "x-pack/platform/packages/private/ml/url_state" - ], - [ - "@kbn/ml-validators", - "x-pack/platform/packages/private/ml/validators" - ], - [ - "@kbn/mock-idp-plugin", - "packages/kbn-mock-idp-plugin" - ], - [ - "@kbn/mock-idp-utils", - "packages/kbn-mock-idp-utils" - ], - [ - "@kbn/monaco", - "packages/kbn-monaco" - ], - [ - "@kbn/monitoring-collection-plugin", - "x-pack/platform/plugins/private/monitoring_collection" - ], - [ - "@kbn/monitoring-plugin", - "x-pack/platform/plugins/private/monitoring" - ], - [ - "@kbn/navigation-plugin", - "src/plugins/navigation" - ], - [ - "@kbn/newsfeed-plugin", - "src/plugins/newsfeed" - ], - [ - "@kbn/newsfeed-test-plugin", - "test/common/plugins/newsfeed" - ], - [ - "@kbn/no-data-page-plugin", - "src/plugins/no_data_page" - ], - [ - "@kbn/notifications-plugin", - "x-pack/plugins/notifications" - ], - [ - "@kbn/object-versioning", - "packages/kbn-object-versioning" - ], - [ - "@kbn/object-versioning-utils", - "packages/kbn-object-versioning-utils" - ], - [ - "@kbn/observability-ai-assistant-app-plugin", - "x-pack/solutions/observability/plugins/observability_ai_assistant_app" - ], - [ - "@kbn/observability-ai-assistant-management-plugin", - "x-pack/solutions/observability/plugins/observability_ai_assistant_management" - ], - [ - "@kbn/observability-ai-assistant-plugin", - "x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant" - ], - [ - "@kbn/observability-ai-common", - "x-pack/solutions/observability/packages/observability_ai/observability_ai_common" - ], - [ - "@kbn/observability-ai-server", - "x-pack/solutions/observability/packages/observability_ai/observability_ai_server" - ], - [ - "@kbn/observability-alert-details", - "x-pack/solutions/observability/packages/alert_details" - ], - [ - "@kbn/observability-alerting-rule-utils", - "x-pack/platform/packages/shared/observability/alerting_rule_utils" - ], - [ - "@kbn/observability-alerting-test-data", - "x-pack/solutions/observability/packages/alerting_test_data" - ], - [ - "@kbn/observability-fixtures-plugin", - "x-pack/test/cases_api_integration/common/plugins/observability" - ], - [ - "@kbn/observability-get-padded-alert-time-range-util", - "x-pack/solutions/observability/packages/get_padded_alert_time_range_util" - ], - [ - "@kbn/observability-logs-explorer-plugin", - "x-pack/solutions/observability/plugins/observability_logs_explorer" - ], - [ - "@kbn/observability-logs-overview", - "x-pack/platform/packages/shared/observability/logs_overview" - ], - [ - "@kbn/observability-onboarding-e2e", - "x-pack/solutions/observability/plugins/observability_onboarding/e2e" - ], - [ - "@kbn/observability-onboarding-plugin", - "x-pack/solutions/observability/plugins/observability_onboarding" - ], - [ - "@kbn/observability-plugin", - "x-pack/solutions/observability/plugins/observability" - ], - [ - "@kbn/observability-shared-plugin", - "x-pack/solutions/observability/plugins/observability_shared" - ], - [ - "@kbn/observability-synthetics-test-data", - "x-pack/solutions/observability/packages/synthetics_test_data" - ], - [ - "@kbn/observability-utils-browser", - "x-pack/solutions/observability/packages/utils_browser" - ], - [ - "@kbn/observability-utils-common", - "x-pack/solutions/observability/packages/utils_common" - ], - [ - "@kbn/observability-utils-server", - "x-pack/solutions/observability/packages/utils_server" - ], - [ - "@kbn/oidc-provider-plugin", - "x-pack/test/security_api_integration/plugins/oidc_provider" - ], - [ - "@kbn/open-telemetry-instrumented-plugin", - "test/common/plugins/otel_metrics" - ], - [ - "@kbn/openapi-bundler", - "packages/kbn-openapi-bundler" - ], - [ - "@kbn/openapi-common", - "src/platform/packages/shared/kbn-openapi-common" - ], - [ - "@kbn/openapi-generator", - "packages/kbn-openapi-generator" - ], - [ - "@kbn/optimizer", - "packages/kbn-optimizer" - ], - [ - "@kbn/optimizer-webpack-helpers", - "packages/kbn-optimizer-webpack-helpers" - ], - [ - "@kbn/osquery-io-ts-types", - "src/platform/packages/shared/kbn-osquery-io-ts-types" - ], - [ - "@kbn/osquery-plugin", - "x-pack/platform/plugins/shared/osquery" - ], - [ - "@kbn/paertial-results-example-plugin", - "examples/partial_results_example" - ], - [ - "@kbn/painless-lab-plugin", - "x-pack/platform/plugins/private/painless_lab" - ], - [ - "@kbn/palettes", - "packages/kbn-palettes" - ], - [ - "@kbn/panel-loader", - "src/platform/packages/private/kbn-panel-loader" - ], - [ - "@kbn/peggy", - "packages/kbn-peggy" - ], - [ - "@kbn/peggy-loader", - "packages/kbn-peggy-loader" - ], - [ - "@kbn/performance-testing-dataset-extractor", - "packages/kbn-performance-testing-dataset-extractor" - ], - [ - "@kbn/picomatcher", - "packages/kbn-picomatcher" - ], - [ - "@kbn/plugin-check", - "packages/kbn-plugin-check" - ], - [ - "@kbn/plugin-generator", - "packages/kbn-plugin-generator" - ], - [ - "@kbn/plugin-helpers", - "packages/kbn-plugin-helpers" - ], - [ - "@kbn/portable-dashboards-example", - "examples/portable_dashboards_example" - ], - [ - "@kbn/preboot-example-plugin", - "examples/preboot_example" - ], - [ - "@kbn/presentation-containers", - "src/platform/packages/shared/presentation/presentation_containers" - ], - [ - "@kbn/presentation-panel-plugin", - "src/platform/plugins/private/presentation_panel" - ], - [ - "@kbn/presentation-publishing", - "src/platform/packages/shared/presentation/presentation_publishing" - ], - [ - "@kbn/presentation-util-plugin", - "src/platform/plugins/shared/presentation_util" - ], - [ - "@kbn/product-doc-artifact-builder", - "x-pack/packages/ai-infra/product-doc-artifact-builder" - ], - [ - "@kbn/product-doc-base-plugin", - "x-pack/platform/plugins/shared/ai_infra/product_doc_base" - ], - [ - "@kbn/product-doc-common", - "x-pack/platform/packages/shared/ai-infra/product-doc-common" - ], - [ - "@kbn/profiling-data-access-plugin", - "x-pack/solutions/observability/plugins/profiling_data_access" - ], - [ - "@kbn/profiling-plugin", - "x-pack/solutions/observability/plugins/profiling" - ], - [ - "@kbn/profiling-utils", - "src/platform/packages/shared/kbn-profiling-utils" - ], - [ - "@kbn/random-sampling", - "x-pack/packages/kbn-random-sampling" - ], - [ - "@kbn/react-field", - "src/platform/packages/shared/kbn-react-field" - ], - [ - "@kbn/react-hooks", - "src/platform/packages/shared/kbn-react-hooks" - ], - [ - "@kbn/react-kibana-context-common", - "packages/react/kibana_context/common" - ], - [ - "@kbn/react-kibana-context-render", - "packages/react/kibana_context/render" - ], - [ - "@kbn/react-kibana-context-root", - "packages/react/kibana_context/root" - ], - [ - "@kbn/react-kibana-context-styled", - "packages/react/kibana_context/styled" - ], - [ - "@kbn/react-kibana-context-theme", - "packages/react/kibana_context/theme" - ], - [ - "@kbn/react-kibana-mount", - "packages/react/kibana_mount" - ], - [ - "@kbn/react-mute-legacy-root-warning", - "packages/kbn-react-mute-legacy-root-warning" - ], - [ - "@kbn/recently-accessed", - "packages/kbn-recently-accessed" - ], - [ - "@kbn/relocate", - "packages/kbn-relocate" - ], - [ - "@kbn/remote-clusters-plugin", - "x-pack/platform/plugins/private/remote_clusters" - ], - [ - "@kbn/rendering-plugin", - "test/plugin_functional/plugins/rendering_plugin" - ], - [ - "@kbn/repo-file-maps", - "packages/kbn-repo-file-maps" - ], - [ - "@kbn/repo-info", - "packages/kbn-repo-info" - ], - [ - "@kbn/repo-linter", - "packages/kbn-repo-linter" - ], - [ - "@kbn/repo-packages", - "packages/kbn-repo-packages" - ], - [ - "@kbn/repo-path", - "packages/kbn-repo-path" - ], - [ - "@kbn/repo-source-classifier", - "packages/kbn-repo-source-classifier" - ], - [ - "@kbn/repo-source-classifier-cli", - "packages/kbn-repo-source-classifier-cli" - ], - [ - "@kbn/reporting-common", - "packages/kbn-reporting/common" - ], - [ - "@kbn/reporting-csv-share-panel", - "packages/kbn-reporting/get_csv_panel_actions" - ], - [ - "@kbn/reporting-export-types-csv", - "packages/kbn-reporting/export_types/csv" - ], - [ - "@kbn/reporting-export-types-csv-common", - "packages/kbn-reporting/export_types/csv_common" - ], - [ - "@kbn/reporting-export-types-pdf", - "packages/kbn-reporting/export_types/pdf" - ], - [ - "@kbn/reporting-export-types-pdf-common", - "packages/kbn-reporting/export_types/pdf_common" - ], - [ - "@kbn/reporting-export-types-png", - "packages/kbn-reporting/export_types/png" - ], - [ - "@kbn/reporting-export-types-png-common", - "packages/kbn-reporting/export_types/png_common" - ], - [ - "@kbn/reporting-mocks-server", - "packages/kbn-reporting/mocks_server" - ], - [ - "@kbn/reporting-plugin", - "x-pack/plugins/reporting" - ], - [ - "@kbn/reporting-public", - "packages/kbn-reporting/public" - ], - [ - "@kbn/reporting-server", - "packages/kbn-reporting/server" - ], - [ - "@kbn/resizable-layout", - "src/platform/packages/shared/kbn-resizable-layout" - ], - [ - "@kbn/resizable-layout-examples-plugin", - "examples/resizable_layout_examples" - ], - [ - "@kbn/resolver-test-plugin", - "x-pack/test/plugin_functional/plugins/resolver_test" - ], - [ - "@kbn/response-ops-feature-flag-service", - "packages/response-ops/feature_flag_service" - ], - [ - "@kbn/response-ops-rule-form", - "packages/response-ops/rule_form" - ], - [ - "@kbn/response-ops-rule-params", - "src/platform/packages/private/response-ops/rule_params" - ], - [ - "@kbn/response-stream-plugin", - "examples/response_stream" - ], - [ - "@kbn/rison", - "packages/kbn-rison" - ], - [ - "@kbn/rollup", - "x-pack/platform/packages/private/rollup" - ], - [ - "@kbn/rollup-plugin", - "x-pack/platform/plugins/private/rollup" - ], - [ - "@kbn/router-to-openapispec", - "packages/kbn-router-to-openapispec" - ], - [ - "@kbn/router-utils", - "src/platform/packages/shared/kbn-router-utils" - ], - [ - "@kbn/routing-example-plugin", - "examples/routing_example" - ], - [ - "@kbn/rrule", - "src/platform/packages/shared/kbn-rrule" - ], - [ - "@kbn/rule-data-utils", - "src/platform/packages/shared/kbn-rule-data-utils" - ], - [ - "@kbn/rule-registry-plugin", - "x-pack/platform/plugins/shared/rule_registry" - ], - [ - "@kbn/runtime-fields-plugin", - "x-pack/platform/plugins/private/runtime_fields" - ], - [ - "@kbn/safer-lodash-set", - "packages/kbn-safer-lodash-set" - ], - [ - "@kbn/saml-provider-plugin", - "x-pack/test/security_api_integration/plugins/saml_provider" - ], - [ - "@kbn/sample-task-plugin", - "x-pack/test/plugin_api_integration/plugins/sample_task_plugin" - ], - [ - "@kbn/sample-task-plugin-update-by-query", - "x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget" - ], - [ - "@kbn/saved-object-export-transforms-plugin", - "test/plugin_functional/plugins/saved_object_export_transforms" - ], - [ - "@kbn/saved-object-import-warnings-plugin", - "test/plugin_functional/plugins/saved_object_import_warnings" - ], - [ - "@kbn/saved-object-test-plugin", - "x-pack/test/saved_object_api_integration/common/plugins/saved_object_test_plugin" - ], - [ - "@kbn/saved-objects-finder-plugin", - "src/platform/plugins/shared/saved_objects_finder" - ], - [ - "@kbn/saved-objects-hidden-from-http-apis-type-plugin", - "test/plugin_functional/plugins/saved_objects_hidden_from_http_apis_type" - ], - [ - "@kbn/saved-objects-hidden-type-plugin", - "test/plugin_functional/plugins/saved_objects_hidden_type" - ], - [ - "@kbn/saved-objects-management-plugin", - "src/plugins/saved_objects_management" - ], - [ - "@kbn/saved-objects-plugin", - "src/plugins/saved_objects" - ], - [ - "@kbn/saved-objects-settings", - "packages/kbn-saved-objects-settings" - ], - [ - "@kbn/saved-objects-tagging-oss-plugin", - "src/plugins/saved_objects_tagging_oss" - ], - [ - "@kbn/saved-objects-tagging-plugin", - "x-pack/plugins/saved_objects_tagging" - ], - [ - "@kbn/saved-search-component", - "packages/kbn-saved-search-component" - ], - [ - "@kbn/saved-search-plugin", - "src/platform/plugins/shared/saved_search" - ], - [ - "@kbn/scout", - "packages/kbn-scout" - ], - [ - "@kbn/scout-info", - "packages/kbn-scout-info" - ], - [ - "@kbn/scout-reporting", - "packages/kbn-scout-reporting" - ], - [ - "@kbn/screenshot-mode-example-plugin", - "examples/screenshot_mode_example" - ], - [ - "@kbn/screenshot-mode-plugin", - "src/plugins/screenshot_mode" - ], - [ - "@kbn/screenshotting-example-plugin", - "x-pack/examples/screenshotting_example" - ], - [ - "@kbn/screenshotting-plugin", - "x-pack/platform/plugins/shared/screenshotting" - ], - [ - "@kbn/screenshotting-server", - "packages/kbn-screenshotting-server" - ], - [ - "@kbn/search-api-keys-components", - "x-pack/solutions/search/packages/kbn-search-api-keys-components" - ], - [ - "@kbn/search-api-keys-server", - "x-pack/solutions/search/packages/kbn-search-api-keys-server" - ], - [ - "@kbn/search-api-panels", - "src/platform/packages/shared/kbn-search-api-panels" - ], - [ - "@kbn/search-assistant", - "x-pack/solutions/search/plugins/search_assistant" - ], - [ - "@kbn/search-connectors", - "src/platform/packages/shared/kbn-search-connectors" - ], - [ - "@kbn/search-connectors-plugin", - "x-pack/solutions/search/plugins/search_connectors" - ], - [ - "@kbn/search-errors", - "src/platform/packages/shared/kbn-search-errors" - ], - [ - "@kbn/search-examples-plugin", - "examples/search_examples" - ], - [ - "@kbn/search-homepage", - "x-pack/solutions/search/plugins/search_homepage" - ], - [ - "@kbn/search-index-documents", - "x-pack/solutions/search/packages/kbn-search-index-documents" - ], - [ - "@kbn/search-indices", - "x-pack/solutions/search/plugins/search_indices" - ], - [ - "@kbn/search-inference-endpoints", - "x-pack/solutions/search/plugins/search_inference_endpoints" - ], - [ - "@kbn/search-navigation", - "x-pack/solutions/search/plugins/search_solution/search_navigation" - ], - [ - "@kbn/search-notebooks", - "x-pack/solutions/search/plugins/search_notebooks" - ], - [ - "@kbn/search-playground", - "x-pack/solutions/search/plugins/search_playground" - ], - [ - "@kbn/search-response-warnings", - "src/platform/packages/shared/kbn-search-response-warnings" - ], - [ - "@kbn/search-shared-ui", - "x-pack/solutions/search/packages/search/shared_ui" - ], - [ - "@kbn/search-types", - "src/platform/packages/shared/kbn-search-types" - ], - [ - "@kbn/searchprofiler-plugin", - "x-pack/platform/plugins/shared/searchprofiler" - ], - [ - "@kbn/security-api-integration-helpers", - "x-pack/test/security_api_integration/packages/helpers" - ], - [ - "@kbn/security-api-key-management", - "x-pack/packages/security/api_key_management" - ], - [ - "@kbn/security-authorization-core", - "x-pack/packages/security/authorization_core" - ], - [ - "@kbn/security-authorization-core-common", - "x-pack/packages/security/authorization_core_common" - ], - [ - "@kbn/security-form-components", - "x-pack/packages/security/form_components" - ], - [ - "@kbn/security-hardening", - "packages/kbn-security-hardening" - ], - [ - "@kbn/security-plugin", - "x-pack/plugins/security" - ], - [ - "@kbn/security-plugin-types-common", - "x-pack/packages/security/plugin_types_common" - ], - [ - "@kbn/security-plugin-types-public", - "x-pack/packages/security/plugin_types_public" - ], - [ - "@kbn/security-plugin-types-server", - "x-pack/packages/security/plugin_types_server" - ], - [ - "@kbn/security-role-management-model", - "x-pack/packages/security/role_management_model" - ], - [ - "@kbn/security-solution-distribution-bar", - "x-pack/solutions/security/packages/distribution_bar" - ], - [ - "@kbn/security-solution-ess", - "x-pack/solutions/security/plugins/security_solution_ess" - ], - [ - "@kbn/security-solution-features", - "x-pack/solutions/security/packages/features" - ], - [ - "@kbn/security-solution-fixtures-plugin", - "x-pack/test/cases_api_integration/common/plugins/security_solution" - ], - [ - "@kbn/security-solution-navigation", - "x-pack/solutions/security/packages/navigation" - ], - [ - "@kbn/security-solution-plugin", - "x-pack/solutions/security/plugins/security_solution" - ], - [ - "@kbn/security-solution-serverless", - "x-pack/solutions/security/plugins/security_solution_serverless" - ], - [ - "@kbn/security-solution-side-nav", - "x-pack/solutions/security/packages/side_nav" - ], - [ - "@kbn/security-solution-storybook-config", - "x-pack/solutions/security/packages/storybook/config" - ], - [ - "@kbn/security-solution-upselling", - "x-pack/solutions/security/packages/upselling" - ], - [ - "@kbn/security-test-endpoints-plugin", - "x-pack/test/security_functional/plugins/test_endpoints" - ], - [ - "@kbn/security-ui-components", - "x-pack/packages/security/ui_components" - ], - [ - "@kbn/securitysolution-autocomplete", - "x-pack/solutions/security/packages/kbn-securitysolution-autocomplete" - ], - [ - "@kbn/securitysolution-data-table", - "x-pack/solutions/security/packages/data_table" - ], - [ - "@kbn/securitysolution-ecs", - "src/platform/packages/shared/kbn-securitysolution-ecs" - ], - [ - "@kbn/securitysolution-endpoint-exceptions-common", - "x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common" - ], - [ - "@kbn/securitysolution-es-utils", - "src/platform/packages/shared/kbn-securitysolution-es-utils" - ], - [ - "@kbn/securitysolution-exception-list-components", - "x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components" - ], - [ - "@kbn/securitysolution-exceptions-common", - "x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common" - ], - [ - "@kbn/securitysolution-hook-utils", - "x-pack/solutions/security/packages/kbn-securitysolution-hook-utils" - ], - [ - "@kbn/securitysolution-io-ts-alerting-types", - "x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types" - ], - [ - "@kbn/securitysolution-io-ts-list-types", - "x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types" - ], - [ - "@kbn/securitysolution-io-ts-types", - "src/platform/packages/shared/kbn-securitysolution-io-ts-types" - ], - [ - "@kbn/securitysolution-io-ts-utils", - "src/platform/packages/shared/kbn-securitysolution-io-ts-utils" - ], - [ - "@kbn/securitysolution-list-api", - "x-pack/solutions/security/packages/kbn-securitysolution-list-api" - ], - [ - "@kbn/securitysolution-list-constants", - "x-pack/solutions/security/packages/kbn-securitysolution-list-constants" - ], - [ - "@kbn/securitysolution-list-hooks", - "x-pack/solutions/security/packages/kbn-securitysolution-list-hooks" - ], - [ - "@kbn/securitysolution-list-utils", - "x-pack/solutions/security/packages/kbn-securitysolution-list-utils" - ], - [ - "@kbn/securitysolution-lists-common", - "x-pack/solutions/security/packages/kbn-securitysolution-lists-common" - ], - [ - "@kbn/securitysolution-rules", - "src/platform/packages/shared/kbn-securitysolution-rules" - ], - [ - "@kbn/securitysolution-t-grid", - "x-pack/solutions/security/packages/kbn-securitysolution-t-grid" - ], - [ - "@kbn/securitysolution-utils", - "x-pack/solutions/security/packages/kbn-securitysolution-utils" - ], - [ - "@kbn/server-http-tools", - "packages/kbn-server-http-tools" - ], - [ - "@kbn/server-route-repository", - "src/platform/packages/shared/kbn-server-route-repository" - ], - [ - "@kbn/server-route-repository-client", - "src/platform/packages/shared/kbn-server-route-repository-client" - ], - [ - "@kbn/server-route-repository-utils", - "src/platform/packages/shared/kbn-server-route-repository-utils" - ], - [ - "@kbn/serverless", - "x-pack/plugins/serverless" - ], - [ - "@kbn/serverless-common-settings", - "packages/serverless/settings/common" - ], - [ - "@kbn/serverless-observability", - "x-pack/solutions/observability/plugins/serverless_observability" - ], - [ - "@kbn/serverless-observability-settings", - "packages/serverless/settings/observability_project" - ], - [ - "@kbn/serverless-project-switcher", - "packages/serverless/project_switcher" - ], - [ - "@kbn/serverless-search", - "x-pack/solutions/search/plugins/serverless_search" - ], - [ - "@kbn/serverless-search-settings", - "src/platform/packages/shared/serverless/settings/search_project" - ], - [ - "@kbn/serverless-security-settings", - "src/platform/packages/shared/serverless/settings/security_project" - ], - [ - "@kbn/serverless-storybook-config", - "packages/serverless/storybook/config" - ], - [ - "@kbn/serverless-types", - "packages/serverless/types" - ], - [ - "@kbn/session-notifications-plugin", - "test/plugin_functional/plugins/session_notifications" - ], - [ - "@kbn/session-view-plugin", - "x-pack/solutions/security/plugins/session_view" - ], - [ - "@kbn/set-map", - "packages/kbn-set-map" - ], - [ - "@kbn/share-examples-plugin", - "examples/share_examples" - ], - [ - "@kbn/share-plugin", - "src/plugins/share" - ], - [ - "@kbn/shared-svg", - "src/platform/packages/shared/kbn-shared-svg" - ], - [ - "@kbn/shared-ux-avatar-solution", - "packages/shared-ux/avatar/solution" - ], - [ - "@kbn/shared-ux-button-exit-full-screen", - "packages/shared-ux/button/exit_full_screen" - ], - [ - "@kbn/shared-ux-button-toolbar", - "packages/shared-ux/button_toolbar" - ], - [ - "@kbn/shared-ux-card-no-data", - "packages/shared-ux/card/no_data/impl" - ], - [ - "@kbn/shared-ux-card-no-data-mocks", - "packages/shared-ux/card/no_data/mocks" - ], - [ - "@kbn/shared-ux-card-no-data-types", - "packages/shared-ux/card/no_data/types" - ], - [ - "@kbn/shared-ux-chrome-navigation", - "packages/shared-ux/chrome/navigation" - ], - [ - "@kbn/shared-ux-error-boundary", - "packages/shared-ux/error_boundary" - ], - [ - "@kbn/shared-ux-file-context", - "packages/shared-ux/file/context" - ], - [ - "@kbn/shared-ux-file-image", - "packages/shared-ux/file/image/impl" - ], - [ - "@kbn/shared-ux-file-image-mocks", - "packages/shared-ux/file/image/mocks" - ], - [ - "@kbn/shared-ux-file-mocks", - "packages/shared-ux/file/mocks" - ], - [ - "@kbn/shared-ux-file-picker", - "packages/shared-ux/file/file_picker/impl" - ], - [ - "@kbn/shared-ux-file-types", - "packages/shared-ux/file/types" - ], - [ - "@kbn/shared-ux-file-upload", - "packages/shared-ux/file/file_upload/impl" - ], - [ - "@kbn/shared-ux-file-util", - "packages/shared-ux/file/util" - ], - [ - "@kbn/shared-ux-link-redirect-app", - "packages/shared-ux/link/redirect_app/impl" - ], - [ - "@kbn/shared-ux-link-redirect-app-mocks", - "packages/shared-ux/link/redirect_app/mocks" - ], - [ - "@kbn/shared-ux-link-redirect-app-types", - "packages/shared-ux/link/redirect_app/types" - ], - [ - "@kbn/shared-ux-markdown", - "packages/shared-ux/markdown/impl" - ], - [ - "@kbn/shared-ux-markdown-mocks", - "packages/shared-ux/markdown/mocks" - ], - [ - "@kbn/shared-ux-markdown-types", - "packages/shared-ux/markdown/types" - ], - [ - "@kbn/shared-ux-page-analytics-no-data", - "packages/shared-ux/page/analytics_no_data/impl" - ], - [ - "@kbn/shared-ux-page-analytics-no-data-mocks", - "packages/shared-ux/page/analytics_no_data/mocks" - ], - [ - "@kbn/shared-ux-page-analytics-no-data-types", - "packages/shared-ux/page/analytics_no_data/types" - ], - [ - "@kbn/shared-ux-page-kibana-no-data", - "packages/shared-ux/page/kibana_no_data/impl" - ], - [ - "@kbn/shared-ux-page-kibana-no-data-mocks", - "packages/shared-ux/page/kibana_no_data/mocks" - ], - [ - "@kbn/shared-ux-page-kibana-no-data-types", - "packages/shared-ux/page/kibana_no_data/types" - ], - [ - "@kbn/shared-ux-page-kibana-template", - "packages/shared-ux/page/kibana_template/impl" - ], - [ - "@kbn/shared-ux-page-kibana-template-mocks", - "packages/shared-ux/page/kibana_template/mocks" - ], - [ - "@kbn/shared-ux-page-kibana-template-types", - "packages/shared-ux/page/kibana_template/types" - ], - [ - "@kbn/shared-ux-page-no-data", - "packages/shared-ux/page/no_data/impl" - ], - [ - "@kbn/shared-ux-page-no-data-config", - "packages/shared-ux/page/no_data_config/impl" - ], - [ - "@kbn/shared-ux-page-no-data-config-mocks", - "packages/shared-ux/page/no_data_config/mocks" - ], - [ - "@kbn/shared-ux-page-no-data-config-types", - "packages/shared-ux/page/no_data_config/types" - ], - [ - "@kbn/shared-ux-page-no-data-mocks", - "packages/shared-ux/page/no_data/mocks" - ], - [ - "@kbn/shared-ux-page-no-data-types", - "packages/shared-ux/page/no_data/types" - ], - [ - "@kbn/shared-ux-page-solution-nav", - "packages/shared-ux/page/solution_nav" - ], - [ - "@kbn/shared-ux-prompt-no-data-views", - "packages/shared-ux/prompt/no_data_views/impl" - ], - [ - "@kbn/shared-ux-prompt-no-data-views-mocks", - "packages/shared-ux/prompt/no_data_views/mocks" - ], - [ - "@kbn/shared-ux-prompt-no-data-views-types", - "packages/shared-ux/prompt/no_data_views/types" - ], - [ - "@kbn/shared-ux-prompt-not-found", - "packages/shared-ux/prompt/not_found" - ], - [ - "@kbn/shared-ux-router", - "packages/shared-ux/router/impl" - ], - [ - "@kbn/shared-ux-router-mocks", - "packages/shared-ux/router/mocks" - ], - [ - "@kbn/shared-ux-router-types", - "packages/shared-ux/router/types" - ], - [ - "@kbn/shared-ux-storybook-config", - "packages/shared-ux/storybook/config" - ], - [ - "@kbn/shared-ux-storybook-mock", - "packages/shared-ux/storybook/mock" - ], - [ - "@kbn/shared-ux-tabbed-modal", - "packages/shared-ux/modal/tabbed" - ], - [ - "@kbn/shared-ux-table-persist", - "packages/shared-ux/table_persist" - ], - [ - "@kbn/shared-ux-utility", - "packages/kbn-shared-ux-utility" - ], - [ - "@kbn/slo-plugin", - "x-pack/solutions/observability/plugins/slo" - ], - [ - "@kbn/slo-schema", - "x-pack/platform/packages/shared/kbn-slo-schema" - ], - [ - "@kbn/snapshot-restore-plugin", - "x-pack/platform/plugins/private/snapshot_restore" - ], - [ - "@kbn/some-dev-log", - "packages/kbn-some-dev-log" - ], - [ - "@kbn/sort-package-json", - "packages/kbn-sort-package-json" - ], - [ - "@kbn/sort-predicates", - "packages/kbn-sort-predicates" - ], - [ - "@kbn/spaces-plugin", - "x-pack/plugins/spaces" - ], - [ - "@kbn/spaces-test-plugin", - "x-pack/test/spaces_api_integration/common/plugins/spaces_test_plugin" - ], - [ - "@kbn/sse-utils", - "src/platform/packages/shared/kbn-sse-utils" - ], - [ - "@kbn/sse-utils-client", - "src/platform/packages/shared/kbn-sse-utils-client" - ], - [ - "@kbn/sse-utils-server", - "src/platform/packages/shared/kbn-sse-utils-server" - ], - [ - "@kbn/stack-alerts-plugin", - "x-pack/platform/plugins/shared/stack_alerts" - ], - [ - "@kbn/stack-connectors-plugin", - "x-pack/platform/plugins/shared/stack_connectors" - ], - [ - "@kbn/stack-management-usage-test-plugin", - "x-pack/test/usage_collection/plugins/stack_management_usage_test" - ], - [ - "@kbn/state-containers-examples-plugin", - "examples/state_containers_examples" - ], - [ - "@kbn/status-plugin-a-plugin", - "test/server_integration/plugins/status_plugin_a" - ], - [ - "@kbn/status-plugin-b-plugin", - "test/server_integration/plugins/status_plugin_b" - ], - [ - "@kbn/std", - "packages/kbn-std" - ], - [ - "@kbn/stdio-dev-helpers", - "packages/kbn-stdio-dev-helpers" - ], - [ - "@kbn/storybook", - "packages/kbn-storybook" - ], - [ - "@kbn/streams-app-plugin", - "x-pack/solutions/observability/plugins/streams_app" - ], - [ - "@kbn/streams-plugin", - "x-pack/solutions/observability/plugins/streams" - ], - [ - "@kbn/streams-schema", - "x-pack/packages/kbn-streams-schema" - ], - [ - "@kbn/synthetics-e2e", - "x-pack/solutions/observability/plugins/synthetics/e2e" - ], - [ - "@kbn/synthetics-plugin", - "x-pack/solutions/observability/plugins/synthetics" - ], - [ - "@kbn/synthetics-private-location", - "x-pack/packages/kbn-synthetics-private-location" - ], - [ - "@kbn/task-manager-fixture-plugin", - "x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture" - ], - [ - "@kbn/task-manager-performance-plugin", - "x-pack/test/plugin_api_perf/plugins/task_manager_performance" - ], - [ - "@kbn/task-manager-plugin", - "x-pack/platform/plugins/shared/task_manager" - ], - [ - "@kbn/telemetry-collection-manager-plugin", - "src/plugins/telemetry_collection_manager" - ], - [ - "@kbn/telemetry-collection-xpack-plugin", - "x-pack/plugins/telemetry_collection_xpack" - ], - [ - "@kbn/telemetry-management-section-plugin", - "src/plugins/telemetry_management_section" - ], - [ - "@kbn/telemetry-plugin", - "src/plugins/telemetry" - ], - [ - "@kbn/telemetry-test-plugin", - "test/plugin_functional/plugins/telemetry" - ], - [ - "@kbn/telemetry-tools", - "packages/kbn-telemetry-tools" - ], - [ - "@kbn/test", - "packages/kbn-test" - ], - [ - "@kbn/test-eui-helpers", - "packages/kbn-test-eui-helpers" - ], - [ - "@kbn/test-feature-usage-plugin", - "x-pack/test/licensing_plugin/plugins/test_feature_usage" - ], - [ - "@kbn/test-jest-helpers", - "packages/kbn-test-jest-helpers" - ], - [ - "@kbn/test-subj-selector", - "packages/kbn-test-subj-selector" - ], - [ - "@kbn/test-suites-serverless", - "x-pack/test_serverless" - ], - [ - "@kbn/test-suites-src", - "test" - ], - [ - "@kbn/test-suites-xpack", - "x-pack/test" - ], - [ - "@kbn/test-suites-xpack-performance", - "x-pack/performance" - ], - [ - "@kbn/testing-embedded-lens-plugin", - "x-pack/examples/testing_embedded_lens" - ], - [ - "@kbn/third-party-lens-navigation-prompt-plugin", - "x-pack/examples/third_party_lens_navigation_prompt" - ], - [ - "@kbn/third-party-vis-lens-example-plugin", - "x-pack/examples/third_party_vis_lens_example" - ], - [ - "@kbn/threat-intelligence-plugin", - "x-pack/solutions/security/plugins/threat_intelligence" - ], - [ - "@kbn/timelines-plugin", - "x-pack/solutions/security/plugins/timelines" - ], - [ - "@kbn/timelion-grammar", - "packages/kbn-timelion-grammar" - ], - [ - "@kbn/timerange", - "src/platform/packages/shared/kbn-timerange" - ], - [ - "@kbn/tinymath", - "packages/kbn-tinymath" - ], - [ - "@kbn/tooling-log", - "packages/kbn-tooling-log" - ], - [ - "@kbn/transform-plugin", - "x-pack/platform/plugins/private/transform" - ], - [ - "@kbn/translations-plugin", - "x-pack/platform/plugins/private/translations" - ], - [ - "@kbn/transpose-utils", - "packages/kbn-transpose-utils" - ], - [ - "@kbn/triggers-actions-ui-example-plugin", - "x-pack/examples/triggers_actions_ui_example" - ], - [ - "@kbn/triggers-actions-ui-plugin", - "x-pack/platform/plugins/shared/triggers_actions_ui" - ], - [ - "@kbn/triggers-actions-ui-types", - "src/platform/packages/shared/kbn-triggers-actions-ui-types" - ], - [ - "@kbn/try-in-console", - "src/platform/packages/shared/kbn-try-in-console" - ], - [ - "@kbn/ts-projects", - "packages/kbn-ts-projects" - ], - [ - "@kbn/ts-type-check-cli", - "packages/kbn-ts-type-check-cli" - ], - [ - "@kbn/typed-react-router-config", - "src/platform/packages/shared/kbn-typed-react-router-config" - ], - [ - "@kbn/ui-actions-browser", - "packages/kbn-ui-actions-browser" - ], - [ - "@kbn/ui-actions-enhanced-examples-plugin", - "x-pack/examples/ui_actions_enhanced_examples" - ], - [ - "@kbn/ui-actions-enhanced-plugin", - "src/plugins/ui_actions_enhanced" - ], - [ - "@kbn/ui-actions-examples-plugin", - "examples/ui_action_examples" - ], - [ - "@kbn/ui-actions-explorer-plugin", - "examples/ui_actions_explorer" - ], - [ - "@kbn/ui-actions-plugin", - "src/plugins/ui_actions" - ], - [ - "@kbn/ui-settings-plugin", - "test/plugin_functional/plugins/ui_settings_plugin" - ], - [ - "@kbn/ui-shared-deps-npm", - "packages/kbn-ui-shared-deps-npm" - ], - [ - "@kbn/ui-shared-deps-src", - "packages/kbn-ui-shared-deps-src" - ], - [ - "@kbn/ui-theme", - "packages/kbn-ui-theme" - ], - [ - "@kbn/unified-data-table", - "src/platform/packages/shared/kbn-unified-data-table" - ], - [ - "@kbn/unified-doc-viewer", - "src/platform/packages/shared/kbn-unified-doc-viewer" - ], - [ - "@kbn/unified-doc-viewer-examples", - "examples/unified_doc_viewer" - ], - [ - "@kbn/unified-doc-viewer-plugin", - "src/platform/plugins/shared/unified_doc_viewer" - ], - [ - "@kbn/unified-field-list", - "src/platform/packages/shared/kbn-unified-field-list" - ], - [ - "@kbn/unified-field-list-examples-plugin", - "examples/unified_field_list_examples" - ], - [ - "@kbn/unified-histogram-plugin", - "src/platform/plugins/shared/unified_histogram" - ], - [ - "@kbn/unified-search-plugin", - "src/plugins/unified_search" - ], - [ - "@kbn/unsaved-changes-badge", - "src/platform/packages/private/kbn-unsaved-changes-badge" - ], - [ - "@kbn/unsaved-changes-prompt", - "src/platform/packages/shared/kbn-unsaved-changes-prompt" - ], - [ - "@kbn/upgrade-assistant-plugin", - "x-pack/plugins/upgrade_assistant" - ], - [ - "@kbn/uptime-plugin", - "x-pack/solutions/observability/plugins/uptime" - ], - [ - "@kbn/url-drilldown-plugin", - "x-pack/plugins/drilldowns/url_drilldown" - ], - [ - "@kbn/url-forwarding-plugin", - "src/plugins/url_forwarding" - ], - [ - "@kbn/usage-collection-plugin", - "src/plugins/usage_collection" - ], - [ - "@kbn/usage-collection-test-plugin", - "test/plugin_functional/plugins/usage_collection" - ], - [ - "@kbn/use-tracked-promise", - "packages/kbn-use-tracked-promise" - ], - [ - "@kbn/user-profile-components", - "packages/kbn-user-profile-components" - ], - [ - "@kbn/user-profile-examples-plugin", - "examples/user_profile_examples" - ], - [ - "@kbn/user-profiles-consumer-plugin", - "x-pack/test/security_api_integration/plugins/user_profiles_consumer" - ], - [ - "@kbn/utility-types", - "packages/kbn-utility-types" - ], - [ - "@kbn/utility-types-jest", - "packages/kbn-utility-types-jest" - ], - [ - "@kbn/utils", - "packages/kbn-utils" - ], - [ - "@kbn/ux-plugin", - "x-pack/solutions/observability/plugins/ux" - ], - [ - "@kbn/v8-profiler-examples-plugin", - "examples/v8_profiler_examples" - ], - [ - "@kbn/validate-next-docs-cli", - "packages/kbn-validate-next-docs-cli" - ], - [ - "@kbn/vis-default-editor-plugin", - "src/plugins/vis_default_editor" - ], - [ - "@kbn/vis-type-gauge-plugin", - "src/plugins/vis_types/gauge" - ], - [ - "@kbn/vis-type-heatmap-plugin", - "src/plugins/vis_types/heatmap" - ], - [ - "@kbn/vis-type-markdown-plugin", - "src/platform/plugins/private/vis_type_markdown" - ], - [ - "@kbn/vis-type-metric-plugin", - "src/plugins/vis_types/metric" - ], - [ - "@kbn/vis-type-pie-plugin", - "src/plugins/vis_types/pie" - ], - [ - "@kbn/vis-type-table-plugin", - "src/plugins/vis_types/table" - ], - [ - "@kbn/vis-type-tagcloud-plugin", - "src/plugins/vis_types/tagcloud" - ], - [ - "@kbn/vis-type-timelion-plugin", - "src/plugins/vis_types/timelion" - ], - [ - "@kbn/vis-type-timeseries-plugin", - "src/plugins/vis_types/timeseries" - ], - [ - "@kbn/vis-type-vega-plugin", - "src/plugins/vis_types/vega" - ], - [ - "@kbn/vis-type-vislib-plugin", - "src/plugins/vis_types/vislib" - ], - [ - "@kbn/vis-type-xy-plugin", - "src/plugins/vis_types/xy" - ], - [ - "@kbn/visualization-ui-components", - "packages/kbn-visualization-ui-components" - ], - [ - "@kbn/visualization-utils", - "packages/kbn-visualization-utils" - ], - [ - "@kbn/visualizations-plugin", - "src/plugins/visualizations" - ], - [ - "@kbn/watcher-plugin", - "x-pack/platform/plugins/private/watcher" - ], - [ - "@kbn/web-worker-stub", - "packages/kbn-web-worker-stub" - ], - [ - "@kbn/whereis-pkg-cli", - "packages/kbn-whereis-pkg-cli" - ], - [ - "@kbn/xstate-utils", - "src/platform/packages/shared/kbn-xstate-utils" - ], - [ - "@kbn/yarn-lock-validator", - "packages/kbn-yarn-lock-validator" - ], - [ - "@kbn/zod", - "packages/kbn-zod" - ], - [ - "@kbn/zod-helpers", - "src/platform/packages/shared/kbn-zod-helpers" - ] -] \ No newline at end of file diff --git a/packages/response-ops/alerts_apis/README.md b/packages/response-ops/alerts_apis/README.md new file mode 100644 index 0000000000000..74d102a668533 --- /dev/null +++ b/packages/response-ops/alerts_apis/README.md @@ -0,0 +1,3 @@ +# @kbn/response-ops-alerts-apis + +Client-side Alerts HTTP API fetchers and React Query wrappers. diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_muted_alerts.test.ts b/packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.test.ts similarity index 78% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_muted_alerts.test.ts rename to packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.test.ts index 976875adc6b3d..46f3c061b2d1e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_muted_alerts.test.ts +++ b/packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.test.ts @@ -1,15 +1,18 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { httpServiceMock } from '@kbn/core/public/mocks'; -import { getRulesWithMutedAlerts } from './get_rules_with_muted_alerts'; +import { getMutedAlertsInstancesByRule } from './get_muted_alerts_instances_by_rule'; const http = httpServiceMock.createStartContract(); -describe('getRulesWithMutedAlerts', () => { +describe('getMutedAlertsInstancesByRule', () => { const apiRes = { page: 1, per_page: 10, @@ -24,7 +27,7 @@ describe('getRulesWithMutedAlerts', () => { }); test('should call find API with correct params', async () => { - const result = await getRulesWithMutedAlerts({ http, ruleIds: ['foo'] }); + const result = await getMutedAlertsInstancesByRule({ http, ruleIds: ['foo'] }); expect(result).toEqual({ page: 1, @@ -45,7 +48,7 @@ describe('getRulesWithMutedAlerts', () => { }); test('should call find API with multiple ruleIds', async () => { - const result = await getRulesWithMutedAlerts({ http, ruleIds: ['foo', 'bar'] }); + const result = await getMutedAlertsInstancesByRule({ http, ruleIds: ['foo', 'bar'] }); expect(result).toEqual({ page: 1, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_with_muted_alerts.ts b/packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.ts similarity index 60% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_with_muted_alerts.ts rename to packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.ts index 5162869c1a575..ed05514d12401 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_with_muted_alerts.ts +++ b/packages/response-ops/alerts_apis/apis/get_muted_alerts_instances_by_rule.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { HttpStart } from '@kbn/core-http-browser'; @@ -19,17 +21,17 @@ export interface FindRulesResponse { data: Rule[]; } -export interface GetRulesWithMutedAlertsParams { +export interface GetMutedAlertsInstancesByRuleParams { ruleIds: string[]; http: HttpStart; signal?: AbortSignal; } -export const getRulesWithMutedAlerts = async ({ +export const getMutedAlertsInstancesByRule = async ({ http, ruleIds, signal, -}: GetRulesWithMutedAlertsParams) => { +}: GetMutedAlertsInstancesByRuleParams) => { const filterNode = nodeBuilder.or(ruleIds.map((id) => nodeBuilder.is('alert.id', `alert:${id}`))); return http.post(INTERNAL_FIND_RULES_URL, { body: JSON.stringify({ diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.test.ts b/packages/response-ops/alerts_apis/apis/mute_alert_instance.test.ts similarity index 57% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.test.ts rename to packages/response-ops/alerts_apis/apis/mute_alert_instance.test.ts index dc046903b25b0..f8a63cf11c8bd 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.test.ts +++ b/packages/response-ops/alerts_apis/apis/mute_alert_instance.test.ts @@ -1,12 +1,14 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { httpServiceMock } from '@kbn/core/public/mocks'; -import { muteAlertInstance } from './mute_alert'; +import { muteAlertInstance } from './mute_alert_instance'; const http = httpServiceMock.createStartContract(); diff --git a/packages/response-ops/alerts_apis/apis/mute_alert_instance.ts b/packages/response-ops/alerts_apis/apis/mute_alert_instance.ts new file mode 100644 index 0000000000000..76b26fc59a3d3 --- /dev/null +++ b/packages/response-ops/alerts_apis/apis/mute_alert_instance.ts @@ -0,0 +1,25 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { HttpSetup } from '@kbn/core/public'; +import { BASE_ALERTING_API_PATH } from '../constants'; + +export interface MuteAlertInstanceParams { + id: string; + instanceId: string; + http: HttpSetup; +} + +export const muteAlertInstance = ({ id, instanceId, http }: MuteAlertInstanceParams) => { + return http.post( + `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent( + instanceId + )}/_mute` + ); +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.test.ts b/packages/response-ops/alerts_apis/apis/unmute_alert_instance.test.ts similarity index 57% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.test.ts rename to packages/response-ops/alerts_apis/apis/unmute_alert_instance.test.ts index c7c1b4ab5dab0..dc462f84fd38f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.test.ts +++ b/packages/response-ops/alerts_apis/apis/unmute_alert_instance.test.ts @@ -1,12 +1,14 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { httpServiceMock } from '@kbn/core/public/mocks'; -import { unmuteAlertInstance } from './unmute_alert'; +import { unmuteAlertInstance } from './unmute_alert_instance'; const http = httpServiceMock.createStartContract(); diff --git a/packages/response-ops/alerts_apis/apis/unmute_alert_instance.ts b/packages/response-ops/alerts_apis/apis/unmute_alert_instance.ts new file mode 100644 index 0000000000000..f1260712e2cf6 --- /dev/null +++ b/packages/response-ops/alerts_apis/apis/unmute_alert_instance.ts @@ -0,0 +1,25 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { HttpSetup } from '@kbn/core/public'; +import { BASE_ALERTING_API_PATH } from '../constants'; + +export interface UnmuteAlertInstanceParams { + id: string; + instanceId: string; + http: HttpSetup; +} + +export const unmuteAlertInstance = ({ id, instanceId, http }: UnmuteAlertInstanceParams) => { + return http.post( + `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent( + instanceId + )}/_unmute` + ); +}; diff --git a/packages/response-ops/alerts_apis/constants.ts b/packages/response-ops/alerts_apis/constants.ts new file mode 100644 index 0000000000000..5ba66d66887e3 --- /dev/null +++ b/packages/response-ops/alerts_apis/constants.ts @@ -0,0 +1,22 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export const BASE_ALERTING_API_PATH = '/api/alerting'; + +export const queryKeys = { + root: 'alerts', + mutedAlerts: (ruleIds: string[]) => + [queryKeys.root, 'mutedInstanceIdsForRuleIds', ruleIds] as const, +}; + +export const mutationKeys = { + root: 'alerts', + muteAlertInstance: () => [mutationKeys.root, 'muteAlertInstance'] as const, + unmuteAlertInstance: () => [mutationKeys.root, 'unmuteAlertInstance'] as const, +}; diff --git a/packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.test.tsx b/packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.test.tsx new file mode 100644 index 0000000000000..ac1dc15801994 --- /dev/null +++ b/packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.test.tsx @@ -0,0 +1,64 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { renderHook, waitFor } from '@testing-library/react'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { Wrapper } from '@kbn/alerts-ui-shared/src/common/test_utils/wrapper'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import * as api from '../apis/get_muted_alerts_instances_by_rule'; +import { useGetMutedAlertsQuery } from './use_get_muted_alerts_query'; + +jest.mock('../apis/get_muted_alerts_instances_by_rule'); + +const ruleIds = ['a', 'b']; + +describe('useGetMutedAlertsQuery', () => { + const http = httpServiceMock.createStartContract(); + const notifications = notificationServiceMock.createStartContract(); + const addErrorMock = notifications.toasts.addError; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls the api when invoked with the correct parameters', async () => { + const muteAlertInstanceSpy = jest.spyOn(api, 'getMutedAlertsInstancesByRule'); + + renderHook(() => useGetMutedAlertsQuery({ http, notifications, ruleIds }), { + wrapper: Wrapper, + }); + + await waitFor(() => + expect(muteAlertInstanceSpy).toHaveBeenCalledWith(expect.objectContaining({ ruleIds })) + ); + }); + + it('does not call the api if the enabled option is false', async () => { + const spy = jest.spyOn(api, 'getMutedAlertsInstancesByRule'); + + renderHook(() => useGetMutedAlertsQuery({ http, notifications, ruleIds }, { enabled: false }), { + wrapper: Wrapper, + }); + + await waitFor(() => expect(spy).not.toHaveBeenCalled()); + }); + + it('shows a toast error when the api returns an error', async () => { + const spy = jest + .spyOn(api, 'getMutedAlertsInstancesByRule') + .mockRejectedValue(new Error('An error')); + + renderHook(() => useGetMutedAlertsQuery({ http, notifications, ruleIds }), { + wrapper: Wrapper, + }); + + await waitFor(() => expect(spy).toHaveBeenCalled()); + await waitFor(() => expect(addErrorMock).toHaveBeenCalled()); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.tsx b/packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.tsx similarity index 50% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.tsx rename to packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.tsx index 9bbd6fee97a2d..52623236c68bd 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.tsx +++ b/packages/response-ops/alerts_apis/hooks/use_get_muted_alerts_query.tsx @@ -1,28 +1,31 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; import { QueryOptionsOverrides } from '@kbn/alerts-ui-shared/src/common/types/tanstack_query_utility_types'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { queryKeys } from '../constants'; +import { MutedAlerts, ServerError } from '../types'; import { - getRulesWithMutedAlerts, - GetRulesWithMutedAlertsParams, -} from '../apis/get_rules_with_muted_alerts'; -import { useKibana } from '../../../../../common'; -import { triggersActionsUiQueriesKeys } from '../../../../hooks/constants'; -import { MutedAlerts, ServerError } from '../../types'; + getMutedAlertsInstancesByRule, + GetMutedAlertsInstancesByRuleParams, +} from '../apis/get_muted_alerts_instances_by_rule'; -const ERROR_TITLE = i18n.translate('xpack.triggersActionsUI.mutedAlerts.api.get', { +const ERROR_TITLE = i18n.translate('xpack.responseOpsAlertsApis.mutedAlerts.api.get', { defaultMessage: 'Error fetching muted alerts data', }); -const getMutedAlerts = ({ http, signal, ruleIds }: GetRulesWithMutedAlertsParams) => - getRulesWithMutedAlerts({ http, ruleIds, signal }).then(({ data: rules }) => +const getMutedAlerts = ({ http, signal, ruleIds }: GetMutedAlertsInstancesByRuleParams) => + getMutedAlertsInstancesByRule({ http, ruleIds, signal }).then(({ data: rules }) => rules?.reduce((mutedAlerts, rule) => { mutedAlerts[rule.id] = rule.muted_alert_ids; return mutedAlerts; @@ -31,19 +34,17 @@ const getMutedAlerts = ({ http, signal, ruleIds }: GetRulesWithMutedAlertsParams export interface UseGetMutedAlertsQueryParams { ruleIds: string[]; + http: HttpStart; + notifications: NotificationsStart; } export const useGetMutedAlertsQuery = ( - { ruleIds }: UseGetMutedAlertsQueryParams, + { ruleIds, http, notifications: { toasts } }: UseGetMutedAlertsQueryParams, { enabled }: QueryOptionsOverrides = {} ) => { - const { - http, - notifications: { toasts }, - } = useKibana().services; return useQuery({ context: AlertsQueryContext, - queryKey: triggersActionsUiQueriesKeys.mutedAlerts(ruleIds), + queryKey: queryKeys.mutedAlerts(ruleIds), queryFn: ({ signal }) => getMutedAlerts({ http, signal, ruleIds }), onError: (error: ServerError) => { if (error.name !== 'AbortError') { diff --git a/packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.test.tsx b/packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.test.tsx new file mode 100644 index 0000000000000..65854d714868d --- /dev/null +++ b/packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.test.tsx @@ -0,0 +1,62 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { renderHook, waitFor } from '@testing-library/react'; +import { Wrapper } from '@kbn/alerts-ui-shared/src/common/test_utils/wrapper'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import * as api from '../apis/mute_alert_instance'; +import { useMuteAlertInstance } from './use_mute_alert_instance'; + +jest.mock('../apis/mute_alert_instance'); + +const params = { ruleId: '', alertInstanceId: '' }; + +describe('useMuteAlertInstance', () => { + const http = httpServiceMock.createStartContract(); + const notifications = notificationServiceMock.createStartContract(); + const addErrorMock = notifications.toasts.addError; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls the api when invoked with the correct parameters', async () => { + const muteAlertInstanceSpy = jest.spyOn(api, 'muteAlertInstance'); + + const { result } = renderHook(() => useMuteAlertInstance({ http, notifications }), { + wrapper: Wrapper, + }); + + result.current.mutate(params); + + await waitFor(() => { + expect(muteAlertInstanceSpy).toHaveBeenCalledWith({ + id: params.ruleId, + instanceId: params.alertInstanceId, + http: expect.anything(), + }); + }); + }); + + it('shows a toast error when the api returns an error', async () => { + const spy = jest.spyOn(api, 'muteAlertInstance').mockRejectedValue(new Error('An error')); + + const { result } = renderHook(() => useMuteAlertInstance({ http, notifications }), { + wrapper: Wrapper, + }); + + result.current.mutate(params); + + await waitFor(() => { + expect(spy).toHaveBeenCalled(); + expect(addErrorMock).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.ts b/packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.ts new file mode 100644 index 0000000000000..0421b793bc011 --- /dev/null +++ b/packages/response-ops/alerts_apis/hooks/use_mute_alert_instance.ts @@ -0,0 +1,57 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { useMutation } from '@tanstack/react-query'; +import { i18n } from '@kbn/i18n'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { mutationKeys } from '../constants'; +import type { ServerError, ToggleAlertParams } from '../types'; +import { muteAlertInstance } from '../apis/mute_alert_instance'; + +const ERROR_TITLE = i18n.translate('alertsApis.muteAlert.error', { + defaultMessage: 'Error muting alert', +}); + +export interface UseMuteAlertInstanceParams { + http: HttpStart; + notifications: NotificationsStart; +} + +export const useMuteAlertInstance = ({ + http, + notifications: { toasts }, +}: UseMuteAlertInstanceParams) => { + return useMutation( + ({ ruleId, alertInstanceId }: ToggleAlertParams) => + muteAlertInstance({ http, id: ruleId, instanceId: alertInstanceId }), + { + mutationKey: mutationKeys.muteAlertInstance(), + context: AlertsQueryContext, + onSuccess() { + toasts.addSuccess( + i18n.translate('xpack.responseOpsAlertsApis.alertsTable.alertMuted', { + defaultMessage: 'Alert muted', + }) + ); + }, + onError: (error: ServerError) => { + if (error.name !== 'AbortError') { + toasts.addError( + error.body && error.body.message ? new Error(error.body.message) : error, + { + title: ERROR_TITLE, + } + ); + } + }, + } + ); +}; diff --git a/packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.test.tsx b/packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.test.tsx new file mode 100644 index 0000000000000..3c368b632e604 --- /dev/null +++ b/packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.test.tsx @@ -0,0 +1,62 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { renderHook, waitFor } from '@testing-library/react'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { Wrapper } from '@kbn/alerts-ui-shared/src/common/test_utils/wrapper'; +import { useUnmuteAlertInstance } from './use_unmute_alert_instance'; +import * as api from '../apis/unmute_alert_instance'; + +jest.mock('../apis/unmute_alert_instance'); + +const params = { ruleId: '', alertInstanceId: '' }; + +describe('useUnmuteAlertInstance', () => { + const http = httpServiceMock.createStartContract(); + const notifications = notificationServiceMock.createStartContract(); + const addErrorMock = notifications.toasts.addError; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls the api when invoked with the correct parameters', async () => { + const muteAlertInstanceSpy = jest.spyOn(api, 'unmuteAlertInstance'); + + const { result } = renderHook(() => useUnmuteAlertInstance({ http, notifications }), { + wrapper: Wrapper, + }); + + result.current.mutate(params); + + await waitFor(() => { + expect(muteAlertInstanceSpy).toHaveBeenCalledWith({ + id: params.ruleId, + instanceId: params.alertInstanceId, + http: expect.anything(), + }); + }); + }); + + it('shows a toast error when the api returns an error', async () => { + const spy = jest.spyOn(api, 'unmuteAlertInstance').mockRejectedValue(new Error('An error')); + + const { result } = renderHook(() => useUnmuteAlertInstance({ http, notifications }), { + wrapper: Wrapper, + }); + + result.current.mutate(params); + + await waitFor(() => { + expect(spy).toHaveBeenCalled(); + expect(addErrorMock).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.ts b/packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.ts new file mode 100644 index 0000000000000..8525991100a2f --- /dev/null +++ b/packages/response-ops/alerts_apis/hooks/use_unmute_alert_instance.ts @@ -0,0 +1,57 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { useMutation } from '@tanstack/react-query'; +import { i18n } from '@kbn/i18n'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { mutationKeys } from '../constants'; +import type { ServerError, ToggleAlertParams } from '../types'; +import { unmuteAlertInstance } from '../apis/unmute_alert_instance'; + +const ERROR_TITLE = i18n.translate('alertsApis.unmuteAlert.error', { + defaultMessage: 'Error unmuting alert', +}); + +export interface UseUnmuteAlertInstanceParams { + http: HttpStart; + notifications: NotificationsStart; +} + +export const useUnmuteAlertInstance = ({ + http, + notifications: { toasts }, +}: UseUnmuteAlertInstanceParams) => { + return useMutation( + ({ ruleId, alertInstanceId }: ToggleAlertParams) => + unmuteAlertInstance({ http, id: ruleId, instanceId: alertInstanceId }), + { + mutationKey: mutationKeys.unmuteAlertInstance(), + context: AlertsQueryContext, + onSuccess() { + toasts.addSuccess( + i18n.translate('xpack.responseOpsAlertsApis.alertsTable.alertUnmuted', { + defaultMessage: 'Alert unmuted', + }) + ); + }, + onError: (error: ServerError) => { + if (error.name !== 'AbortError') { + toasts.addError( + error.body && error.body.message ? new Error(error.body.message) : error, + { + title: ERROR_TITLE, + } + ); + } + }, + } + ); +}; diff --git a/packages/response-ops/alerts_apis/jest.config.js b/packages/response-ops/alerts_apis/jest.config.js new file mode 100644 index 0000000000000..4e584d40c5bc7 --- /dev/null +++ b/packages/response-ops/alerts_apis/jest.config.js @@ -0,0 +1,15 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/packages/response-ops/alerts_apis'], + setupFilesAfterEnv: ['/packages/response-ops/alerts_apis/setup_tests.ts'], +}; diff --git a/packages/response-ops/alerts_apis/kibana.jsonc b/packages/response-ops/alerts_apis/kibana.jsonc new file mode 100644 index 0000000000000..63e90cae3fbc7 --- /dev/null +++ b/packages/response-ops/alerts_apis/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-browser", + "id": "@kbn/response-ops-alerts-apis", + "owner": "@elastic/response-ops", + "group": "platform", + "visibility": "shared" +} diff --git a/packages/response-ops/alerts_apis/package.json b/packages/response-ops/alerts_apis/package.json new file mode 100644 index 0000000000000..7b0c1015879b7 --- /dev/null +++ b/packages/response-ops/alerts_apis/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/response-ops-alerts-apis", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0" +} \ No newline at end of file diff --git a/packages/response-ops/alerts_apis/setup_tests.ts b/packages/response-ops/alerts_apis/setup_tests.ts new file mode 100644 index 0000000000000..e8d6b49e9d94e --- /dev/null +++ b/packages/response-ops/alerts_apis/setup_tests.ts @@ -0,0 +1,11 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +/* eslint-disable import/no-extraneous-dependencies */ +import '@testing-library/jest-dom'; +import 'jest-styled-components'; diff --git a/packages/response-ops/alerts_apis/tsconfig.json b/packages/response-ops/alerts_apis/tsconfig.json new file mode 100644 index 0000000000000..0ea4e7a36c6f2 --- /dev/null +++ b/packages/response-ops/alerts_apis/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/core-http-browser", + "@kbn/i18n", + "@kbn/alerts-ui-shared", + "@kbn/core-notifications-browser", + "@kbn/core-http-browser-mocks", + "@kbn/core-notifications-browser-mocks", + "@kbn/es-query" + ] +} diff --git a/packages/response-ops/alerts_apis/types.ts b/packages/response-ops/alerts_apis/types.ts new file mode 100644 index 0000000000000..b7e19750f6868 --- /dev/null +++ b/packages/response-ops/alerts_apis/types.ts @@ -0,0 +1,22 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; + +export type ServerError = IHttpFetchError; + +export interface ToggleAlertParams { + ruleId: string; + alertInstanceId: string; +} + +/** + * Map from rule ids to muted alert instance ids + */ +export type MutedAlerts = Record; diff --git a/packages/response-ops/alerts_fields_browser/README.md b/packages/response-ops/alerts_fields_browser/README.md new file mode 100644 index 0000000000000..db52eb2e30100 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/README.md @@ -0,0 +1,3 @@ +# @kbn/response-ops-alerts-fields-browser + +A picker component for alert document fields. diff --git a/src/platform/packages/shared/kbn-alerting-types/alert_type.ts b/packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.styles.ts similarity index 64% rename from src/platform/packages/shared/kbn-alerting-types/alert_type.ts rename to packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.styles.ts index 1663e8af556bf..a6fad874c2a6c 100644 --- a/src/platform/packages/shared/kbn-alerting-types/alert_type.ts +++ b/packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.styles.ts @@ -7,15 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { TechnicalRuleDataFieldName } from '@kbn/rule-data-utils'; +import { css } from '@emotion/react'; +import { UseEuiTheme } from '@elastic/eui'; -export interface BasicFields { - _id: string; - _index: string; -} - -export type Alert = BasicFields & { - [Property in TechnicalRuleDataFieldName]?: string[]; -} & { - [x: string]: unknown[]; +export const styles = { + badgesGroup: ({ euiTheme }: { euiTheme: UseEuiTheme['euiTheme'] }) => css` + margin-top: ${euiTheme.size.xs}; + min-height: 24px; + `, }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.test.tsx b/packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.test.tsx similarity index 80% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.test.tsx rename to packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.test.tsx index 2af93334298de..f52246f1aaa1c 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { render } from '@testing-library/react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.tsx b/packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.tsx similarity index 80% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.tsx rename to packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.tsx index 652d5ccbfafbf..e8bf89a55101c 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.tsx +++ b/packages/response-ops/alerts_fields_browser/components/categories_badges/categories_badges.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { useCallback } from 'react'; import { EuiBadge, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import { styles } from './categories_badges.styles'; diff --git a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/alerts_types.ts b/packages/response-ops/alerts_fields_browser/components/categories_badges/index.ts similarity index 75% rename from src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/alerts_types.ts rename to packages/response-ops/alerts_fields_browser/components/categories_badges/index.ts index f06bf794f6ee4..e3a795a174f2a 100644 --- a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/alerts_types.ts +++ b/packages/response-ops/alerts_fields_browser/components/categories_badges/index.ts @@ -7,11 +7,5 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export interface LegacyField { - field: string; - value: string[]; -} -export interface EsQuerySnapshot { - request: string[]; - response: string[]; -} +export { CategoriesBadges } from './categories_badges'; +export type { CategoriesBadgesProps } from './categories_badges'; diff --git a/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.styles.ts b/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.styles.ts new file mode 100644 index 0000000000000..693a40943a1ae --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.styles.ts @@ -0,0 +1,22 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { css } from '@emotion/react'; + +export const styles = { + countBadge: css` + margin-left: 5px; + `, + categoryName: ({ bold }: { bold: boolean }) => css` + font-weight: ${bold ? 'bold' : 'normal'}; + `, + selectableContainer: css` + width: 300px; + `, +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.test.tsx b/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.test.tsx similarity index 88% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.test.tsx rename to packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.test.tsx index fa64f4953ab09..6ef4139ca787d 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.test.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React from 'react'; import { render } from '@testing-library/react'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.tsx b/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.tsx similarity index 91% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.tsx rename to packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.tsx index c87bec9684678..f3f9b90a4e5d0 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.tsx +++ b/packages/response-ops/alerts_fields_browser/components/categories_selector/categories_selector.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { useCallback, useMemo, useState } from 'react'; import { omit } from 'lodash'; import { @@ -17,7 +20,7 @@ import { EuiSelectable, FilterChecked, } from '@elastic/eui'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; import * as i18n from '../../translations'; import { getFieldCount, isEscape } from '../../helpers'; import { styles } from './categories_selector.styles'; diff --git a/packages/response-ops/alerts_fields_browser/components/categories_selector/index.ts b/packages/response-ops/alerts_fields_browser/components/categories_selector/index.ts new file mode 100644 index 0000000000000..62fa588b1dc1d --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/categories_selector/index.ts @@ -0,0 +1,10 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { CategoriesSelector } from './categories_selector'; diff --git a/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.styles.ts b/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.styles.ts new file mode 100644 index 0000000000000..1cb5da4d0dac5 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.styles.ts @@ -0,0 +1,17 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { css } from '@emotion/react'; + +export const styles = { + buttonContainer: css` + display: inline-block; + position: relative; + `, +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx b/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.test.tsx similarity index 90% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx rename to packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.test.tsx index 32a6b1b85019b..88fdd2b503640 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.test.tsx @@ -1,17 +1,19 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; import { act, fireEvent, render, waitFor } from '@testing-library/react'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; -import { mockBrowserFields } from './mock'; -import { FIELD_BROWSER_WIDTH } from './helpers'; +import { mockBrowserFields } from '../../mock'; +import { FIELD_BROWSER_WIDTH } from '../../helpers'; import { FieldBrowserComponent } from './field_browser'; -import type { FieldBrowserProps } from './types'; +import type { FieldBrowserProps } from '../../types'; const defaultProps: FieldBrowserProps = { browserFields: mockBrowserFields, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx b/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.tsx similarity index 89% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx rename to packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.tsx index 32fe7a6f74df8..738f4b2837210 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_browser/field_browser.tsx @@ -1,18 +1,21 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; import { debounce } from 'lodash'; import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; -import type { FieldBrowserProps } from './types'; -import { FieldBrowserModal } from './field_browser_modal'; -import { filterBrowserFieldsByFieldName, filterSelectedBrowserFields } from './helpers'; -import * as i18n from './translations'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { FieldBrowserProps } from '../../types'; +import { FieldBrowserModal } from '../field_browser_modal/field_browser_modal'; +import { filterBrowserFieldsByFieldName, filterSelectedBrowserFields } from '../../helpers'; +import * as i18n from '../../translations'; import { styles } from './field_browser.styles'; const FIELDS_BUTTON_CLASS_NAME = 'fields-button'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser_modal.test.tsx b/packages/response-ops/alerts_fields_browser/components/field_browser_modal/field_browser_modal.test.tsx similarity index 89% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser_modal.test.tsx rename to packages/response-ops/alerts_fields_browser/components/field_browser_modal/field_browser_modal.test.tsx index 49b787041ace2..28f73aabfaca9 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser_modal.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_browser_modal/field_browser_modal.test.tsx @@ -1,14 +1,16 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { mount } from 'enzyme'; import React from 'react'; -import { mockBrowserFields } from './mock'; +import { mockBrowserFields } from '../../mock'; import { FieldBrowserModal, FieldBrowserModalProps } from './field_browser_modal'; const mockOnHide = jest.fn(); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser_modal.tsx b/packages/response-ops/alerts_fields_browser/components/field_browser_modal/field_browser_modal.tsx similarity index 87% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser_modal.tsx rename to packages/response-ops/alerts_fields_browser/components/field_browser_modal/field_browser_modal.tsx index cff489c3c15be..f826ae7cb60c1 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser_modal.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_browser_modal/field_browser_modal.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { EuiFlexGroup, EuiFlexItem, @@ -18,16 +21,20 @@ import { } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; -import type { FieldBrowserProps } from './types'; -import { Search } from './components/search'; - -import { CLOSE_BUTTON_CLASS_NAME, FIELD_BROWSER_WIDTH, RESET_FIELDS_CLASS_NAME } from './helpers'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { FieldBrowserProps } from '../../types'; +import { Search } from '../search'; -import * as i18n from './translations'; -import { CategoriesSelector } from './components/categories_selector'; -import { CategoriesBadges } from './components/categories_badges'; -import { FieldTable } from './components/field_table'; +import { + CLOSE_BUTTON_CLASS_NAME, + FIELD_BROWSER_WIDTH, + RESET_FIELDS_CLASS_NAME, +} from '../../helpers'; + +import * as i18n from '../../translations'; +import { CategoriesSelector } from '../categories_selector'; +import { CategoriesBadges } from '../categories_badges'; +import { FieldTable } from '../field_table'; export type FieldBrowserModalProps = Pick< FieldBrowserProps, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.style.ts b/packages/response-ops/alerts_fields_browser/components/field_items/field_items.style.ts similarity index 56% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.style.ts rename to packages/response-ops/alerts_fields_browser/components/field_items/field_items.style.ts index d879b239dc599..2a835c43d70bf 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.style.ts +++ b/packages/response-ops/alerts_fields_browser/components/field_items/field_items.style.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { css } from '@emotion/react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.test.tsx b/packages/response-ops/alerts_fields_browser/components/field_items/field_items.test.tsx similarity index 96% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.test.tsx rename to packages/response-ops/alerts_fields_browser/components/field_items/field_items.test.tsx index f780c4f8f2f10..46bbf336de3cd 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_items/field_items.test.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React from 'react'; import { omit } from 'lodash/fp'; import { render } from '@testing-library/react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.tsx b/packages/response-ops/alerts_fields_browser/components/field_items/field_items.tsx similarity index 92% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.tsx rename to packages/response-ops/alerts_fields_browser/components/field_items/field_items.tsx index ae0f5d7670f1a..30e9e551da520 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/field_items.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_items/field_items.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React from 'react'; import { EuiCheckbox, @@ -15,7 +18,7 @@ import { EuiScreenReaderOnly, } from '@elastic/eui'; import { uniqBy } from 'lodash/fp'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; import { EcsFlat } from '@elastic/ecs'; import { EcsMetadata } from '@kbn/alerts-as-data-utils/src/field_maps/types'; diff --git a/packages/response-ops/alerts_fields_browser/components/field_items/index.ts b/packages/response-ops/alerts_fields_browser/components/field_items/index.ts new file mode 100644 index 0000000000000..1373943bd99b5 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/field_items/index.ts @@ -0,0 +1,10 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { getFieldItemsData, getFieldColumns } from './field_items'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/field_name.test.tsx b/packages/response-ops/alerts_fields_browser/components/field_name/field_name.test.tsx similarity index 70% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/field_name.test.tsx rename to packages/response-ops/alerts_fields_browser/components/field_name/field_name.test.tsx index 3a589ac97e711..bf14f0228c4b0 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/field_name.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_name/field_name.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { mount } from 'enzyme'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/field_name.tsx b/packages/response-ops/alerts_fields_browser/components/field_name/field_name.tsx similarity index 59% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/field_name.tsx rename to packages/response-ops/alerts_fields_browser/components/field_name/field_name.tsx index 0ef0ce64c637b..8f64e690697d4 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/field_name.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_name/field_name.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; diff --git a/packages/response-ops/alerts_fields_browser/components/field_name/index.ts b/packages/response-ops/alerts_fields_browser/components/field_name/index.ts new file mode 100644 index 0000000000000..7add3b194f33a --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/field_name/index.ts @@ -0,0 +1,10 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { FieldName } from './field_name'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.styles.ts b/packages/response-ops/alerts_fields_browser/components/field_table/field_table.styles.ts similarity index 54% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.styles.ts rename to packages/response-ops/alerts_fields_browser/components/field_table/field_table.styles.ts index 665848e47ab2b..6eaab01b01d9f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.styles.ts +++ b/packages/response-ops/alerts_fields_browser/components/field_table/field_table.styles.ts @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { UseEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.test.tsx b/packages/response-ops/alerts_fields_browser/components/field_table/field_table.test.tsx similarity index 93% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.test.tsx rename to packages/response-ops/alerts_fields_browser/components/field_table/field_table.test.tsx index 355cee63f2f32..5d85abbd6295a 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_table/field_table.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.tsx b/packages/response-ops/alerts_fields_browser/components/field_table/field_table.tsx similarity index 90% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.tsx rename to packages/response-ops/alerts_fields_browser/components/field_table/field_table.tsx index 7647e8c625761..d6ba96a4fca7b 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_table/field_table.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiInMemoryTable, @@ -12,7 +15,7 @@ import { useEuiTheme, CriteriaWithPagination, } from '@elastic/eui'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; import { getFieldColumns, getFieldItemsData } from '../field_items'; import { CATEGORY_TABLE_CLASS_NAME, TABLE_HEIGHT } from '../../helpers'; import type { BrowserFieldItem, FieldBrowserProps, GetFieldTableColumns } from '../../types'; diff --git a/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.styles.ts b/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.styles.ts new file mode 100644 index 0000000000000..c04635d5efb49 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.styles.ts @@ -0,0 +1,16 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { css } from '@emotion/react'; + +export const styles = { + count: css` + font-weight: bold; + `, +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.test.tsx b/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.test.tsx similarity index 90% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.test.tsx rename to packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.test.tsx index e46b025e86826..2bde369dcdff1 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.tsx b/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.tsx similarity index 89% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.tsx rename to packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.tsx index b1fa7d9b82ef9..5d65c6aa04137 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.tsx +++ b/packages/response-ops/alerts_fields_browser/components/field_table/field_table_header.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { useCallback, useState } from 'react'; import { EuiText, diff --git a/packages/response-ops/alerts_fields_browser/components/field_table/index.ts b/packages/response-ops/alerts_fields_browser/components/field_table/index.ts new file mode 100644 index 0000000000000..52faf941ddf78 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/field_table/index.ts @@ -0,0 +1,11 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { FieldTable } from './field_table'; +export type { FieldTableProps } from './field_table'; diff --git a/packages/response-ops/alerts_fields_browser/components/search/index.ts b/packages/response-ops/alerts_fields_browser/components/search/index.ts new file mode 100644 index 0000000000000..aac6c19da1575 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/components/search/index.ts @@ -0,0 +1,10 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { Search } from './search'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/search.test.tsx b/packages/response-ops/alerts_fields_browser/components/search/search.test.tsx similarity index 81% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/search.test.tsx rename to packages/response-ops/alerts_fields_browser/components/search/search.test.tsx index 25390da9506cf..042d8fba36d41 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/search.test.tsx +++ b/packages/response-ops/alerts_fields_browser/components/search/search.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { mount } from 'enzyme'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/search.tsx b/packages/response-ops/alerts_fields_browser/components/search/search.tsx similarity index 67% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/search.tsx rename to packages/response-ops/alerts_fields_browser/components/search/search.tsx index f2b833700aa43..650d2ce69314e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/search.tsx +++ b/packages/response-ops/alerts_fields_browser/components/search/search.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/helpers.test.ts b/packages/response-ops/alerts_fields_browser/helpers.test.ts similarity index 96% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/helpers.test.ts rename to packages/response-ops/alerts_fields_browser/helpers.test.ts index 3deddab635778..1951a0f11b37d 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/helpers.test.ts +++ b/packages/response-ops/alerts_fields_browser/helpers.test.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { mockBrowserFields } from './mock'; @@ -15,7 +17,7 @@ import { filterBrowserFieldsByFieldName, filterSelectedBrowserFields, } from './helpers'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; import { EcsFlat } from '@elastic/ecs'; describe('helpers', () => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/helpers.ts b/packages/response-ops/alerts_fields_browser/helpers.ts similarity index 92% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/helpers.ts rename to packages/response-ops/alerts_fields_browser/helpers.ts index 6df7328aa2762..9df810d7f4c75 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/helpers.ts +++ b/packages/response-ops/alerts_fields_browser/helpers.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EcsMetadata } from '@kbn/alerts-as-data-utils/src/field_maps/types'; @@ -11,9 +13,9 @@ import { ALERT_MAINTENANCE_WINDOW_IDS, DefaultAlertFieldName, } from '@kbn/rule-data-utils'; -import { BrowserField, BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { BrowserField, BrowserFields } from '@kbn/rule-registry-plugin/common'; import { isEmpty } from 'lodash/fp'; -import { CASES, MAINTENANCE_WINDOWS } from '../translations'; +import { CASES, MAINTENANCE_WINDOWS } from './translations'; export const FIELD_BROWSER_WIDTH = 925; export const TABLE_HEIGHT = 260; diff --git a/packages/response-ops/alerts_fields_browser/index.ts b/packages/response-ops/alerts_fields_browser/index.ts new file mode 100644 index 0000000000000..d292ac79283fb --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/index.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { FieldBrowser } from './components/field_browser/field_browser'; +export type { FieldBrowserProps, FieldBrowserOptions } from './types'; + +export { FieldBrowser }; diff --git a/packages/response-ops/alerts_fields_browser/jest.config.js b/packages/response-ops/alerts_fields_browser/jest.config.js new file mode 100644 index 0000000000000..b3517ca061fca --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/jest.config.js @@ -0,0 +1,15 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/packages/response-ops/alerts_fields_browser'], + setupFilesAfterEnv: ['/packages/response-ops/alerts_fields_browser/setup_tests.ts'], +}; diff --git a/packages/response-ops/alerts_fields_browser/kibana.jsonc b/packages/response-ops/alerts_fields_browser/kibana.jsonc new file mode 100644 index 0000000000000..ea5d46a04ae34 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-browser", + "id": "@kbn/response-ops-alerts-fields-browser", + "owner": "@elastic/response-ops", + "group": "platform", + "visibility": "shared" +} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/mock.ts b/packages/response-ops/alerts_fields_browser/mock.ts similarity index 97% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/mock.ts rename to packages/response-ops/alerts_fields_browser/mock.ts index 59d9c33838250..189078d135ef6 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/mock.ts +++ b/packages/response-ops/alerts_fields_browser/mock.ts @@ -1,12 +1,14 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; const DEFAULT_INDEX_PATTERN = [ 'apm-*-transaction*', diff --git a/packages/response-ops/alerts_fields_browser/package.json b/packages/response-ops/alerts_fields_browser/package.json new file mode 100644 index 0000000000000..93efb418dbbdf --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/response-ops-alerts-fields-browser", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0" +} \ No newline at end of file diff --git a/packages/response-ops/alerts_fields_browser/setup_tests.ts b/packages/response-ops/alerts_fields_browser/setup_tests.ts new file mode 100644 index 0000000000000..5ebc6d3dac1ca --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/setup_tests.ts @@ -0,0 +1,11 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +// eslint-disable-next-line import/no-extraneous-dependencies +import '@testing-library/jest-dom'; diff --git a/packages/response-ops/alerts_fields_browser/translations.ts b/packages/response-ops/alerts_fields_browser/translations.ts new file mode 100644 index 0000000000000..121a9df0ee3f5 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/translations.ts @@ -0,0 +1,118 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { i18n } from '@kbn/i18n'; + +export const CASES = i18n.translate('responseOpsAlertsFieldsBrowser.cases.label', { + defaultMessage: 'Cases', +}); + +export const MAINTENANCE_WINDOWS = i18n.translate( + 'responseOpsAlertsFieldsBrowser.maintenanceWindows.label', + { + defaultMessage: 'Maintenance Windows', + } +); + +export const CATEGORY = i18n.translate('responseOpsAlertsFieldsBrowser.categoryLabel', { + defaultMessage: 'Category', +}); + +export const CATEGORIES = i18n.translate('responseOpsAlertsFieldsBrowser.categoriesTitle', { + defaultMessage: 'Categories', +}); + +export const CATEGORIES_COUNT = (totalCount: number) => + i18n.translate('responseOpsAlertsFieldsBrowser.categoriesCountTitle', { + values: { totalCount }, + defaultMessage: '{totalCount} {totalCount, plural, =1 {category} other {categories}}', + }); + +export const CLOSE = i18n.translate('responseOpsAlertsFieldsBrowser.closeButton', { + defaultMessage: 'Close', +}); + +export const FIELDS_BROWSER = i18n.translate('responseOpsAlertsFieldsBrowser.fieldBrowserTitle', { + defaultMessage: 'Fields', +}); + +export const DESCRIPTION = i18n.translate('responseOpsAlertsFieldsBrowser.descriptionLabel', { + defaultMessage: 'Description', +}); + +export const DESCRIPTION_FOR_FIELD = (field: string) => + i18n.translate('responseOpsAlertsFieldsBrowser.descriptionForScreenReaderOnly', { + values: { + field, + }, + defaultMessage: 'Description for field {field}:', + }); + +export const NAME = i18n.translate('responseOpsAlertsFieldsBrowser.fieldName', { + defaultMessage: 'Name', +}); + +export const FIELD = i18n.translate('responseOpsAlertsFieldsBrowser.fieldLabel', { + defaultMessage: 'Field', +}); + +export const FIELDS = i18n.translate('responseOpsAlertsFieldsBrowser.fieldsTitle', { + defaultMessage: 'Fields', +}); + +export const FIELDS_SHOWING = i18n.translate('responseOpsAlertsFieldsBrowser.fieldsCountShowing', { + defaultMessage: 'Showing', +}); + +export const FIELDS_COUNT = (totalCount: number) => + i18n.translate('responseOpsAlertsFieldsBrowser.fieldsCountTitle', { + values: { totalCount }, + defaultMessage: '{totalCount, plural, =1 {field} other {fields}}', + }); + +export const FILTER_PLACEHOLDER = i18n.translate( + 'responseOpsAlertsFieldsBrowser.filterPlaceholder', + { + defaultMessage: 'Field name', + } +); + +export const NO_FIELDS_MATCH = i18n.translate('responseOpsAlertsFieldsBrowser.noFieldsMatchLabel', { + defaultMessage: 'No fields match', +}); + +export const NO_FIELDS_MATCH_INPUT = (searchInput: string) => + i18n.translate('responseOpsAlertsFieldsBrowser.noFieldsMatchInputLabel', { + defaultMessage: 'No fields match {searchInput}', + values: { + searchInput, + }, + }); + +export const RESET_FIELDS = i18n.translate('responseOpsAlertsFieldsBrowser.resetFieldsLink', { + defaultMessage: 'Reset Fields', +}); + +export const VIEW_COLUMN = (field: string) => + i18n.translate('responseOpsAlertsFieldsBrowser.viewColumnCheckboxAriaLabel', { + values: { field }, + defaultMessage: 'View {field} column', + }); + +export const VIEW_LABEL = i18n.translate('responseOpsAlertsFieldsBrowser.viewLabel', { + defaultMessage: 'View', +}); + +export const VIEW_VALUE_SELECTED = i18n.translate('responseOpsAlertsFieldsBrowser.viewSelected', { + defaultMessage: 'selected', +}); + +export const VIEW_VALUE_ALL = i18n.translate('responseOpsAlertsFieldsBrowser.viewAll', { + defaultMessage: 'all', +}); diff --git a/packages/response-ops/alerts_fields_browser/tsconfig.json b/packages/response-ops/alerts_fields_browser/tsconfig.json new file mode 100644 index 0000000000000..8036e7dede0a5 --- /dev/null +++ b/packages/response-ops/alerts_fields_browser/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/rule-registry-plugin", + "@kbn/alerts-as-data-utils", + "@kbn/rule-data-utils", + ] +} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/types.ts b/packages/response-ops/alerts_fields_browser/types.ts similarity index 78% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/types.ts rename to packages/response-ops/alerts_fields_browser/types.ts index 898fd67140837..deb845dbc9caf 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/types.ts +++ b/packages/response-ops/alerts_fields_browser/types.ts @@ -1,12 +1,14 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import type { EuiBasicTableColumn } from '@elastic/eui'; -import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import type { BrowserFields } from '@kbn/rule-registry-plugin/common'; /** * An item rendered in the table diff --git a/packages/response-ops/alerts_table/README.md b/packages/response-ops/alerts_table/README.md new file mode 100644 index 0000000000000..2d4ef0fa3f2eb --- /dev/null +++ b/packages/response-ops/alerts_table/README.md @@ -0,0 +1,174 @@ +# @kbn/response-ops-alerts-table + +An abstraction on top of `EuiDataGrid` dedicated to rendering alert documents. + +## Usage + +In addition to `EuiDataGrid`'s functionality, the table manages the paginated and cached fetching of alerts, based on +the provided `ruleTypeIds` and `consumers` (the final query can be refined through the `query` and `initialSort` props). +The `id` prop is used to persist the table state in `localStorage`. + +```tsx + +``` + +## Columns + +Just like in `EuiDataGrid`, the columns are customizable through the `columns` prop. In addition to those, the table +renders an "Actions" column with default alert call-to-actions and provides row selection and bulk actions +functionality. + +```tsx +// The @kbn/rule-data-utils package exports constants +// for many common alert field keys +import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; + + +``` + +## Cells, popovers and flyouts + +All the sub-components of the table are customizable through the `render*` +props (`renderCellValue`, `renderCellPopover`, `renderActionsCell`, etc.). Values passed to these props are treated as +components, allowing hooks, context, and other React concepts to be used. + +```tsx +const CustomCellValue: GetAlertsTableProp<'renderCellValue'> = ({ alert }) => { + // ... +}; + + +``` + +## Render context + +All the sub-component renderers receive as part of their props a context object ( +see [EuiDataGrid's `cellContext`](https://eui.elastic.co/#/tabular-content/data-grid-cells-popovers%23cell-context)) +with common utilities and services (i.e. the fetched alerts, loading states etc.). +You can add properties to this context by means of the `additionalContext` prop: + +```tsx + { + // ... + }} +/> +``` + +The context type is inferred based on the `additionalContext` prop and all render functions props are typed accordingly. +To avoid prop drilling, you can use the `useAlertsTableContext` hook to access the same context in any sub-component. + +```tsx +const CustomCellValue = ({ alert }) => { + const { alertsCount, myCustomProperty } = useAlertsTableContext(); + + // ... +}; +``` + +In order to define your custom sub-components separately from the table but still benefit from the context type +inference, you may want to extract props from the `AlertsTableProps` type. The `GetAlertsTableProp` utility type is +provided for this: it extracts the type of a specific prop from the `AlertsTableProps` type, excluding `undefined` in +case of optional props. + +```tsx +import type { GetAlertsTableProp } from '@kbn/response-ops-alerts-table/types'; + +export const CustomCellValue: GetAlertsTableProp<'renderCellValue'> = ({ alert }) => { + // ... +}; +``` + +If you also have an additional context, you can define a type for it and wrap the `AlertsTableProps`, providing it as a +generic: + +```tsx +import type { AlertsTableProps } from '@kbn/response-ops-alerts-table/types'; + +interface MyAdditionalContext { + myCustomProperty: string; +} + +export type MyAlertsTableProps = AlertsTableProps; +export type GetMyAlertsTableProp = Exclude; + +export const CustomCellValue: GetMyAlertsTableProp<'renderCellValue'> = ({ myCustomProperty }) => { + // ... +}; + + + additionalContext={{ + myCustomProperty: 'my custom value', + }} + renderCellValue={CustomCellValue} +/> +``` + +## Dependencies + +The table relies on the following Kibana services, expected in the `services` prop: + +- `data` +- `http` +- `notifications` +- `fieldFormats` +- `application` +- `licensing` +- `settings` +- `cases` (optional) + +## Integrations + +The table has built-in integration with Maintenance Windows and Cases. If alerts have maintenance windows or cases +associated to them, they will be loaded and made available through the `maintenanceWindows` and `cases` properties of +the render context. +A special cell renderer is used by default for the `kibana.alert.maintenance_window_ids` and `kibana.alert.case_ids` +columns. + +## Lazy loading + +Contrary to the previous implementation exported by `triggersActionsUI`, this package doesn't prescribe how to lazy load +the table component; a default export is just provided for convenience. However, do consider that +the `React.lazy` function loses the original generic types of the component. To make the type inference work correctly, +you can assert the type of the lazy loaded component using a type import: + +```tsx +import type { AlertsTable as AlertsTableType } from '@kbn/response-ops-alerts-table'; + +const AlertsTable = React.lazy(() => import('@kbn/response-ops-alerts-table')) as AlertsTableType; +``` + +## Mocking + +When mocking the table, keep in mind that the component is manually typed as a normal function component (to keep its +generic types), but it's actually a memoized, forwardRef'ed component. To mock it properly, mock the entire module: + +```tsx +jest.mock('@kbn/response-ops-alerts-table', () => ({ + AlertsTable: jest.fn().mockImplementation(() =>
), +})); +``` + +## What's new compared to `triggersActionsUI`? + +- The alerts table registry was removed. The table is now a standalone component and the configuration is based entirely + on props. +- All the custom renderers (cell, cell popover, actions cell, etc.) are now exposed as strongly typed props (`render*`). +- More `EuiDataGrid` props are exposed for customization. diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_cases.test.ts b/packages/response-ops/alerts_table/apis/bulk_get_cases.test.ts similarity index 68% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_cases.test.ts rename to packages/response-ops/alerts_table/apis/bulk_get_cases.test.ts index 6368bafb9c054..575ac3ce7abae 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_cases.test.ts +++ b/packages/response-ops/alerts_table/apis/bulk_get_cases.test.ts @@ -1,14 +1,16 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { bulkGetCases } from './bulk_get_cases'; import { coreMock } from '@kbn/core/public/mocks'; -describe('Bulk Get Cases API', () => { +describe('bulkGetCases', () => { const abortCtrl = new AbortController(); const mockCoreSetup = coreMock.createSetup(); const http = mockCoreSetup.http; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_cases.ts b/packages/response-ops/alerts_table/apis/bulk_get_cases.ts similarity index 72% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_cases.ts rename to packages/response-ops/alerts_table/apis/bulk_get_cases.ts index ec9e1a11564e4..08b6df7ba5078 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_cases.ts +++ b/packages/response-ops/alerts_table/apis/bulk_get_cases.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { CaseStatuses } from '@kbn/cases-components'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.test.ts b/packages/response-ops/alerts_table/apis/bulk_get_maintenance_windows.test.ts similarity index 70% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.test.ts rename to packages/response-ops/alerts_table/apis/bulk_get_maintenance_windows.test.ts index d59d4a8339cdb..c6d0638be3f04 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.test.ts +++ b/packages/response-ops/alerts_table/apis/bulk_get_maintenance_windows.test.ts @@ -1,14 +1,16 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { coreMock } from '@kbn/core/public/mocks'; import { bulkGetMaintenanceWindows } from './bulk_get_maintenance_windows'; -describe('Bulk Get Maintenance Windows API', () => { +describe('bulkGetMaintenanceWindows', () => { const mockCoreSetup = coreMock.createSetup(); const http = mockCoreSetup.http; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts b/packages/response-ops/alerts_table/apis/bulk_get_maintenance_windows.ts similarity index 75% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts rename to packages/response-ops/alerts_table/apis/bulk_get_maintenance_windows.ts index c16dfa91d6a86..6e830f10fecb4 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts +++ b/packages/response-ops/alerts_table/apis/bulk_get_maintenance_windows.ts @@ -1,16 +1,16 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import { HttpStart } from '@kbn/core-http-browser'; -import { - MaintenanceWindow, - INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, -} from '@kbn/alerting-plugin/common'; -import { AsApiContract } from '@kbn/actions-plugin/common'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import type { AsApiContract } from '@kbn/actions-plugin/common'; +import { INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH } from '../constants'; export interface BulkGetMaintenanceWindowsParams { http: HttpStart; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/assets/illustration_product_no_results_magnifying_glass.svg b/packages/response-ops/alerts_table/assets/illustration_product_no_results_magnifying_glass.svg similarity index 100% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/assets/illustration_product_no_results_magnifying_glass.svg rename to packages/response-ops/alerts_table/assets/illustration_product_no_results_magnifying_glass.svg diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/alert_actions_cell.tsx b/packages/response-ops/alerts_table/components/alert_actions_cell.tsx similarity index 76% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/alert_actions_cell.tsx rename to packages/response-ops/alerts_table/components/alert_actions_cell.tsx index d953e3fcc8f7e..b76dec514f306 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/alert_actions_cell.tsx +++ b/packages/response-ops/alerts_table/components/alert_actions_cell.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { @@ -15,8 +17,8 @@ import { import React, { useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import type { AlertsTableProps } from '../../../../types'; -import { DefaultAlertActions } from '.'; +import { DefaultAlertActions } from './default_alert_actions'; +import type { GetAlertsTableProp } from '../types'; const actionsToolTip = i18n.translate('xpack.triggersActionsUI.alertsTable.moreActionsTextLabel', { defaultMessage: 'More actions', @@ -25,7 +27,7 @@ const actionsToolTip = i18n.translate('xpack.triggersActionsUI.alertsTable.moreA /** * The cell containing contextual actions for a single alert row in the table */ -export const AlertActionsCell: AlertsTableProps['renderActionsCell'] = (props) => { +export const AlertActionsCell: GetAlertsTableProp<'renderActionsCell'> = (props) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const closeActionsPopover = () => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/alert_lifecycle_status_cell.test.tsx b/packages/response-ops/alerts_table/components/alert_lifecycle_status_cell.test.tsx similarity index 57% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/alert_lifecycle_status_cell.test.tsx rename to packages/response-ops/alerts_table/components/alert_lifecycle_status_cell.test.tsx index 325954f1b2786..d2e76b06f1bee 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/alert_lifecycle_status_cell.test.tsx +++ b/packages/response-ops/alerts_table/components/alert_lifecycle_status_cell.test.tsx @@ -1,29 +1,28 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; -import { screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import { Alert } from '@kbn/alerting-types'; import { AlertLifecycleStatusCell } from './alert_lifecycle_status_cell'; import { CellComponentProps } from '../types'; -import { Alert } from '../../../../types'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { getCasesMapMock } from '../cases/index.mock'; -import { getMaintenanceWindowsMapMock } from '../maintenance_windows/index.mock'; - -jest.mock('../../../../common/lib/kibana'); +import { getCasesMapMock } from '../mocks/cases.mock'; +import { getMaintenanceWindowsMapMock } from '../mocks/maintenance_windows.mock'; describe('AlertLifecycleStatusCell', () => { const casesMap = getCasesMapMock(); const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); - const alert = { + const alert: Alert = { _id: 'alert-id', _index: 'alert-index', 'kibana.alert.status': ['active'], - } as Alert; + }; const props = { isLoading: false, @@ -32,41 +31,34 @@ describe('AlertLifecycleStatusCell', () => { maintenanceWindows: maintenanceWindowsMap, columnId: 'kibana.alert.status', showAlertStatusWithFlapping: true, + // Assertion used to avoid defining all the (numerous) context properties } as CellComponentProps; - let appMockRender: AppMockRenderer; - - beforeEach(() => { - appMockRender = createAppMockRenderer(); - }); - it('shows the status', async () => { - appMockRender.render(); + render(); expect(screen.getByText('Active')).toBeInTheDocument(); }); it('does not shows the status if showAlertStatusWithFlapping=false', async () => { - appMockRender.render( - - ); + render(); expect(screen.queryByText('Active')).not.toBeInTheDocument(); }); it('shows the status with flapping', async () => { - appMockRender.render( + render( ); expect(screen.getByText('Flapping')).toBeInTheDocument(); }); it('shows the status with multiple values', async () => { - appMockRender.render( + render( ); @@ -74,12 +66,7 @@ describe('AlertLifecycleStatusCell', () => { }); it('shows the default cell if the status is empty', async () => { - appMockRender.render( - - ); + render(); expect(screen.getByText('--')).toBeInTheDocument(); }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/alert_lifecycle_status_cell.tsx b/packages/response-ops/alerts_table/components/alert_lifecycle_status_cell.tsx similarity index 67% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/alert_lifecycle_status_cell.tsx rename to packages/response-ops/alerts_table/components/alert_lifecycle_status_cell.tsx index 188b0a16cb5a5..a4ea204f2a8d6 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/alert_lifecycle_status_cell.tsx +++ b/packages/response-ops/alerts_table/components/alert_lifecycle_status_cell.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { AlertStatus, ALERT_FLAPPING, ALERT_STATUS } from '@kbn/rule-data-utils'; @@ -10,9 +12,9 @@ import React, { memo } from 'react'; import { EuiBadge, EuiFlexGroup, EuiToolTip, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; -import { AlertLifecycleStatusBadge } from '../../../components/alert_lifecycle_status_badge'; +import { AlertLifecycleStatusBadge } from '@kbn/alerts-ui-shared'; import { DefaultCell } from './default_cell'; -import { useAlertMutedState } from '../hooks/alert_mute/use_alert_muted_state'; +import { useAlertMutedState } from '../hooks/use_alert_muted_state'; import type { CellComponent } from '../types'; export const AlertLifecycleStatusCell: CellComponent = memo((props) => { @@ -24,16 +26,16 @@ export const AlertLifecycleStatusCell: CellComponent = memo((props) => { return null; } - const alertStatus = (alert && alert[ALERT_STATUS]) ?? []; + const alertStatus = (alert?.[ALERT_STATUS] ?? []) as string[] | undefined; if (Array.isArray(alertStatus) && alertStatus.length) { - const flapping = (alert && alert[ALERT_FLAPPING]) ?? []; + const flapping = alert?.[ALERT_FLAPPING]?.[0] as boolean | undefined; return ( {isMuted && ( ({ - useUiSetting$: jest.fn((value: string) => ['0,0']), -})); - -// useKibana mock -const mockCaseService = createCasesServiceMock(); -jest.mock('@kbn/kibana-react-plugin/public', () => { - const original = jest.requireActual('@kbn/kibana-react-plugin/public'); - - return { - ...original, - useKibana: () => ({ - services: { - cases: mockCaseService, - notifications: { - toasts: { - addDanger: jest.fn(), - addSuccess: jest.fn(), - }, - }, - }, - }), - }; -}); +} from '../constants'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { testQueryClientConfig } from '@kbn/alerts-ui-shared/src/common/test_utils/test_query_client_config'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -jest.mock('./cases/use_case_view_navigation'); +jest.mock('../hooks/use_case_view_navigation'); const cellActionOnClickMockedFn = jest.fn(); @@ -74,6 +52,48 @@ afterAll(() => { cleanup(); }); +export type BaseAlertsDataGridProps = AlertsDataGridProps; +export type TestAlertsDataGridProps = Partial> & { + renderContext?: Partial>; +}; + +const queryClient = new QueryClient(testQueryClientConfig); + +export const mockDataGridProps: Partial = { + pageSizeOptions: [1, 10, 20, 50, 100], + leadingControlColumns: [], + trailingControlColumns: [], + visibleColumns: mockColumns.map((c) => c.id), + 'data-test-subj': 'testTable', + onToggleColumn: jest.fn(), + onResetColumns: jest.fn(), + onChangeVisibleColumns: jest.fn(), + query: {}, + sort: [], + alertsQuerySnapshot: { request: [], response: [] }, + onSortChange: jest.fn(), + onChangePageIndex: jest.fn(), + onChangePageSize: jest.fn(), + getBulkActions: () => [ + { + id: 0, + items: [ + { + label: 'Fake Bulk Action', + key: 'fakeBulkAction', + 'data-test-subj': 'fake-bulk-action', + disableOnQuery: false, + onClick: () => {}, + }, + ], + }, + ], + fieldsBrowserOptions: { + createFieldButton: () => , + }, + casesConfiguration: { featureId: 'test-feature-id', owner: ['cases'] }, +}; + describe('AlertsDataGrid', () => { const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; useCaseViewNavigationMock.mockReturnValue({ navigateToCaseView: jest.fn() }); @@ -84,14 +104,9 @@ describe('AlertsDataGrid', () => { renderContext?: Partial>; } > = (props) => { - const { AppWrapper } = useMemo( - () => createAppMockRenderer({ queryClientContext: AlertsQueryContext }), - [] - ); - const bulkActionsStore = useReducer( bulkActionsReducer, - props.initialBulkActionsState || mockBulkActionsState + props.initialBulkActionsState || createMockBulkActionsState() ); const renderContext = useMemo( () => ({ @@ -103,11 +118,13 @@ describe('AlertsDataGrid', () => { ); return ( - - - - - + + + + + + + ); }; @@ -238,7 +255,7 @@ describe('AlertsDataGrid', () => { expect(screen.queryByTestId('expandColumnCellOpenFlyoutButton-0')).not.toBeInTheDocument(); }); - it('should render no action column if there is neither the action nor the expand action config is set', () => { + it('should render no action column if neither the action nor the expand action config is set', () => { render( { it('should show the row loader when callback triggered with false', async () => { const initialBulkActionsState = { - ...mockBulkActionsState, + ...createMockBulkActionsState(), rowSelection: new Map([[0, { isLoading: true }]]), }; @@ -365,7 +382,7 @@ describe('AlertsDataGrid', () => { @@ -390,7 +407,7 @@ describe('AlertsDataGrid', () => { showColumnSelector: true, }} initialBulkActionsState={{ - ...mockBulkActionsState, + ...createMockBulkActionsState(), rowSelection: new Map(), }} /> diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx b/packages/response-ops/alerts_table/components/alerts_data_grid.tsx similarity index 56% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx rename to packages/response-ops/alerts_table/components/alerts_data_grid.tsx index 9d6a8c062d142..1438d0b7020ff 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.tsx +++ b/packages/response-ops/alerts_table/components/alerts_data_grid.tsx @@ -1,64 +1,43 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { - ComponentProps, - FC, - lazy, - memo, - PropsWithChildren, - Suspense, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import React, { FC, lazy, Suspense, useCallback, useMemo } from 'react'; import { - EuiCodeBlock, EuiDataGrid, EuiDataGridControlColumn, EuiDataGridProps, EuiDataGridStyle, EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiSkeletonText, - EuiSpacer, - EuiText, RenderCellValue, tint, useEuiTheme, } from '@elastic/eui'; -import styled from '@emotion/styled'; -import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; -import { EuiDataGridCellPopoverElementProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; import { euiThemeVars } from '@kbn/ui-theme'; -import { BulkActionsCell } from './bulk_actions/components/row_cell'; -import { BulkActionsHeader } from './bulk_actions/components'; -import { useAlertsTableContext } from './contexts/alerts_table_context'; -import { useBulkActions, useSorting } from './hooks'; +import { ControlColumnHeaderCell } from './control_column_header_cell'; +import { CellValueHost } from './cell_value_host'; +import { BulkActionsCell } from './bulk_actions_cell'; +import { BulkActionsHeader } from './bulk_actions_header_cell'; import { AdditionalContext, - Alert, AlertsDataGridProps, - AlertsTableProps, BulkActionsVerbs, CellActionsOptions, - FetchAlertData, -} from '../../../types'; -import { ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL } from './translations'; -import { useGetToolbarVisibility } from './toolbar'; -import { InspectButtonContainer } from './toolbar/components/inspect'; -import { SystemCellId } from './types'; -import { SystemCellFactory, systemCells } from './cells'; -import { typedMemo } from './utils'; -import type { AlertsFlyout as AlertsFlyoutType } from './alerts_flyout/alerts_flyout'; -import { ErrorBoundary } from '../common/components/error_boundary'; +} from '../types'; +import { useGetToolbarVisibility } from '../hooks/use_toolbar_visibility'; +import { InspectButtonContainer } from './alerts_query_inspector'; +import { typedMemo } from '../utils/react'; +import type { AlertsFlyout as AlertsFlyoutType } from './alerts_flyout'; +import { useBulkActions } from '../hooks/use_bulk_actions'; +import { useSorting } from '../hooks/use_sorting'; +import { CellPopoverHost } from './cell_popover_host'; +import { NonVirtualizedGridBody } from './non_virtualized_grid_body'; const AlertsFlyout = lazy(() => import('./alerts_flyout')) as typeof AlertsFlyoutType; @@ -73,204 +52,10 @@ const defaultCellActionsOptions: CellActionsOptions = { disabledCellActions: [], }; const DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50, 100]; -const DEFAULT_ACTIONS_COLUMNS_WIDTH = 75; +const DEFAULT_ACTIONS_COLUMN_WIDTH = 75; const stableMappedRowClasses: EuiDataGridStyle['rowClasses'] = {}; -interface BasicCellValueProps { - columnId: string; - alert: Alert; -} - -const BasicCellValue = memo(({ alert, columnId }: BasicCellValueProps) => { - const value = alert[columnId]; - if (Array.isArray(value)) { - return <>{value.length ? value.join() : '--'}; - } - return <>{value}; -}); - -const ControlColumnHeaderRenderCell = memo(() => { - return ( - - {ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL} - - ); -}); - -const CustomCellWrapper = ({ children }: PropsWithChildren) => ( - - {children} - -); - -const isSystemCell = (columnId: string): columnId is SystemCellId => { - return systemCells.includes(columnId as SystemCellId); -}; - -// Here we force the error callout to be the same height as the cell content -// so that the error detail gets hidden in the overflow area and only shown in -// the cell popover -const errorCalloutStyles = css` - height: 1lh; -`; - -/** - * An error callout that displays the error stack in a code block - */ -const ViewError = ({ error }: { error: Error }) => ( - <> - - - - - - - - - - - - - - {error.stack} - -); - -const Row = styled.div` - display: flex; - min-width: fit-content; -`; - -type CustomGridBodyProps = Pick< - Parameters>['0'], - 'Cell' | 'visibleColumns' -> & { - alertsData: FetchAlertData['oldAlertsData']; - isLoading: boolean; - pageIndex: number; - pageSize: number; - actualGridStyle: EuiDataGridStyle; - stripes?: boolean; -}; - -const CellValueHost: AlertsTableProps['renderCellValue'] = (props) => { - const { - columnId, - renderCellValue: CellValue, - isLoading, - alerts, - oldAlertsData, - ecsAlertsData, - cases, - maintenanceWindows, - showAlertStatusWithFlapping, - casesConfig, - rowIndex, - pageIndex, - pageSize, - } = props; - const idx = rowIndex - pageSize * pageIndex; - const alert = alerts[idx]; - const legacyAlert = oldAlertsData[idx]; - const ecsAlert = ecsAlertsData[idx]; - if (isSystemCell(columnId)) { - return ( - - ); - } else if (alert) { - if (CellValue) { - return ( - - - - ); - } else { - return ; - } - } else if (isLoading) { - return ; - } - return null; -}; - -const CellPopoverHost = (props: EuiDataGridCellPopoverElementProps) => { - const { rowIndex, DefaultCellPopover } = props; - const renderContext = useAlertsTableContext(); - const { pageSize, pageIndex, alerts, renderCellPopover: CellPopover } = renderContext; - - const idx = rowIndex - pageSize * pageIndex; - const alert = alerts[idx]; - if (alert && CellPopover) { - return ( - - - - ); - } - - return ; -}; - -const CustomGridBody = memo( - ({ - alertsData, - isLoading, - pageIndex, - pageSize, - actualGridStyle, - visibleColumns, - Cell, - stripes, - }: CustomGridBodyProps) => { - return ( - <> - {alertsData - .concat(isLoading ? Array.from({ length: pageSize - alertsData.length }) : []) - .map((_row, rowIndex) => ( - - {visibleColumns.map((_col, colIndex) => ( - - ))} - - ))} - - ); - } -); - export const AlertsDataGrid = typedMemo( (props: AlertsDataGridProps) => { const { @@ -300,7 +85,7 @@ export const AlertsDataGrid = typedMemo( onPaginateFlyout, onChangePageSize, onChangePageIndex, - actionsColumnWidth = DEFAULT_ACTIONS_COLUMNS_WIDTH, + actionsColumnWidth = DEFAULT_ACTIONS_COLUMN_WIDTH, getBulkActions, fieldsBrowserOptions, cellActionsOptions, @@ -313,7 +98,6 @@ export const AlertsDataGrid = typedMemo( alerts, alertsCount, isLoadingAlerts, - oldAlertsData, browserFields, renderActionsCell: ActionsCell, pageIndex, @@ -321,37 +105,11 @@ export const AlertsDataGrid = typedMemo( refresh: refreshQueries, columns, dataGridRef, + services: { http, notifications, application, cases: casesService, settings }, } = renderContext; const { colorMode } = useEuiTheme(); - - const [activeRowClasses, setActiveRowClasses] = useState< - NonNullable - >({}); - const { sortingColumns, onSort } = useSorting(onSortChange, visibleColumns, sortingFields); - - const bulkActionArgs = useMemo( - () => ({ - ruleTypeIds, - query, - alertsCount: alerts.length, - casesConfig: casesConfiguration, - getBulkActions, - refresh: refreshQueries, - hideBulkActions, - }), - [ - ruleTypeIds, - query, - alerts.length, - casesConfiguration, - getBulkActions, - refreshQueries, - hideBulkActions, - ] - ); - const { isBulkActionsColumnActive, bulkActionsState, @@ -359,40 +117,32 @@ export const AlertsDataGrid = typedMemo( setIsBulkActionsLoading, clearSelection, updateBulkActionsState, - } = useBulkActions(bulkActionArgs); + } = useBulkActions({ + ruleTypeIds, + query, + alertsCount: alerts.length, + casesConfig: casesConfiguration, + getBulkActions, + refresh: refreshQueries, + hideBulkActions, + http, + notifications, + application, + casesService, + }); const refresh = useCallback(() => { refreshQueries(); clearSelection(); }, [clearSelection, refreshQueries]); - const toolbarVisibilityArgs = useMemo(() => { - return { - bulkActions, - alertsCount, - rowSelection: bulkActionsState.rowSelection, - alerts, - isLoading, - columnIds: columns.map((column) => column.id), - onToggleColumn, - onResetColumns, - browserFields, - additionalToolbarControls, - setIsBulkActionsLoading, - clearSelection, - refresh, - fieldsBrowserOptions, - alertsQuerySnapshot, - showInspectButton, - toolbarVisibilityProp, - }; - }, [ + const toolbarVisibility = useGetToolbarVisibility({ bulkActions, alertsCount, - bulkActionsState.rowSelection, + rowSelection: bulkActionsState.rowSelection, alerts, isLoading, - columns, + columnIds: columns.map((column) => column.id), onToggleColumn, onResetColumns, browserFields, @@ -404,9 +154,8 @@ export const AlertsDataGrid = typedMemo( alertsQuerySnapshot, showInspectButton, toolbarVisibilityProp, - ]); - - const toolbarVisibility = useGetToolbarVisibility(toolbarVisibilityArgs); + settings, + }); const customActionsColumn: EuiDataGridControlColumn | undefined = useMemo(() => { if (ActionsCell) { @@ -431,24 +180,22 @@ export const AlertsDataGrid = typedMemo( } return ( - + )} + // Though untyped, `_props` contains the correct render context + {...(_props as any)} alert={alert} legacyAlert={legacyAlert} ecsAlert={ecsAlert} setIsActionLoading={setIsActionLoading} /> - + ); }; return { id: 'expandColumn', width: actionsColumnWidth, - headerCellRender: ControlColumnHeaderRenderCell, + headerCellRender: ControlColumnHeaderCell, rowCellRender: RowCellRender, }; } @@ -475,12 +222,14 @@ export const AlertsDataGrid = typedMemo( }, [additionalLeadingControlColumns, isBulkActionsColumnActive, customActionsColumn]); const flyoutRowIndex = flyoutAlertIndex + pageIndex * pageSize; - useEffect(() => { - // Row classes do not deal with visible row indices, so we need to handle page offset - setActiveRowClasses({ + + // Row classes do not deal with visible row indices, so we need to handle page offset + const activeRowClasses = useMemo>( + () => ({ [flyoutRowIndex]: 'alertsTableActiveRow', - }); - }, [flyoutRowIndex]); + }), + [flyoutRowIndex] + ); const handleFlyoutClose = useCallback(() => setFlyoutAlertIndex(-1), [setFlyoutAlertIndex]); @@ -539,7 +288,7 @@ export const AlertsDataGrid = typedMemo( ...DefaultGridStyle, ...propGridStyle, rowClasses: { - // We're spreadind the highlighted row classes first, so that the active + // We're spreading the highlighted row classes first, so that the active // row classed can override the highlighted row classes. ...highlightedRowClasses, ...activeRowClasses, @@ -578,11 +327,11 @@ export const AlertsDataGrid = typedMemo( ({ visibleColumns: _visibleColumns, Cell, headerRow, footerRow }) => ( <> {headerRow} - ), - [ - actualGridStyle, - oldAlertsData, - pageIndex, - pageSize, - isLoadingAlerts, - props.gridStyle?.stripes, - ] + [alerts, actualGridStyle, pageIndex, pageSize, isLoadingAlerts, props.gridStyle?.stripes] ); const sortProps = useMemo(() => { @@ -629,7 +371,7 @@ export const AlertsDataGrid = typedMemo(
{flyoutAlertIndex > -1 && ( - {...renderContext} alert={alerts[flyoutAlertIndex]} alertsCount={alertsCount} @@ -658,7 +400,8 @@ export const AlertsDataGrid = typedMemo( rowCount={alertsCount} renderCustomGridBody={dynamicRowHeight ? renderCustomGridBody : undefined} cellContext={renderContext} - renderCellValue={CellValueHost as RenderCellValue} // CellValue will receive the correct props through `cellContext` + // Cast necessary because the `cellContext` type is too wide in EuiDataGrid + renderCellValue={CellValueHost as RenderCellValue} renderCellPopover={CellPopoverHost} gridStyle={actualGridStyle} sorting={sortProps} @@ -675,6 +418,3 @@ export const AlertsDataGrid = typedMemo( ); (AlertsDataGrid as FC).displayName = 'AlertsDataGrid'; - -// eslint-disable-next-line import/no-default-export -export { AlertsDataGrid as default }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx b/packages/response-ops/alerts_table/components/alerts_flyout.test.tsx similarity index 86% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx rename to packages/response-ops/alerts_table/components/alerts_flyout.test.tsx index 7ce22ab1cad53..cb14ea876358e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx +++ b/packages/response-ops/alerts_table/components/alerts_flyout.test.tsx @@ -1,26 +1,30 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { ComponentProps } from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; import { AlertsFlyout } from './alerts_flyout'; -import { Alert, AlertsField, FlyoutSectionRenderer } from '../../../../types'; +import { AlertsField, FlyoutSectionRenderer } from '../types'; +import { createPartialObjectMock } from '../utils/test'; type FlyoutProps = ComponentProps; const onClose = jest.fn(); const onPaginate = jest.fn(); -const props = { +const props = createPartialObjectMock({ alert: { - [AlertsField.name]: ['one'], - [AlertsField.reason]: ['two'], _id: '0123456789', _index: '.alerts-default', - } as unknown as Alert, + [AlertsField.name]: ['one'], + [AlertsField.reason]: ['two'], + }, tableId: 'test', columns: [ { @@ -47,7 +51,7 @@ const props = { isLoading: false, onClose, onPaginate, -} as unknown as FlyoutProps; +}); describe('AlertsFlyout', () => { afterEach(() => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx b/packages/response-ops/alerts_table/components/alerts_flyout.tsx similarity index 59% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx rename to packages/response-ops/alerts_table/components/alerts_flyout.tsx index 875ad15c6f708..22ad07b249c06 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx +++ b/packages/response-ops/alerts_table/components/alerts_flyout.tsx @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { Suspense, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { @@ -16,8 +19,14 @@ import { EuiProgress, } from '@elastic/eui'; import usePrevious from 'react-use/lib/usePrevious'; -import { DefaultAlertsFlyoutHeader } from './default_alerts_flyout'; -import { FlyoutSectionRenderer } from '../../../../types'; +import type { Alert } from '@kbn/alerting-types'; +import { DefaultAlertsFlyoutBody, DefaultAlertsFlyoutHeader } from './default_alerts_flyout'; +import { + AdditionalContext, + FlyoutSectionProps, + FlyoutSectionRenderer, + RenderContext, +} from '../types'; const PAGINATION_LABEL = i18n.translate( 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel', @@ -26,7 +35,16 @@ const PAGINATION_LABEL = i18n.translate( } ); -export const AlertsFlyout: FlyoutSectionRenderer = ({ alert, ...renderContext }) => { +export const AlertsFlyout = ({ + alert, + ...renderContext +}: RenderContext & { + alert: Alert; + flyoutIndex: number; + isLoading: boolean; + onClose: () => void; + onPaginate: (pageIndex: number) => void; +}) => { const { flyoutIndex, alertsCount, @@ -34,16 +52,18 @@ export const AlertsFlyout: FlyoutSectionRenderer = ({ alert, ...renderContext }) onPaginate, isLoading, renderFlyoutHeader: Header = DefaultAlertsFlyoutHeader, - renderFlyoutBody: Body, - renderFlyoutFooter: Footer, + renderFlyoutBody: Body = DefaultAlertsFlyoutBody, + renderFlyoutFooter, } = renderContext; + const Footer: FlyoutSectionRenderer | undefined = renderFlyoutFooter; const prevAlert = usePrevious(alert); const props = useMemo( - () => ({ - ...renderContext, - // Show the previous alert while loading the next one - alert: alert === undefined && prevAlert != null ? prevAlert : alert, - }), + () => + ({ + ...renderContext, + // Show the previous alert while loading the next one + alert: alert === undefined && prevAlert != null ? prevAlert : alert, + } as FlyoutSectionProps), // eslint-disable-next-line react-hooks/exhaustive-deps [alert, renderContext] ); @@ -52,7 +72,7 @@ export const AlertsFlyout: FlyoutSectionRenderer = ({ alert, ...renderContext }) () => Header ? ( -
+ {...props} /> ) : null, [Header, props] @@ -62,7 +82,7 @@ export const AlertsFlyout: FlyoutSectionRenderer = ({ alert, ...renderContext }) () => Body ? ( - + {...props} /> ) : null, [Body, props] @@ -102,3 +122,8 @@ export const AlertsFlyout: FlyoutSectionRenderer = ({ alert, ...renderContext }) ); }; + +// Lazy loading helpers +// eslint-disable-next-line import/no-default-export +export { AlertsFlyout as default }; +export type AlertsFlyout = typeof AlertsFlyout; diff --git a/packages/response-ops/alerts_table/components/alerts_query_inspector.test.tsx b/packages/response-ops/alerts_table/components/alerts_query_inspector.test.tsx new file mode 100644 index 0000000000000..0405efd1faaa4 --- /dev/null +++ b/packages/response-ops/alerts_table/components/alerts_query_inspector.test.tsx @@ -0,0 +1,43 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen, cleanup } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { AlertsQueryInspector } from './alerts_query_inspector'; +import { AlertsQueryInspectorModal } from './alerts_query_inspector_modal'; + +jest.mock('./alerts_query_inspector_modal'); +jest + .mocked(AlertsQueryInspectorModal.type) + .mockImplementation(() =>
); + +describe('AlertsQueryInspector', () => { + const alertsQuerySnapshot = { + request: [''], + response: [''], + }; + + afterEach(() => { + cleanup(); + }); + + test('open Inspect Modal', async () => { + render( + + ); + await userEvent.click(await screen.findByTestId('inspect-icon-button')); + + expect(await screen.findByTestId('mocked-modal')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.tsx b/packages/response-ops/alerts_table/components/alerts_query_inspector.tsx similarity index 66% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.tsx rename to packages/response-ops/alerts_table/components/alerts_query_inspector.tsx index 99505543ba2e8..c266623c292b9 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.tsx +++ b/packages/response-ops/alerts_table/components/alerts_query_inspector.tsx @@ -1,18 +1,20 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EuiButtonIcon } from '@elastic/eui'; import React, { useState, memo, useCallback } from 'react'; -import { EsQuerySnapshot } from '@kbn/alerts-ui-shared'; +import { EsQuerySnapshot } from '@kbn/alerting-types'; import { HoverVisibilityContainer } from './hover_visibility_container'; -import { ModalInspectQuery } from './modal'; -import * as i18n from './translations'; +import { AlertsQueryInspectorModal } from './alerts_query_inspector_modal'; +import * as i18n from '../translations'; export const BUTTON_CLASS = 'inspectButtonComponent'; const VISIBILITY_CLASSES = [BUTTON_CLASS]; @@ -37,7 +39,7 @@ interface InspectButtonProps { inspectTitle: string; } -const InspectButtonComponent: React.FC = ({ +const AlertsQueryInspectorComponent: React.FC = ({ alertsQuerySnapshot, inspectTitle, }) => { @@ -63,7 +65,7 @@ const InspectButtonComponent: React.FC = ({ onClick={onOpenModal} /> {isShowingModal && ( - = ({ ); }; -InspectButtonComponent.displayName = 'InspectButtonComponent'; -export const InspectButton = React.memo(InspectButtonComponent); +AlertsQueryInspectorComponent.displayName = 'AlertsQueryInspectorComponent'; + +export const AlertsQueryInspector = React.memo(AlertsQueryInspectorComponent); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.test.tsx b/packages/response-ops/alerts_table/components/alerts_query_inspector_modal.test.tsx similarity index 90% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.test.tsx rename to packages/response-ops/alerts_table/components/alerts_query_inspector_modal.test.tsx index 4ec008cb79fd3..eb09539c188d0 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.test.tsx +++ b/packages/response-ops/alerts_table/components/alerts_query_inspector_modal.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; @@ -10,8 +12,8 @@ import { of } from 'rxjs'; import { fireEvent, render, screen } from '@testing-library/react'; import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks'; import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; -import type { ModalInspectProps } from './modal'; -import { ModalInspectQuery } from './modal'; +import type { ModalInspectProps } from './alerts_query_inspector_modal'; +import { AlertsQueryInspectorModal } from './alerts_query_inspector_modal'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -32,7 +34,7 @@ const getRequest = ( const response = '{"took": 880,"timed_out": false,"_shards": {"total": 26,"successful": 26,"skipped": 0,"failed": 0},"hits": {"max_score": null,"hits": []},"aggregations": {"hosts": {"value": 541},"hosts_histogram": {"buckets": [{"key_as_string": "2019 - 07 - 05T01: 00: 00.000Z", "key": 1562288400000, "doc_count": 1492321, "count": { "value": 105 }}, {"key_as_string": "2019 - 07 - 05T13: 00: 00.000Z", "key": 1562331600000, "doc_count": 2412761, "count": { "value": 453}},{"key_as_string": "2019 - 07 - 06T01: 00: 00.000Z", "key": 1562374800000, "doc_count": 111658, "count": { "value": 15}}],"interval": "12h"}},"status": 200}'; -describe('Modal Inspect', () => { +describe('AlertsQueryInspectorModal', () => { const closeModal = jest.fn(); const defaultProps: ModalInspectProps = { closeModal, @@ -46,7 +48,7 @@ describe('Modal Inspect', () => { const renderModalInspectQuery = () => { const theme = { theme$: of({ darkMode: false, name: 'amsterdam' }) }; const userProfile = userProfileServiceMock.createStart(); - return render(, { + return render(, { wrapper: ({ children }) => ( {children} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.tsx b/packages/response-ops/alerts_table/components/alerts_query_inspector_modal.tsx similarity index 81% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.tsx rename to packages/response-ops/alerts_table/components/alerts_query_inspector_modal.tsx index 2207a872472d7..d013b7205b2a9 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/modal.tsx +++ b/packages/response-ops/alerts_table/components/alerts_query_inspector_modal.tsx @@ -1,10 +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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ +import React, { type ReactNode } from 'react'; import { EuiButton, EuiCodeBlock, @@ -19,13 +22,10 @@ import { EuiTabbedContent, } from '@elastic/eui'; import numeral from '@elastic/numeral'; -import { ReactNode } from 'react'; -import React from 'react'; - -import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { isEmpty } from 'lodash'; -import { EsQuerySnapshot } from '@kbn/alerts-ui-shared'; -import * as i18n from './translations'; +import type { EsQuerySnapshot } from '@kbn/alerting-types'; +import { css } from '@emotion/react'; +import * as i18n from '../translations'; export interface ModalInspectProps { closeModal: () => void; @@ -48,19 +48,6 @@ interface Response { aggregations: Record; } -const MyEuiModal = euiStyled(EuiModal)` - width: min(768px, calc(100vw - 16px)); - min-height: 41vh; - .euiModal__flex { - width: 60vw; - } - .euiCodeBlock { - height: auto !important; - max-width: 718px; - } -`; - -MyEuiModal.displayName = 'MyEuiModal'; const parse = function (str: string): T { try { return JSON.parse(str); @@ -77,7 +64,7 @@ const stringify = (object: Request | Response): string => { } }; -const ModalInspectQueryComponent = ({ +const AlertsQueryInspectorModalComponent = ({ closeModal, alertsQuerySnapshot, title, @@ -192,7 +179,21 @@ const ModalInspectQueryComponent = ({ ]; return ( - + {i18n.INSPECT} {title} @@ -208,8 +209,8 @@ const ModalInspectQueryComponent = ({ {i18n.CLOSE} - + ); }; -export const ModalInspectQuery = React.memo(ModalInspectQueryComponent); +export const AlertsQueryInspectorModal = React.memo(AlertsQueryInspectorModalComponent); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx b/packages/response-ops/alerts_table/components/alerts_table.test.tsx similarity index 80% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx rename to packages/response-ops/alerts_table/components/alerts_table.test.tsx index 105de06ab8d7e..1d12042f13128 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx +++ b/packages/response-ops/alerts_table/components/alerts_table.test.tsx @@ -1,48 +1,50 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { FunctionComponent } from 'react'; import { BehaviorSubject } from 'rxjs'; import userEvent from '@testing-library/user-event'; import { get } from 'lodash'; import { render, waitFor, screen, act } from '@testing-library/react'; import { ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS, ALERT_UUID } from '@kbn/rule-data-utils'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; - +import type { Alert, LegacyField } from '@kbn/alerting-types'; +import { settingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { fetchAlertsFields } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'; +import { searchAlerts } from '@kbn/alerts-ui-shared/src/common/apis/search_alerts/search_alerts'; +import { testQueryClientConfig } from '@kbn/alerts-ui-shared/src/common/test_utils/test_query_client_config'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { getMutedAlertsInstancesByRule } from '@kbn/response-ops-alerts-apis/apis/get_muted_alerts_instances_by_rule'; +import { applicationServiceMock, notificationServiceMock } from '@kbn/core/public/mocks'; +import { afterAll } from '@elastic/synthetics'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { - Alerts, - AlertsField, AlertsDataGridProps, - FetchAlertData, AlertsTableProps, AdditionalContext, - Alert, RenderContext, -} from '../../../types'; -import { PLUGIN_ID } from '../../../common/constants'; + AlertsField, +} from '../types'; import { AlertsTable } from './alerts_table'; import { AlertsDataGrid } from './alerts_data_grid'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { getCasesMock } from './cases/index.mock'; -import { createCasesServiceMock } from './index.mock'; -import { getMaintenanceWindowsMock } from './maintenance_windows/index.mock'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { fetchAlertsFields } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'; -import { searchAlerts } from '@kbn/alerts-ui-shared/src/common/apis/search_alerts/search_alerts'; -import { bulkGetCases } from './hooks/apis/bulk_get_cases'; -import { getRulesWithMutedAlerts } from './hooks/apis/get_rules_with_muted_alerts'; -import { bulkGetMaintenanceWindows } from './hooks/apis/bulk_get_maintenance_windows'; -import { testQueryClientConfig } from '@kbn/alerts-ui-shared/src/common/test_utils/test_query_client_config'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import { useLicense } from '../../hooks/use_license'; +import { getCasesMock, createCasesServiceMock } from '../mocks/cases.mock'; +import { getMaintenanceWindowsMock } from '../mocks/maintenance_windows.mock'; +import { bulkGetCases } from '../apis/bulk_get_cases'; +import { bulkGetMaintenanceWindows } from '../apis/bulk_get_maintenance_windows'; +import { useLicense } from '../hooks/use_license'; +import { getJsDomPerformanceFix } from '../utils/test'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; type BaseAlertsTableProps = AlertsTableProps; -jest.mock('@kbn/kibana-utils-plugin/public'); - // Search alerts mock jest.mock('@kbn/alerts-ui-shared/src/common/apis/search_alerts/search_alerts'); const mockSearchAlerts = jest.mocked(searchAlerts); @@ -64,8 +66,10 @@ const columns = [ displayAsText: 'Maintenance Windows', }, ]; -const alerts = [ +const alerts: Alert[] = [ { + _id: 'test-1', + _index: 'alerts', [AlertsField.name]: ['one'], [AlertsField.reason]: ['two'], [AlertsField.uuid]: ['1047d115-670d-469e-af7a-86fdd2b2f814'], @@ -74,6 +78,8 @@ const alerts = [ [ALERT_MAINTENANCE_WINDOW_IDS]: ['test-mw-id-1'], }, { + _id: 'test-2', + _index: 'alerts', [AlertsField.name]: ['three'], [AlertsField.reason]: ['four'], [AlertsField.uuid]: ['bf5f6d63-5afd-48e0-baf6-f28c2b68db46'], @@ -81,13 +87,15 @@ const alerts = [ [ALERT_MAINTENANCE_WINDOW_IDS]: ['test-mw-id-2'], }, { + _id: 'test-3', + _index: 'alerts', [AlertsField.name]: ['five'], [AlertsField.reason]: ['six'], [AlertsField.uuid]: ['1047d115-5afd-469e-baf6-f28c2b68db46'], [ALERT_CASE_IDS]: [], [ALERT_MAINTENANCE_WINDOW_IDS]: [], }, -] as unknown as Alerts; +]; const oldAlertsData = [ [ { @@ -119,7 +127,7 @@ const oldAlertsData = [ value: ['six'], }, ], -] as FetchAlertData['oldAlertsData']; +] as LegacyField[][]; const ecsAlertsData = [ [ { @@ -166,7 +174,7 @@ const ecsAlertsData = [ }, }, ], -] as FetchAlertData['ecsAlertsData']; +]; const mockSearchAlertsResponse: Awaited> = { alerts, ecsAlertsData, @@ -174,7 +182,6 @@ const mockSearchAlertsResponse: Awaited> = { total: alerts.length, querySnapshot: { request: [], response: [] }, }; -mockSearchAlerts.mockResolvedValue(mockSearchAlertsResponse); // Alerts fields mock jest.mock('@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'); @@ -201,20 +208,20 @@ jest.mocked(fetchAlertsFields).mockResolvedValue({ }); // Muted alerts mock -jest.mock('./hooks/apis/get_rules_with_muted_alerts'); -jest.mocked(getRulesWithMutedAlerts).mockResolvedValue({ +jest.mock('@kbn/response-ops-alerts-apis/apis/get_muted_alerts_instances_by_rule'); +jest.mocked(getMutedAlertsInstancesByRule).mockResolvedValue({ data: [], }); // Cases mock -jest.mock('./hooks/apis/bulk_get_cases'); +jest.mock('../apis/bulk_get_cases'); const mockBulkGetCases = jest.mocked(bulkGetCases); const mockCases = getCasesMock(); mockBulkGetCases.mockResolvedValue({ cases: mockCases, errors: [] }); // Maintenance windows mock -jest.mock('./hooks/apis/bulk_get_maintenance_windows'); -jest.mock('../../hooks/use_license'); +jest.mock('../apis/bulk_get_maintenance_windows'); +jest.mock('../hooks/use_license'); const mockBulkGetMaintenanceWindows = jest.mocked(bulkGetMaintenanceWindows); jest.mocked(useLicense).mockReturnValue({ isAtLeastPlatinum: () => true }); const mockMaintenanceWindows = getMaintenanceWindowsMock(); @@ -229,80 +236,18 @@ jest.mock('./alerts_data_grid', () => ({ })); const mockAlertsDataGrid = jest.mocked(AlertsDataGrid); -// useKibana mock +const applicationMock = applicationServiceMock.createStartContract(); const mockCurrentAppId$ = new BehaviorSubject('testAppId'); const mockCaseService = createCasesServiceMock(); -const mockHttpService = httpServiceMock.createStartContract(); -jest.mock('../../../common/lib/kibana/kibana_react', () => ({ - useKibana: () => ({ - services: { - application: { - getUrlForApp: jest.fn(() => ''), - capabilities: { - cases: { - create_cases: true, - read_cases: true, - update_cases: true, - delete_cases: true, - push_cases: true, - }, - maintenanceWindow: { - show: true, - }, - }, - currentAppId$: mockCurrentAppId$, - }, - cases: mockCaseService, - notifications: { - toasts: { - addDanger: () => {}, - }, - }, - data: {}, - http: mockHttpService, - }, - }), -})); -// Storage mock -const mockStorage = Storage as jest.Mock; -mockStorage.mockImplementation(() => { - return { get: jest.fn(), set: jest.fn() }; -}); +const { fix, cleanup } = getJsDomPerformanceFix(); -const originalGetComputedStyle = Object.assign({}, window.getComputedStyle); beforeAll(() => { - // The JSDOM implementation is too slow - // Especially for dropdowns that try to position themselves - // perf issue - https://github.com/jsdom/jsdom/issues/3234 - Object.defineProperty(window, 'getComputedStyle', { - value: (el: HTMLElement) => { - /** - * This is based on the jsdom implementation of getComputedStyle - * https://github.com/jsdom/jsdom/blob/9dae17bf0ad09042cfccd82e6a9d06d3a615d9f4/lib/jsdom/browser/Window.js#L779-L820 - * - * It is missing global style parsing and will only return styles applied directly to an element. - * Will not return styles that are global or from emotion - */ - const declaration = new CSSStyleDeclaration(); - const { style } = el; - - Array.prototype.forEach.call(style, (property: string) => { - declaration.setProperty( - property, - style.getPropertyValue(property), - style.getPropertyPriority(property) - ); - }); - - return declaration; - }, - configurable: true, - writable: true, - }); + fix(); }); + afterAll(() => { - Object.defineProperty(window, 'getComputedStyle', originalGetComputedStyle); + cleanup(); }); const queryClient = new QueryClient(testQueryClientConfig); @@ -315,8 +260,17 @@ const TestComponent: FunctionComponent = (props) => ( ); describe('AlertsTable', () => { + // Storage mock + const mockStorageGet = jest.fn(); + jest.mock('../utils/storage', () => ({ + Storage: jest.fn().mockReturnValue({ + get: mockStorageGet, + set: jest.fn(), + }), + })); + const tableProps: BaseAlertsTableProps = { - id: PLUGIN_ID, + id: 'test-alerts-table', ruleTypeIds: ['logs'], query: {}, columns, @@ -340,6 +294,32 @@ describe('AlertsTable', () => { ))} ), + services: { + http: httpServiceMock.createStartContract(), + application: { + ...applicationMock, + getUrlForApp: jest.fn(() => ''), + capabilities: { + ...applicationMock.capabilities, + cases: { + create_cases: true, + read_cases: true, + update_cases: true, + delete_cases: true, + push_cases: true, + }, + maintenanceWindow: { + show: true, + }, + }, + currentAppId$: mockCurrentAppId$, + }, + data: dataPluginMock.createStartContract(), + fieldFormats: fieldFormatsMock, + licensing: licensingMock.createStart(), + notifications: notificationServiceMock.createStartContract(), + settings: settingsServiceMock.createStartContract(), + }, }; let onChangePageIndex: AlertsDataGridProps['onChangePageIndex']; @@ -354,9 +334,18 @@ describe('AlertsTable', () => { beforeEach(() => { jest.clearAllMocks(); + mockSearchAlerts.mockResolvedValue(mockSearchAlertsResponse); }); describe('Cases', () => { + const casesTableProps = { + ...tableProps, + services: { + ...tableProps.services, + cases: mockCaseService, + }, + }; + beforeEach(() => { jest.clearAllMocks(); mockCaseService.helpers.canUseCases = jest.fn().mockReturnValue({ create: true, read: true }); @@ -367,12 +356,12 @@ describe('AlertsTable', () => { }); it('should show the cases column', async () => { - render(); + render(); expect(await screen.findByText('Cases')).toBeInTheDocument(); }); it('should show the cases titles correctly', async () => { - render(); + render(); expect(await screen.findByText('Test case')).toBeInTheDocument(); expect(await screen.findByText('Test case 2')).toBeInTheDocument(); }); @@ -380,12 +369,12 @@ describe('AlertsTable', () => { it('should show the loading skeleton when fetching cases', async () => { mockBulkGetCases.mockResolvedValue({ cases: mockCases, errors: [] }); - render(); + render(); expect((await screen.findAllByTestId('cases-cell-loading')).length).toBe(3); }); it('should pass the correct case ids to useBulkGetCases', async () => { - render(); + render(); await waitFor(() => { expect(mockBulkGetCases).toHaveBeenCalledWith( @@ -402,7 +391,7 @@ describe('AlertsTable', () => { alerts: [...mockSearchAlertsResponse.alerts, ...mockSearchAlertsResponse.alerts], }); - render(); + render(); await waitFor(() => { expect(mockBulkGetCases).toHaveBeenCalledWith( @@ -420,12 +409,12 @@ describe('AlertsTable', () => { { ...mockSearchAlertsResponse.alerts[0], 'kibana.alert.case_ids': [], - } as unknown as Alert, + }, mockSearchAlertsResponse.alerts[1], ], }); - render(); + render(); await waitFor(() => { expect(mockBulkGetCases).toHaveBeenCalledWith( @@ -441,7 +430,7 @@ describe('AlertsTable', () => { .fn() .mockReturnValue({ create: false, read: false }); - render(); + render(); await waitFor(() => { expect(mockBulkGetCases).not.toHaveBeenCalled(); @@ -452,8 +441,8 @@ describe('AlertsTable', () => { mockCaseService.helpers.canUseCases = jest.fn().mockReturnValue({ create: true, read: true }); const props: BaseAlertsTableProps = { - ...tableProps, - casesConfiguration: { featureId: 'test-feature-id', owner: ['test-owner'] }, + ...casesTableProps, + casesConfiguration: { featureId: 'test-feature-id', owner: ['cases'] }, }; render( @@ -473,24 +462,24 @@ describe('AlertsTable', () => { }); it('calls canUseCases with an empty array if the case configuration is not defined', async () => { - render(); + render(); expect(mockCaseService.helpers.canUseCases).toHaveBeenCalledWith([]); }); it('calls canUseCases with the case owner if defined', async () => { const props: BaseAlertsTableProps = { - ...tableProps, - casesConfiguration: { featureId: 'test-feature-id', owner: ['test-owner'] }, + ...casesTableProps, + casesConfiguration: { featureId: 'test-feature-id', owner: ['cases'] }, }; render(); - expect(mockCaseService.helpers.canUseCases).toHaveBeenCalledWith(['test-owner']); + expect(mockCaseService.helpers.canUseCases).toHaveBeenCalledWith(['cases']); }); it('should call the cases context with the correct props', async () => { const props: BaseAlertsTableProps = { - ...tableProps, - casesConfiguration: { featureId: 'test-feature-id', owner: ['test-owner'] }, + ...casesTableProps, + casesConfiguration: { featureId: 'test-feature-id', owner: ['cases'] }, }; const CasesContextMock = jest.fn().mockReturnValue(null); @@ -501,7 +490,7 @@ describe('AlertsTable', () => { expect(CasesContextMock).toHaveBeenCalledWith( { children: expect.anything(), - owner: ['test-owner'], + owner: ['cases'], permissions: { create: true, read: true }, features: { alerts: { sync: false } }, }, @@ -513,7 +502,7 @@ describe('AlertsTable', () => { const CasesContextMock = jest.fn().mockReturnValue(null); mockCaseService.ui.getCasesContext = jest.fn().mockReturnValue(CasesContextMock); - render(); + render(); expect(CasesContextMock).toHaveBeenCalledWith( { children: expect.anything(), @@ -532,7 +521,7 @@ describe('AlertsTable', () => { .fn() .mockReturnValue({ create: false, read: false }); - render(); + render(); expect(CasesContextMock).toHaveBeenCalledWith( { children: expect.anything(), @@ -546,10 +535,10 @@ describe('AlertsTable', () => { it('should call the cases context with sync alerts turned on if defined in the cases config', async () => { const props: BaseAlertsTableProps = { - ...tableProps, + ...casesTableProps, casesConfiguration: { featureId: 'test-feature-id', - owner: ['test-owner'], + owner: ['cases'], syncAlerts: true, }, }; @@ -561,7 +550,7 @@ describe('AlertsTable', () => { expect(CasesContextMock).toHaveBeenCalledWith( { children: expect.anything(), - owner: ['test-owner'], + owner: ['cases'], permissions: { create: true, read: true }, features: { alerts: { sync: true } }, }, @@ -620,7 +609,7 @@ describe('AlertsTable', () => { { ...mockSearchAlertsResponse.alerts[0], 'kibana.alert.maintenance_window_ids': [], - } as unknown as Alert, + }, mockSearchAlertsResponse.alerts[1], ], }); @@ -668,7 +657,7 @@ describe('AlertsTable', () => { describe('flyout', () => { it('should show a flyout when selecting an alert', async () => { const wrapper = render(); - userEvent.click(wrapper.queryAllByTestId('expandColumnCellOpenFlyoutButton-0')[0]!); + await userEvent.click(wrapper.queryAllByTestId('expandColumnCellOpenFlyoutButton-0')[0]!); const result = await wrapper.findAllByTestId('alertsFlyout'); expect(result.length).toBe(1); @@ -677,11 +666,11 @@ describe('AlertsTable', () => { expect(wrapper.queryByTestId('alertsFlyoutReason')?.textContent).toBe('two'); // Should paginate too - userEvent.click(wrapper.queryAllByTestId('pagination-button-next')[0]); + await userEvent.click(wrapper.queryAllByTestId('pagination-button-next')[0]); expect(wrapper.queryByTestId('alertsFlyoutName')?.textContent).toBe('three'); expect(wrapper.queryByTestId('alertsFlyoutReason')?.textContent).toBe('four'); - userEvent.click(wrapper.queryAllByTestId('pagination-button-previous')[0]); + await userEvent.click(wrapper.queryAllByTestId('pagination-button-previous')[0]); expect(wrapper.queryByTestId('alertsFlyoutName')?.textContent).toBe('one'); expect(wrapper.queryByTestId('alertsFlyoutReason')?.textContent).toBe('two'); }); @@ -696,13 +685,13 @@ describe('AlertsTable', () => { /> ); - userEvent.click(await screen.findByTestId('expandColumnCellOpenFlyoutButton-0')); + await userEvent.click(await screen.findByTestId('expandColumnCellOpenFlyoutButton-0')); const result = await screen.findAllByTestId('alertsFlyout'); expect(result.length).toBe(1); mockSearchAlerts.mockClear(); - userEvent.click(await screen.findByTestId('pagination-button-next')); + await userEvent.click(await screen.findByTestId('pagination-button-next')); expect(mockSearchAlerts).toHaveBeenCalledWith( expect.objectContaining({ pageIndex: 1, @@ -711,7 +700,7 @@ describe('AlertsTable', () => { ); mockSearchAlerts.mockClear(); - userEvent.click(await screen.findByTestId('pagination-button-previous')); + await userEvent.click(await screen.findByTestId('pagination-button-previous')); expect(mockSearchAlerts).toHaveBeenCalledWith( expect.objectContaining({ pageIndex: 0, @@ -730,13 +719,17 @@ describe('AlertsTable', () => { /> ); - userEvent.click((await screen.findAllByTestId('expandColumnCellOpenFlyoutButton-0'))[0]); + await userEvent.click( + ( + await screen.findAllByTestId('expandColumnCellOpenFlyoutButton-0') + )[0] + ); const result = await screen.findAllByTestId('alertsFlyout'); expect(result.length).toBe(1); mockSearchAlerts.mockClear(); - userEvent.click(await screen.findByTestId('pagination-button-last')); + await userEvent.click(await screen.findByTestId('pagination-button-last')); expect(mockSearchAlerts).toHaveBeenCalledWith( expect.objectContaining({ pageIndex: 1, @@ -745,7 +738,7 @@ describe('AlertsTable', () => { ); mockSearchAlerts.mockClear(); - userEvent.click(await screen.findByTestId('pagination-button-previous')); + await userEvent.click(await screen.findByTestId('pagination-button-previous')); expect(mockSearchAlerts).toHaveBeenCalledWith( expect.objectContaining({ pageIndex: 0, @@ -774,10 +767,10 @@ describe('AlertsTable', () => { render(); expect(screen.queryByTestId(`dataGridHeaderCell-${AlertsField.name}`)).not.toBe(null); - userEvent.click(await screen.findByTestId('show-field-browser')); + await userEvent.click(await screen.findByTestId('show-field-browser')); const fieldCheckbox = screen.getByTestId(`field-${AlertsField.name}-checkbox`); - userEvent.click(fieldCheckbox); - userEvent.click(screen.getByTestId('close')); + await userEvent.click(fieldCheckbox); + await userEvent.click(screen.getByTestId('close')); await waitFor(() => { expect(screen.queryByTestId(`dataGridHeaderCell-${AlertsField.name}`)).toBe(null); @@ -785,31 +778,26 @@ describe('AlertsTable', () => { }); it('should restore a default element that has been removed previously', async () => { - mockStorage.mockClear(); - mockStorage.mockImplementation(() => ({ - get: () => { - return { - columns: [{ displayAsText: 'Reason', id: AlertsField.reason, schema: undefined }], - sort: [ - { - [AlertsField.reason]: { - order: 'asc', - }, - }, - ], - visibleColumns: [AlertsField.reason], - }; - }, - set: jest.fn(), - })); + mockStorageGet.mockClear(); + mockStorageGet.mockReturnValue({ + columns: [{ displayAsText: 'Reason', id: AlertsField.reason, schema: undefined }], + sort: [ + { + [AlertsField.reason]: { + order: 'asc', + }, + }, + ], + visibleColumns: [AlertsField.reason], + }); render(); expect(screen.queryByTestId(`dataGridHeaderCell-${AlertsField.name}`)).toBe(null); - userEvent.click(await screen.findByTestId('show-field-browser')); + await userEvent.click(await screen.findByTestId('show-field-browser')); const fieldCheckbox = screen.getByTestId(`field-${AlertsField.name}-checkbox`); - userEvent.click(fieldCheckbox); - userEvent.click(screen.getByTestId('close')); + await userEvent.click(fieldCheckbox); + await userEvent.click(screen.getByTestId('close')); await waitFor(() => { expect(screen.queryByTestId(`dataGridHeaderCell-${AlertsField.name}`)).not.toBe(null); @@ -826,10 +814,10 @@ describe('AlertsTable', () => { const { getByTestId, queryByTestId } = render(); expect(queryByTestId(`dataGridHeaderCell-${AlertsField.uuid}`)).toBe(null); - userEvent.click(getByTestId('show-field-browser')); + await userEvent.click(getByTestId('show-field-browser')); const fieldCheckbox = getByTestId(`field-${AlertsField.uuid}-checkbox`); - userEvent.click(fieldCheckbox); - userEvent.click(getByTestId('close')); + await userEvent.click(fieldCheckbox); + await userEvent.click(getByTestId('close')); await waitFor(() => { expect(queryByTestId(`dataGridHeaderCell-${AlertsField.uuid}`)).not.toBe(null); @@ -898,7 +886,7 @@ describe('AlertsTable', () => { }); }); - describe('Client provided toolbar visiblity options', () => { + describe('Client provided toolbar visibility options', () => { it('hide column order control', () => { const props: BaseAlertsTableProps = { ...tableProps, @@ -925,9 +913,11 @@ describe('AlertsTable', () => { it('resets the page index when any query parameter changes', () => { mockSearchAlerts.mockResolvedValue({ ...mockSearchAlertsResponse, - alerts: Array.from({ length: 100 }).map( - (_, i) => ({ [AlertsField.uuid]: `alert-${i}` } as unknown as Alert) - ), + alerts: Array.from({ length: 100 }).map((_, i) => ({ + _id: `${i}`, + _index: 'alerts', + [AlertsField.uuid]: [`alert-${i}`], + })), }); const { rerender } = render(); act(() => { @@ -945,9 +935,11 @@ describe('AlertsTable', () => { it('resets the page index when refetching alerts', () => { mockSearchAlerts.mockResolvedValue({ ...mockSearchAlertsResponse, - alerts: Array.from({ length: 100 }).map( - (_, i) => ({ [AlertsField.uuid]: `alert-${i}` } as unknown as Alert) - ), + alerts: Array.from({ length: 100 }).map((_, i) => ({ + _id: `${i}`, + _index: 'alerts', + [AlertsField.uuid]: [`alert-${i}`], + })), }); render(); act(() => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/packages/response-ops/alerts_table/components/alerts_table.tsx similarity index 78% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx rename to packages/response-ops/alerts_table/components/alerts_table.tsx index 5513c4a407fdc..eaec51b9e8139 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx +++ b/packages/response-ops/alerts_table/components/alerts_table.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { @@ -23,10 +25,6 @@ import { EuiDataGridColumn, EuiProgress, EuiDataGridSorting, - EuiEmptyPrompt, - EuiButton, - EuiCode, - EuiCopy, EuiDataGridControlColumn, EuiDataGridRefProps, } from '@elastic/eui'; @@ -37,46 +35,40 @@ import { ALERT_UUID, } from '@kbn/rule-data-utils'; import type { RuleRegistrySearchRequestPagination } from '@kbn/rule-registry-plugin/common'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import type { SortCombinations } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; import { QueryClientProvider, useQueryClient } from '@tanstack/react-query'; import { useSearchAlertsQuery } from '@kbn/alerts-ui-shared/src/common/hooks/use_search_alerts_query'; import { DEFAULT_ALERTS_PAGE_SIZE } from '@kbn/alerts-ui-shared/src/common/constants'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; import deepEqual from 'fast-deep-equal'; -import { typedForwardRef } from '../../../../common/utils'; -import { useKibana } from '../../../common/lib/kibana'; -import { useGetMutedAlertsQuery } from './hooks/alert_mute/use_get_muted_alerts'; +import { Alert } from '@kbn/alerting-types'; +import { useGetMutedAlertsQuery } from '@kbn/response-ops-alerts-apis/hooks/use_get_muted_alerts_query'; +import { queryKeys as alertsQueryKeys } from '@kbn/response-ops-alerts-apis/constants'; +import { ErrorFallback } from './error_fallback'; +import { defaultAlertsTableColumns } from '../configuration'; +import { Storage } from '../utils/storage'; +import { queryKeys } from '../constants'; import { AlertsDataGrid } from './alerts_data_grid'; import { EmptyState } from './empty_state'; +import { RenderContext, RowSelectionState } from '../types'; import { AdditionalContext, - Alert, - Alerts, AlertsDataGridProps, AlertsTableImperativeApi, AlertsTableProps, - RenderContext, - RowSelectionState, -} from '../../../types'; -import { - ALERTS_TABLE_UNKNOWN_ERROR_TITLE, - ALERTS_TABLE_UNKNOWN_ERROR_MESSAGE, - ALERTS_TABLE_UNKNOWN_ERROR_COPY_TO_CLIPBOARD_LABEL, -} from './translations'; -import { bulkActionsReducer } from './bulk_actions/reducer'; -import { useColumns } from './hooks/use_columns'; -import { InspectButtonContainer } from './toolbar/components/inspect'; -import { alertsTableQueryClient } from './query_client'; -import { useBulkGetCasesQuery } from './hooks/use_bulk_get_cases'; -import { useBulkGetMaintenanceWindowsQuery } from './hooks/use_bulk_get_maintenance_windows'; -import { CasesService } from './types'; -import { AlertsTableContextProvider } from './contexts/alerts_table_context'; -import { ErrorBoundary, FallbackComponent } from '../common/components/error_boundary'; -import { usePagination } from './hooks/use_pagination'; -import { triggersActionsUiQueriesKeys } from '../../hooks/constants'; - -export interface AlertsTableStorage { +} from '../types'; +import { bulkActionsReducer } from '../reducers/bulk_actions_reducer'; +import { useColumns } from '../hooks/use_columns'; +import { InspectButtonContainer } from './alerts_query_inspector'; +import { alertsTableQueryClient } from '../query_client'; +import { useBulkGetCasesQuery } from '../hooks/use_bulk_get_cases'; +import { useBulkGetMaintenanceWindowsQuery } from '../hooks/use_bulk_get_maintenance_windows'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; +import { ErrorBoundary } from './error_boundary'; +import { usePagination } from '../hooks/use_pagination'; +import { typedForwardRef } from '../utils/react'; + +export interface AlertsTablePersistedConfiguration { columns: EuiDataGridColumn[]; visibleColumns?: string[]; sort: SortCombinations[]; @@ -86,30 +78,30 @@ type AlertWithCaseIds = Alert & Required>; type AlertWithMaintenanceWindowIds = Alert & Required>; -const getCaseIdsFromAlerts = (alerts: Alerts) => [ +const getCaseIdsFromAlerts = (alerts: Alert[]) => [ ...new Set( alerts .filter((alert): alert is AlertWithCaseIds => { const caseIds = alert[ALERT_CASE_IDS]; return caseIds != null && caseIds.length > 0; }) - .map((alert) => alert[ALERT_CASE_IDS]) + .map((alert) => alert[ALERT_CASE_IDS] as string[]) .flat() ), ]; -const getRuleIdsFromAlerts = (alerts: Alerts) => [ - ...new Set(alerts.map((a) => a[ALERT_RULE_UUID]![0])), +const getRuleIdsFromAlerts = (alerts: Alert[]) => [ + ...new Set(alerts.map((a) => a[ALERT_RULE_UUID]![0] as string)), ]; -const getMaintenanceWindowIdsFromAlerts = (alerts: Alerts) => [ +const getMaintenanceWindowIdsFromAlerts = (alerts: Alert[]) => [ ...new Set( alerts .filter((alert): alert is AlertWithMaintenanceWindowIds => { const maintenanceWindowIds = alert[ALERT_MAINTENANCE_WINDOW_IDS]; return maintenanceWindowIds != null && maintenanceWindowIds.length > 0; }) - .map((alert) => alert[ALERT_MAINTENANCE_WINDOW_IDS]) + .map((alert) => alert[ALERT_MAINTENANCE_WINDOW_IDS] as string[]) .flat() ), ]; @@ -130,41 +122,38 @@ const initialBulkActionsState = { updatedAt: Date.now(), }; -const ErrorBoundaryFallback: FallbackComponent = ({ error }) => { - return ( - {ALERTS_TABLE_UNKNOWN_ERROR_TITLE}} - body={ - <> -

{ALERTS_TABLE_UNKNOWN_ERROR_MESSAGE}

- {error.message && {error.message}} - - } - actions={ - - {(copy) => ( - - {ALERTS_TABLE_UNKNOWN_ERROR_COPY_TO_CLIPBOARD_LABEL} - - )} - - } - /> - ); -}; - +/** + * An `EuiDataGrid` abstraction to render alert documents + * + * It manages the paginated and cached fetching of alerts based on the + * provided `featureIds` (the final query can be refined through the + * `query` and `initialSort` props). The `id` prop is required in order + * to persist the table state in `localStorage` + * + * @example + * ```tsx + * + * ``` + */ export const AlertsTable = memo( forwardRef((props, ref) => { return ( - + ); }) + // Type cast to avoid losing the generic type ) as typeof AlertsTableContent; (AlertsTable as FC).displayName = 'AlertsTable'; @@ -185,7 +174,7 @@ const AlertsTableContent = typedForwardRef( leadingControlColumns = DEFAULT_LEADING_CONTROL_COLUMNS, trailingControlColumns, rowHeightsOptions, - columns: initialColumns, + columns: initialColumns = defaultAlertsTableColumns, gridStyle, browserFields: propBrowserFields, onUpdate, @@ -205,22 +194,22 @@ const AlertsTableContent = typedForwardRef( renderFlyoutFooter, renderAdditionalToolbarControls: AdditionalToolbarControlsComponent, lastReloadRequestTime, + services, ...publicDataGridProps }: AlertsTableProps, ref: Ref ) => { + // Memoized so that consumers can pass an inline object without causing re-renders + // eslint-disable-next-line react-hooks/exhaustive-deps + const memoizedServices = useMemo(() => services, Object.values(services)); const { casesConfiguration, showInspectButton } = publicDataGridProps; - const { - data, - cases: casesService, - fieldFormats, - } = useKibana().services as ReturnType['services'] & { - cases?: CasesService; - }; + const { data, cases: casesService, http, notifications, application, licensing } = services; const queryClient = useQueryClient({ context: AlertsQueryContext }); const storage = useRef(new Storage(window.localStorage)); const dataGridRef = useRef(null); - const localStorageAlertsTableConfig = storage.current.get(id) as Partial; + const localStorageAlertsTableConfig = storage.current.get( + id + ) as Partial; const columnsLocal = useMemo( () => @@ -244,7 +233,7 @@ const AlertsTableContent = typedForwardRef( }), [columnsLocal, localStorageAlertsTableConfig, initialSort] ); - const storageAlertsTable = useRef(getStorageConfig()); + const storageAlertsTable = useRef(getStorageConfig()); storageAlertsTable.current = getStorageConfig(); @@ -275,6 +264,7 @@ const AlertsTableContent = typedForwardRef( id, defaultColumns: initialColumns ?? DEFAULT_COLUMNS, alertsFields: propBrowserFields, + http, }); const [queryParams, setQueryParams] = useState({ @@ -337,6 +327,8 @@ const AlertsTableContent = typedForwardRef( const ruleIds = useMemo(() => getRuleIdsFromAlerts(alerts), [alerts]); const mutedAlertsQuery = useGetMutedAlertsQuery({ ruleIds, + http, + notifications, }); const caseIds = useMemo(() => getCaseIdsFromAlerts(alerts), [alerts]); @@ -344,7 +336,7 @@ const AlertsTableContent = typedForwardRef( return casesService?.helpers.canUseCases(casesConfiguration?.owner ?? []); }, [casesConfiguration?.owner, casesService?.helpers]); const casesQuery = useBulkGetCasesQuery( - { caseIds }, + { caseIds, http, notifications }, { enabled: isCasesColumnEnabled(columns) && !!casesPermissions?.read, } @@ -352,7 +344,7 @@ const AlertsTableContent = typedForwardRef( const maintenanceWindowIds = useMemo(() => getMaintenanceWindowIdsFromAlerts(alerts), [alerts]); const maintenanceWindowsQuery = useBulkGetMaintenanceWindowsQuery( - { ids: maintenanceWindowIds }, + { ids: maintenanceWindowIds, http, application, notifications, licensing }, { enabled: isMaintenanceWindowColumnEnabled(columns), context: AlertsQueryContext } ); @@ -363,11 +355,9 @@ const AlertsTableContent = typedForwardRef( } else { refetchAlerts(); } - queryClient.invalidateQueries(triggersActionsUiQueriesKeys.casesBulkGet(caseIds)); - queryClient.invalidateQueries(triggersActionsUiQueriesKeys.mutedAlerts(ruleIds)); - queryClient.invalidateQueries( - triggersActionsUiQueriesKeys.maintenanceWindowsBulkGet(maintenanceWindowIds) - ); + queryClient.invalidateQueries(queryKeys.casesBulkGet(caseIds)); + queryClient.invalidateQueries(alertsQueryKeys.mutedAlerts(ruleIds)); + queryClient.invalidateQueries(queryKeys.maintenanceWindowsBulkGet(maintenanceWindowIds)); }, [caseIds, maintenanceWindowIds, queryClient, queryParams.pageIndex, refetchAlerts, ruleIds]); useEffect(() => { @@ -381,7 +371,19 @@ const AlertsTableContent = typedForwardRef( toggleColumn: onToggleColumn, })); - const bulkActionsStore = useReducer(bulkActionsReducer, initialBulkActionsState); + const [bulkActionsState, dispatchBulkAction] = useReducer( + bulkActionsReducer, + initialBulkActionsState + ); + + const bulkActionsStore = useMemo( + () => + [bulkActionsState, dispatchBulkAction] as [ + typeof bulkActionsState, + typeof dispatchBulkAction + ], + [bulkActionsState, dispatchBulkAction] + ); const onSortChange = useCallback( (_sort: EuiDataGridSorting['columns']) => { @@ -439,48 +441,37 @@ const AlertsTableContent = typedForwardRef( columns, tableId: id, dataGridRef, - refresh, - isLoading: isLoadingAlerts || casesQuery.isFetching || maintenanceWindowsQuery.isFetching || mutedAlertsQuery.isFetching, - isLoadingAlerts, alerts, alertsCount, // TODO deprecate ecsAlertsData, oldAlertsData, - browserFields, - isLoadingCases: casesQuery.isFetching, cases: casesQuery.data, - isLoadingMaintenanceWindows: maintenanceWindowsQuery.isFetching, maintenanceWindows: maintenanceWindowsQuery.data, - isLoadingMutedAlerts: mutedAlertsQuery.isFetching, mutedAlerts: mutedAlertsQuery.data, - pageIndex: pagination.pageIndex, pageSize: pagination.pageSize, - showAlertStatusWithFlapping, - fieldFormats, openAlertInFlyout, - bulkActionsStore, - renderCellValue, renderCellPopover, renderActionsCell, renderFlyoutHeader, renderFlyoutBody, renderFlyoutFooter, + services: memoizedServices, } as RenderContext), [ additionalContext, @@ -488,21 +479,20 @@ const AlertsTableContent = typedForwardRef( id, refresh, isLoadingAlerts, - alerts, - alertsCount, - ecsAlertsData, - oldAlertsData, - browserFields, casesQuery.isFetching, casesQuery.data, maintenanceWindowsQuery.isFetching, maintenanceWindowsQuery.data, mutedAlertsQuery.isFetching, mutedAlertsQuery.data, + alerts, + alertsCount, + ecsAlertsData, + oldAlertsData, + browserFields, pagination.pageIndex, pagination.pageSize, showAlertStatusWithFlapping, - fieldFormats, openAlertInFlyout, bulkActionsStore, renderCellValue, @@ -511,6 +501,7 @@ const AlertsTableContent = typedForwardRef( renderFlyoutHeader, renderFlyoutBody, renderFlyoutFooter, + memoizedServices, ] ); @@ -618,5 +609,7 @@ const AlertsTableContent = typedForwardRef( } ); +// Lazy loading helpers // eslint-disable-next-line import/no-default-export export { AlertsTable as default }; +export type AlertsTable = typeof AlertsTable; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/row_cell.tsx b/packages/response-ops/alerts_table/components/bulk_actions_cell.tsx similarity index 76% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/row_cell.tsx rename to packages/response-ops/alerts_table/components/bulk_actions_cell.tsx index 8c8f7bdcc4576..357091b72a9bd 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/row_cell.tsx +++ b/packages/response-ops/alerts_table/components/bulk_actions_cell.tsx @@ -1,15 +1,17 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EuiCheckbox, EuiLoadingSpinner, RenderCellValue } from '@elastic/eui'; import React, { ChangeEvent, memo, useCallback } from 'react'; import { SELECT_ROW_ARIA_LABEL } from '../translations'; -import { useAlertsTableContext } from '../../contexts/alerts_table_context'; -import { BulkActionsVerbs } from '../../../../../types'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; +import { BulkActionsVerbs } from '../types'; export const BulkActionsCell = memo( ({ @@ -42,7 +44,6 @@ export const BulkActionsCell = memo( // NOTE: id is prefixed here to avoid conflicts with labels in other sections in the app. // see https://github.com/elastic/kibana/issues/162837 - return ( ); } + // See props comment above ) as unknown as RenderCellValue; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/column_header.tsx b/packages/response-ops/alerts_table/components/bulk_actions_header_cell.tsx similarity index 68% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/column_header.tsx rename to packages/response-ops/alerts_table/components/bulk_actions_header_cell.tsx index 31a48affcc3f9..1194ebdc7e81f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/column_header.tsx +++ b/packages/response-ops/alerts_table/components/bulk_actions_header_cell.tsx @@ -1,15 +1,17 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EuiCheckbox } from '@elastic/eui'; import React, { ChangeEvent, useCallback } from 'react'; -import { BulkActionsVerbs } from '../../../../../types'; +import { BulkActionsVerbs } from '../types'; import { COLUMN_HEADER_ARIA_LABEL } from '../translations'; -import { useAlertsTableContext } from '../../contexts/alerts_table_context'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; const BulkActionsHeaderComponent: React.FunctionComponent = () => { const { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/toolbar.tsx b/packages/response-ops/alerts_table/components/bulk_actions_toolbar_control.tsx similarity index 82% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/toolbar.tsx rename to packages/response-ops/alerts_table/components/bulk_actions_toolbar_control.tsx index ca252bd65d851..beff67e93b531 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/toolbar.tsx +++ b/packages/response-ops/alerts_table/components/bulk_actions_toolbar_control.tsx @@ -1,14 +1,15 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ +import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { EuiPopover, EuiButtonEmpty, EuiContextMenu } from '@elastic/eui'; import numeral from '@elastic/numeral'; -import React, { useState, useCallback, useMemo, useEffect } from 'react'; -import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; import { ALERT_CASE_IDS, ALERT_RULE_NAME, @@ -16,43 +17,28 @@ import { ALERT_WORKFLOW_ASSIGNEE_IDS, ALERT_WORKFLOW_TAGS, } from '@kbn/rule-data-utils'; -import { - Alerts, - BulkActionsPanelConfig, - BulkActionsVerbs, - RowSelection, -} from '../../../../../types'; +import { Alert } from '@kbn/alerting-types'; +import useObservable from 'react-use/lib/useObservable'; +import type { SettingsStart } from '@kbn/core-ui-settings-browser'; +import { BulkActionsPanelConfig, BulkActionsVerbs, RowSelection, TimelineItem } from '../types'; import * as i18n from '../translations'; -import { useAlertsTableContext } from '../../contexts/alerts_table_context'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; interface BulkActionsProps { totalItems: number; panels: BulkActionsPanelConfig[]; - alerts: Alerts; + alerts: Alert[]; setIsBulkActionsLoading: (loading: boolean) => void; clearSelection: () => void; refresh: () => void; -} - -// Duplicated just for legacy reasons. Timelines plugin will be removed but -// as long as the integration still work with Timelines we have to keep it -export interface TimelineItem { - _id: string; - _index?: string | null; - data: TimelineNonEcsData[]; - ecs: { _id: string; _index: string }; -} - -export interface TimelineNonEcsData { - field: string; - value?: string[] | null; + settings: SettingsStart; } const DEFAULT_NUMBER_FORMAT = 'format:number:defaultPattern'; const containerStyles = { display: 'inline-block', position: 'relative' } as const; const selectedIdsToTimelineItemMapper = ( - alerts: Alerts, + alerts: Alert[], rowSelection: RowSelection ): TimelineItem[] => { return Array.from(rowSelection.keys()).map((rowIndex: number) => { @@ -61,11 +47,14 @@ const selectedIdsToTimelineItemMapper = ( _id: alert._id, _index: alert._index, data: [ - { field: ALERT_RULE_NAME, value: alert[ALERT_RULE_NAME] }, - { field: ALERT_RULE_UUID, value: alert[ALERT_RULE_UUID] }, - { field: ALERT_CASE_IDS, value: alert[ALERT_CASE_IDS] ?? [] }, - { field: ALERT_WORKFLOW_TAGS, value: alert[ALERT_WORKFLOW_TAGS] ?? [] }, - { field: ALERT_WORKFLOW_ASSIGNEE_IDS, value: alert[ALERT_WORKFLOW_ASSIGNEE_IDS] ?? [] }, + { field: ALERT_RULE_NAME, value: alert[ALERT_RULE_NAME] as string[] }, + { field: ALERT_RULE_UUID, value: alert[ALERT_RULE_UUID] as string[] }, + { field: ALERT_CASE_IDS, value: (alert[ALERT_CASE_IDS] ?? []) as string[] }, + { field: ALERT_WORKFLOW_TAGS, value: (alert[ALERT_WORKFLOW_TAGS] ?? []) as string[] }, + { + field: ALERT_WORKFLOW_ASSIGNEE_IDS, + value: (alert[ALERT_WORKFLOW_ASSIGNEE_IDS] ?? []) as string[], + }, ], ecs: { _id: alert._id, @@ -84,7 +73,7 @@ const useBulkActionsToMenuPanelMapper = ( clearSelection: BulkActionsProps['clearSelection'], // In case bulk item action changes the alert data and need to refresh table page. refresh: BulkActionsProps['refresh'], - alerts: Alerts, + alerts: Alert[], closeIfPopoverIsOpen: () => void ) => { const { @@ -153,16 +142,20 @@ const BulkActionsComponent: React.FC = ({ setIsBulkActionsLoading, clearSelection, refresh, + settings, }) => { const { bulkActionsStore: [{ rowSelection, isAllSelected }, updateSelectedRows], } = useAlertsTableContext(); const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const [showClearSelection, setShowClearSelectiong] = useState(false); + const defaultNumberFormat = useObservable( + useMemo(() => settings.client.get$(DEFAULT_NUMBER_FORMAT), [settings.client]), + settings.client.get(DEFAULT_NUMBER_FORMAT) + ); + const [showClearSelection, setShowClearSelection] = useState(false); useEffect(() => { - setShowClearSelectiong(isAllSelected); + setShowClearSelection(isAllSelected); }, [isAllSelected]); const selectedCount = rowSelection.size; @@ -265,6 +258,6 @@ const BulkActionsComponent: React.FC = ({ ); }; -// disabled to be able lazy load +// Lazy loading helpers // eslint-disable-next-line import/no-default-export export default React.memo(BulkActionsComponent); diff --git a/packages/response-ops/alerts_table/components/cases_cell.test.tsx b/packages/response-ops/alerts_table/components/cases_cell.test.tsx new file mode 100644 index 0000000000000..89af910a1f1cf --- /dev/null +++ b/packages/response-ops/alerts_table/components/cases_cell.test.tsx @@ -0,0 +1,136 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Alert } from '@kbn/alerting-types'; +import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { CasesCell } from './cases_cell'; +import { AdditionalContext, CellComponentProps, RenderContext } from '../types'; +import { getCasesMapMock } from '../mocks/cases.mock'; +import { getMaintenanceWindowsMapMock } from '../mocks/maintenance_windows.mock'; +import { useCaseViewNavigation } from '../hooks/use_case_view_navigation'; +import { createPartialObjectMock } from '../utils/test'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; + +jest.mock('../hooks/use_case_view_navigation'); + +const useCaseViewNavigationMock = jest.mocked(useCaseViewNavigation); +const casesMap = getCasesMapMock(); +const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); +const alert: Alert = { + _id: 'alert-id', + _index: 'alert-index', + 'kibana.alert.case_ids': ['test-id'], +}; + +const props = createPartialObjectMock({ + isLoading: false, + alert, + cases: casesMap, + maintenanceWindows: maintenanceWindowsMap, + columnId: 'kibana.alert.case_ids', + showAlertStatusWithFlapping: false, +}); + +const context = createPartialObjectMock>({ + services: { + application: applicationServiceMock.createStartContract(), + }, +}); + +const navigateToCaseView = jest.fn(); +useCaseViewNavigationMock.mockReturnValue({ navigateToCaseView }); + +const TestComponent = (_props: CellComponentProps) => ( + + + + + +); + +describe('CasesCell', () => { + it('renders the cases cell', async () => { + render(); + expect(screen.getByText('Test case')).toBeInTheDocument(); + }); + + it('renders the loading skeleton', async () => { + render(); + expect(screen.getByTestId('cases-cell-loading')).toBeInTheDocument(); + }); + + it('renders multiple cases correctly', async () => { + render( + + ); + + expect(screen.getByText('Test case')).toBeInTheDocument(); + expect(screen.getByText('Test case 2')).toBeInTheDocument(); + }); + + it('does not render a case that it is in the map but not in the alerts data', async () => { + render(); + + expect(screen.getByText('Test case')).toBeInTheDocument(); + expect(screen.queryByText('Test case 2')).not.toBeInTheDocument(); + }); + + it('does not show any cases when the alert does not have any case ids', async () => { + render(); + + expect(screen.queryByText('Test case')).not.toBeInTheDocument(); + expect(screen.queryByText('Test case 2')).not.toBeInTheDocument(); + }); + + it('does show the default value when the alert does not have any case ids', async () => { + render(); + + expect(screen.getByText('--')).toBeInTheDocument(); + }); + + it('does not show any cases when the alert has invalid case ids', async () => { + render( + + ); + + expect(screen.queryByTestId('cases-cell-link')).not.toBeInTheDocument(); + }); + + it('does show the default value when the alert has invalid case ids', async () => { + render( + + ); + + expect(screen.getByText('--')).toBeInTheDocument(); + }); + + it('shows the cases tooltip', async () => { + render(); + expect(screen.getByText('Test case')).toBeInTheDocument(); + + await userEvent.hover(screen.getByText('Test case')); + + expect(await screen.findByTestId('cases-components-tooltip')).toBeInTheDocument(); + }); + + it('navigates to the case correctly', async () => { + render(); + expect(screen.getByText('Test case')).toBeInTheDocument(); + + await userEvent.click(screen.getByText('Test case')); + expect(navigateToCaseView).toBeCalledWith({ caseId: 'test-id' }); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.tsx b/packages/response-ops/alerts_table/components/cases_cell.tsx similarity index 67% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.tsx rename to packages/response-ops/alerts_table/components/cases_cell.tsx index afbc4cdf76e74..fd3fc6db1d3d2 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.tsx +++ b/packages/response-ops/alerts_table/components/cases_cell.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { memo } from 'react'; @@ -10,9 +12,10 @@ import { EuiLink, EuiSkeletonText } from '@elastic/eui'; import { Tooltip as CaseTooltip } from '@kbn/cases-components'; import type { CaseTooltipContentProps } from '@kbn/cases-components'; import { ALERT_CASE_IDS } from '@kbn/rule-data-utils'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; import type { CellComponent } from '../types'; -import { useCaseViewNavigation } from './use_case_view_navigation'; -import { Case } from '../hooks/apis/bulk_get_cases'; +import { useCaseViewNavigation } from '../hooks/use_case_view_navigation'; +import type { Case } from '../apis/bulk_get_cases'; const formatCase = (theCase: Case): CaseTooltipContentProps => ({ title: theCase.title, @@ -28,9 +31,12 @@ const formatCase = (theCase: Case): CaseTooltipContentProps => ({ export const CasesCell: CellComponent = memo((props) => { const { isLoading, alert, cases, caseAppId } = props; - const { navigateToCaseView } = useCaseViewNavigation(caseAppId); + const { + services: { application }, + } = useAlertsTableContext(); + const { navigateToCaseView } = useCaseViewNavigation(application, caseAppId); - const caseIds = (alert && alert[ALERT_CASE_IDS]) ?? []; + const caseIds = (alert && (alert[ALERT_CASE_IDS] as string[])) ?? []; const validCases = caseIds .map((id) => cases?.get(id)) diff --git a/packages/response-ops/alerts_table/components/cell_popover_host.test.tsx b/packages/response-ops/alerts_table/components/cell_popover_host.test.tsx new file mode 100644 index 0000000000000..c1a2128074ddc --- /dev/null +++ b/packages/response-ops/alerts_table/components/cell_popover_host.test.tsx @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useEffect } from 'react'; +import { screen, render } from '@testing-library/react'; +import { EuiDataGridCellPopoverElementProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; +import { CellPopoverHost } from './cell_popover_host'; +import { createPartialObjectMock } from '../utils/test'; +import { mockRenderContext } from '../mocks/context.mock'; + +const props = createPartialObjectMock({ + rowIndex: 0, + DefaultCellPopover: jest.fn().mockReturnValue(
), +}); + +describe('CellPopoverHost', () => { + it('should render the renderCellPopover when provided', () => { + render( +
), + }} + > + + + ); + expect(screen.getByTestId('renderCellPopover')).toBeInTheDocument(); + }); + + it('should catch errors from the custom CellPopover', async () => { + const CustomCellPopover = () => { + useEffect(() => { + throw new Error('test error'); + }, []); + return null; + }; + render( + + + + + + ); + expect(await screen.findByTestId('errorCell')).toBeInTheDocument(); + }); + + it('should render the DefaultCellPopover when renderCellPopover is not provided', () => { + render( + + + + ); + expect(screen.getByTestId('defaultCellPopover')).toBeInTheDocument(); + }); +}); diff --git a/packages/response-ops/alerts_table/components/cell_popover_host.tsx b/packages/response-ops/alerts_table/components/cell_popover_host.tsx new file mode 100644 index 0000000000000..bba96d7a0084d --- /dev/null +++ b/packages/response-ops/alerts_table/components/cell_popover_host.tsx @@ -0,0 +1,37 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EuiDataGridCellPopoverElementProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; +import React from 'react'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; +import { ErrorBoundary } from './error_boundary'; +import { ErrorCell } from './error_cell'; + +/** + * Entry point for rendering cell popovers + * + * Wraps the provided `CellPopover` with an `ErrorBoundary` to catch any errors that occur + */ +export const CellPopoverHost = (props: EuiDataGridCellPopoverElementProps) => { + const { rowIndex, DefaultCellPopover } = props; + const renderContext = useAlertsTableContext(); + const { pageSize, pageIndex, alerts, renderCellPopover: CellPopover } = renderContext; + + const idx = rowIndex - pageSize * pageIndex; + const alert = alerts[idx]; + if (alert && CellPopover) { + return ( + + + + ); + } + + return ; +}; diff --git a/packages/response-ops/alerts_table/components/cell_value_host.test.tsx b/packages/response-ops/alerts_table/components/cell_value_host.test.tsx new file mode 100644 index 0000000000000..b0f2fefaaa867 --- /dev/null +++ b/packages/response-ops/alerts_table/components/cell_value_host.test.tsx @@ -0,0 +1,76 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { ComponentProps, FC } from 'react'; +import { render, screen } from '@testing-library/react'; +import { CellValueHost } from './cell_value_host'; +import { createPartialObjectMock } from '../utils/test'; +import { ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS, ALERT_STATUS } from '@kbn/rule-data-utils'; +import { DefaultCellValue } from './default_cell_value'; + +jest.mock('./system_cell', () => { + const original = jest.requireActual('./system_cell'); + return { + ...original, + SystemCell: jest.fn(() =>
), + }; +}); +jest.mock('./default_cell_value'); +jest.mocked(DefaultCellValue as FC).mockReturnValue(
); + +const props = createPartialObjectMock>({ + isLoading: false, + alerts: [ + { + _id: 'test', + _index: 'alerts', + [ALERT_STATUS]: ['active'], + }, + ], + oldAlertsData: [], + ecsAlertsData: [], + showAlertStatusWithFlapping: false, + casesConfig: undefined, + rowIndex: 0, + pageIndex: 0, + pageSize: 10, +}); + +describe('CellValueHost', () => { + it.each([ALERT_STATUS, ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS])( + 'should render a SystemCell for cases, maintenance windows, and alert status', + (columnId) => { + render(); + expect(screen.getByTestId('systemCell')).toBeInTheDocument(); + } + ); + + it('should render the provided renderCellValue for other fields', () => { + render( + ( +
+ ))} + /> + ); + expect(screen.getByTestId('customRenderCellValue')).toBeInTheDocument(); + }); + + it('should render a DefaultCell for other fields when a custom renderCellValue is not defined', () => { + render(); + expect(screen.getByTestId('defaultCell')).toBeInTheDocument(); + }); + + it('should render a loading skeleton when the isLoading prop is true and the alert is not available yet', () => { + render(); + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + }); +}); diff --git a/packages/response-ops/alerts_table/components/cell_value_host.tsx b/packages/response-ops/alerts_table/components/cell_value_host.tsx new file mode 100644 index 0000000000000..aa878598dd192 --- /dev/null +++ b/packages/response-ops/alerts_table/components/cell_value_host.tsx @@ -0,0 +1,74 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EuiSkeletonText } from '@elastic/eui'; +import React from 'react'; +import { GetAlertsTableProp, SystemCellId } from '../types'; +import { DefaultCellValue } from './default_cell_value'; +import { SystemCell, systemCells } from './system_cell'; +import { ErrorBoundary } from './error_boundary'; +import { ErrorCell } from './error_cell'; + +/** + * Entry point for rendering cell values + * + * Renders a SystemCell for cases, maintenance windows, and alert status, the `renderCellValue` + * provided in the `AlertsTableProps` for other fields or the default cell value renderer otherwise. + * When the alerts or the related cases or maintenance windows are loading, a skeleton text is rendered. + */ +export const CellValueHost: GetAlertsTableProp<'renderCellValue'> = (props) => { + const { + columnId, + renderCellValue: CellValue = DefaultCellValue, + isLoading, + alerts, + oldAlertsData, + ecsAlertsData, + cases, + maintenanceWindows, + showAlertStatusWithFlapping, + casesConfig, + rowIndex, + pageIndex, + pageSize, + } = props; + const idx = rowIndex - pageSize * pageIndex; + const alert = alerts[idx]; + const legacyAlert = oldAlertsData[idx]; + const ecsAlert = ecsAlertsData[idx]; + if (isSystemCell(columnId) && alert) { + return ( + + ); + } + if (alert) { + return ( + + + + ); + } + if (isLoading) { + return ; + } + return null; +}; + +const isSystemCell = (columnId: string): columnId is SystemCellId => { + return systemCells.includes(columnId as SystemCellId); +}; diff --git a/packages/response-ops/alerts_table/components/control_column_header_cell.tsx b/packages/response-ops/alerts_table/components/control_column_header_cell.tsx new file mode 100644 index 0000000000000..373a595dd96f0 --- /dev/null +++ b/packages/response-ops/alerts_table/components/control_column_header_cell.tsx @@ -0,0 +1,19 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { memo } from 'react'; +import { ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL } from '../translations'; + +export const ControlColumnHeaderCell = memo(() => { + return ( + + {ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL} + + ); +}); diff --git a/packages/response-ops/alerts_table/components/default_alert_actions.test.tsx b/packages/response-ops/alerts_table/components/default_alert_actions.test.tsx new file mode 100644 index 0000000000000..b599741b00e99 --- /dev/null +++ b/packages/response-ops/alerts_table/components/default_alert_actions.test.tsx @@ -0,0 +1,89 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { DefaultAlertActions } from './default_alert_actions'; +import { render, screen } from '@testing-library/react'; +import { AdditionalContext, AlertActionsProps, RenderContext } from '../types'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { createPartialObjectMock } from '../utils/test'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; + +jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_load_rule_types_query', () => ({ + useLoadRuleTypesQuery: jest.fn(), +})); +jest.mock('./view_rule_details_alert_action', () => { + return { + ViewRuleDetailsAlertAction: () => ( +
{'ViewRuleDetailsAlertAction'}
+ ), + }; +}); +jest.mock('./view_alert_details_alert_action', () => { + return { + ViewAlertDetailsAlertAction: () => ( +
{'ViewAlertDetailsAlertAction'}
+ ), + }; +}); +jest.mock('./mute_alert_action', () => { + return { MuteAlertAction: () =>
{'MuteAlertAction'}
}; +}); +jest.mock('./mark_as_untracked_alert_action', () => { + return { + MarkAsUntrackedAlertAction: () => ( +
{'MarkAsUntrackedAlertAction'}
+ ), + }; +}); + +const { useLoadRuleTypesQuery } = jest.requireMock( + '@kbn/alerts-ui-shared/src/common/hooks/use_load_rule_types_query' +); + +const http = httpServiceMock.createStartContract(); +const notifications = notificationServiceMock.createStartContract(); +const props = createPartialObjectMock({ + alert: {}, + refresh: jest.fn(), +}); + +const context = createPartialObjectMock>({ + services: { + http, + notifications, + }, +}); + +const TestComponent = (_props: AlertActionsProps) => ( + + + +); + +describe('DefaultAlertActions', () => { + it('should show "Mute" and "Marked as untracked" option', async () => { + useLoadRuleTypesQuery.mockReturnValue({ authorizedToCreateAnyRules: true }); + + render(); + + expect(await screen.findByText('MuteAlertAction')).toBeInTheDocument(); + expect(await screen.findByText('MarkAsUntrackedAlertAction')).toBeInTheDocument(); + }); + + it('should hide "Mute" and "Marked as untracked" option', async () => { + useLoadRuleTypesQuery.mockReturnValue({ authorizedToCreateAnyRules: false }); + + render(); + + expect(screen.queryByText('MuteAlertAction')).not.toBeInTheDocument(); + expect(screen.queryByText('MarkAsUntrackedAlertAction')).not.toBeInTheDocument(); + }); +}); diff --git a/packages/response-ops/alerts_table/components/default_alert_actions.tsx b/packages/response-ops/alerts_table/components/default_alert_actions.tsx new file mode 100644 index 0000000000000..7317fdff1ff18 --- /dev/null +++ b/packages/response-ops/alerts_table/components/default_alert_actions.tsx @@ -0,0 +1,47 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { useLoadRuleTypesQuery } from '@kbn/alerts-ui-shared/src/common/hooks'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import { ViewRuleDetailsAlertAction } from './view_rule_details_alert_action'; +import type { AdditionalContext, AlertActionsProps } from '../types'; +import { ViewAlertDetailsAlertAction } from './view_alert_details_alert_action'; +import { MuteAlertAction } from './mute_alert_action'; +import { MarkAsUntrackedAlertAction } from './mark_as_untracked_alert_action'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; + +/** + * Common alerts table row actions + */ +export const DefaultAlertActions = ( + props: AlertActionsProps +) => { + const { + services: { + http, + notifications: { toasts }, + }, + } = useAlertsTableContext(); + const { authorizedToCreateAnyRules } = useLoadRuleTypesQuery({ + filteredRuleTypes: [], + http, + toasts, + context: AlertsQueryContext, + }); + + return ( + <> + + + {authorizedToCreateAnyRules && } + {authorizedToCreateAnyRules && } + + ); +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx b/packages/response-ops/alerts_table/components/default_alerts_flyout.test.tsx similarity index 77% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx rename to packages/response-ops/alerts_table/components/default_alerts_flyout.test.tsx index 85fe3a995f193..b1ed60af2fc25 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.test.tsx +++ b/packages/response-ops/alerts_table/components/default_alerts_flyout.test.tsx @@ -1,18 +1,27 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ +import React from 'react'; import { waitFor } from '@testing-library/react'; import { mount } from 'enzyme'; import type { ReactWrapper } from 'enzyme'; -import React, { ComponentProps } from 'react'; - -import { AlertsTableFlyoutBaseProps } from '../../../..'; -import { DefaultAlertsFlyoutBody } from './default_alerts_flyout'; import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import { + AdditionalContext, + AlertsTableFlyoutBaseProps, + FlyoutSectionProps, + RenderContext, +} from '../types'; +import { DefaultAlertsFlyoutBody } from './default_alerts_flyout'; +import { createPartialObjectMock } from '../utils/test'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; const columns = [ { @@ -85,18 +94,26 @@ const tabsData = [ { name: 'Table', subj: 'tableTab' }, ]; +const context = createPartialObjectMock>({ + services: { + http: httpServiceMock.createStartContract(), + fieldFormats: fieldFormatsMock, + }, +}); + describe('DefaultAlertsFlyout', () => { let wrapper: ReactWrapper; beforeAll(async () => { wrapper = mount( - )} - /> + + ({ + alert, + isLoading: false, + columns, + })} + /> + ) as ReactWrapper; await waitFor(() => wrapper.update()); }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx b/packages/response-ops/alerts_table/components/default_alerts_flyout.tsx similarity index 67% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx rename to packages/response-ops/alerts_table/components/default_alerts_flyout.tsx index d76e84078bc26..43eefb7a88303 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/default_alerts_flyout.tsx +++ b/packages/response-ops/alerts_table/components/default_alerts_flyout.tsx @@ -1,36 +1,37 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { useCallback, useMemo, useState } from 'react'; import { EuiDescriptionList, EuiPanel, EuiTabbedContentTab, EuiTitle } from '@elastic/eui'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; -import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { ScrollableFlyoutTabbedContent, AlertFieldsTable } from '@kbn/alerts-ui-shared'; -import { FlyoutSectionRenderer } from '../../../../types'; -import { getAlertFormatters } from '../cells/render_cell_value'; +import { AdditionalContext, FlyoutSectionProps } from '../types'; import { defaultAlertsTableColumns } from '../configuration'; +import { DefaultCellValue } from './default_cell_value'; -export const DefaultAlertsFlyoutHeader: FlyoutSectionRenderer = ({ alert }) => { +export const DefaultAlertsFlyoutHeader = ({ + alert, +}: FlyoutSectionProps) => { return ( -

{alert[ALERT_RULE_NAME] ?? 'Unknown'}

+

{(alert[ALERT_RULE_NAME]?.[0] as string) ?? 'Unknown'}

); }; type TabId = 'overview' | 'table'; -export const DefaultAlertsFlyoutBody: FlyoutSectionRenderer = ({ - alert, - fieldFormats, - columns, -}) => { - const formatColumnValue = useMemo(() => getAlertFormatters(fieldFormats), [fieldFormats]); +export const DefaultAlertsFlyoutBody = ( + props: FlyoutSectionProps +) => { + const { alert, columns } = props; const overviewTab = useMemo( () => ({ id: 'overview', @@ -42,11 +43,16 @@ export const DefaultAlertsFlyoutBody: FlyoutSectionRenderer = ({ { - const value = get(alert, column.id)?.[0]; + const value = alert[column.id]?.[0]; return { title: column.displayAsText as string, - description: value != null ? formatColumnValue(column.id, value) : '—', + description: + value != null ? ( + + ) : ( + '—' + ), }; })} type="column" @@ -55,7 +61,7 @@ export const DefaultAlertsFlyoutBody: FlyoutSectionRenderer = ({ ), }), - [alert, columns, formatColumnValue] + [alert, columns, props] ); const tableTab = useMemo( diff --git a/packages/response-ops/alerts_table/components/default_cell.test.tsx b/packages/response-ops/alerts_table/components/default_cell.test.tsx new file mode 100644 index 0000000000000..305c9974bee19 --- /dev/null +++ b/packages/response-ops/alerts_table/components/default_cell.test.tsx @@ -0,0 +1,56 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import type { Alert } from '@kbn/alerting-types'; +import { DefaultCell } from './default_cell'; +import { CellComponentProps } from '../types'; +import { getCasesMapMock } from '../mocks/cases.mock'; +import { getMaintenanceWindowsMapMock } from '../mocks/maintenance_windows.mock'; +import { createPartialObjectMock } from '../utils/test'; + +const casesMap = getCasesMapMock(); +const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); +const alert: Alert = { + _id: 'alert-id', + _index: 'alert-index', + 'kibana.alert.status': ['active'], +}; + +const props = createPartialObjectMock({ + isLoading: false, + alert, + cases: casesMap, + maintenanceWindows: maintenanceWindowsMap, + columnId: 'kibana.alert.status', + showAlertStatusWithFlapping: false, +}); + +describe('DefaultCell', () => { + it('shows the value', async () => { + render(); + expect(screen.getByText('active')).toBeInTheDocument(); + }); + + it('shows empty tag if the value is empty', async () => { + render(); + expect(screen.getByText('--')).toBeInTheDocument(); + }); + + it('shows multiple values', async () => { + render( + + ); + expect(screen.getByText('active, recovered')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.tsx b/packages/response-ops/alerts_table/components/default_cell.tsx similarity index 52% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.tsx rename to packages/response-ops/alerts_table/components/default_cell.tsx index 9ebc146ef36b8..4503002a04af1 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.tsx +++ b/packages/response-ops/alerts_table/components/default_cell.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { memo } from 'react'; diff --git a/packages/response-ops/alerts_table/components/default_cell_value.test.tsx b/packages/response-ops/alerts_table/components/default_cell_value.test.tsx new file mode 100644 index 0000000000000..b9a5713be9be3 --- /dev/null +++ b/packages/response-ops/alerts_table/components/default_cell_value.test.tsx @@ -0,0 +1,111 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { ComponentProps } from 'react'; +import { + ALERT_DURATION, + ALERT_RULE_CONSUMER, + ALERT_RULE_NAME, + ALERT_RULE_PRODUCER, + ALERT_START, + TIMESTAMP, +} from '@kbn/rule-data-utils'; +import { screen, render } from '@testing-library/react'; +import { DefaultCellValue } from './default_cell_value'; +import { createPartialObjectMock } from '../utils/test'; +import { CellComponentProps } from '../types'; +import { mockRenderContext } from '../mocks/context.mock'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; + +const props = createPartialObjectMock({ + ...mockRenderContext, + alert: mockRenderContext.alerts[0], +}); + +const TestComponent = (_props: ComponentProps) => ( + + + +); + +describe('DefaultCellValue', () => { + it.each([TIMESTAMP, ALERT_START])('should format date fields', (columnId) => { + render(); + expect(mockRenderContext.services.fieldFormats.deserialize).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'date', + }) + ); + }); + + it('should render the rule name as a link', () => { + render(); + expect(screen.queryByRole('link')).toBeInTheDocument(); + }); + + it('should render the alert duration in milliseconds', () => { + render(); + expect(mockRenderContext.services.fieldFormats.deserialize).toHaveBeenCalledWith({ + id: 'duration', + params: { + inputFormat: 'microseconds', + outputFormat: 'humanizePrecise', + }, + }); + }); + + describe('in the rule consumer column', () => { + it('should show "observability" for any observability producer', () => { + render( + + ); + expect(screen.queryByText('Observability')).toBeInTheDocument(); + }); + + it.each(['alerts', 'stackAlerts', 'discover'])( + 'should show the producer when the consumer is %s', + (consumer) => { + render( + + ); + expect(screen.queryByText('Machine Learning')).toBeInTheDocument(); + } + ); + }); + + it('else should show the consumer', () => { + render( + + ); + expect(screen.queryByText('test-consumer')).toBeInTheDocument(); + }); +}); diff --git a/packages/response-ops/alerts_table/components/default_cell_value.tsx b/packages/response-ops/alerts_table/components/default_cell_value.tsx new file mode 100644 index 0000000000000..11d4705306a7a --- /dev/null +++ b/packages/response-ops/alerts_table/components/default_cell_value.tsx @@ -0,0 +1,102 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { ComponentProps } from 'react'; +import { isEmpty } from 'lodash'; +import { + ALERT_DURATION, + AlertConsumers, + ALERT_RULE_NAME, + ALERT_RULE_UUID, + ALERT_START, + TIMESTAMP, + ALERT_RULE_CONSUMER, + ALERT_RULE_PRODUCER, +} from '@kbn/rule-data-utils'; +import { EuiBadge, EuiLink } from '@elastic/eui'; +import { AlertsTableSupportedConsumers, GetAlertsTableProp } from '../types'; +import { alertProducersData, observabilityFeatureIds } from '../constants'; +import { useFieldFormatter } from '../hooks/use_field_formatter'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; + +export const DefaultCellValue = ({ + alert, + columnId, +}: Pick>, 'alert' | 'columnId'>) => { + const { + services: { fieldFormats, http }, + } = useAlertsTableContext(); + const formatField = useFieldFormatter(fieldFormats); + const rawValue = alert[columnId]?.[0]; + const value = getRenderValue(rawValue); + + switch (columnId) { + case TIMESTAMP: + case ALERT_START: + return <>{formatField('date')(value)}; + + case ALERT_RULE_NAME: + if (!alert) { + return <>{value}; + } + const ruleName = alert?.[ALERT_RULE_NAME]?.[0] as string | undefined; + const ruleUuid = alert?.[ALERT_RULE_UUID]?.[0] as string | undefined; + if (!ruleName || !ruleUuid) { + return null; + } + return ( + + {ruleName} + + ); + + case ALERT_DURATION: + return ( + <> + {formatField('duration', { + inputFormat: 'microseconds', + outputFormat: 'humanizePrecise', + })(value) || '--'} + + ); + + case ALERT_RULE_CONSUMER: + const producer = alert?.[ALERT_RULE_PRODUCER]?.[0] as AlertConsumers; + const consumer: AlertsTableSupportedConsumers = observabilityFeatureIds.includes(producer) + ? 'observability' + : producer && (value === 'alerts' || value === 'stackAlerts' || value === 'discover') + ? producer + : value; + const consumerData = alertProducersData[consumer]; + if (!consumerData) { + return <>{value}; + } + return {consumerData.displayName}; + + default: + return <>{value}; + } +}; + +const getRenderValue = (mappedNonEcsValue: any) => { + const value = Array.isArray(mappedNonEcsValue) ? mappedNonEcsValue.join() : mappedNonEcsValue; + + if (!isEmpty(value)) { + if (typeof value === 'object') { + return JSON.stringify(value); + } + return value; + } + + return '—'; +}; diff --git a/packages/response-ops/alerts_table/components/empty_state.stories.tsx b/packages/response-ops/alerts_table/components/empty_state.stories.tsx new file mode 100644 index 0000000000000..950f61f449185 --- /dev/null +++ b/packages/response-ops/alerts_table/components/empty_state.stories.tsx @@ -0,0 +1,24 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EmptyState as Component } from './empty_state'; + +export default { + component: Component, + title: 'app/AlertTable', + argTypes: { + height: { type: 'select', options: ['short', 'tall'] }, + }, +}; + +export const EmptyState = { + args: { + height: 'tall', + }, +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.tsx b/packages/response-ops/alerts_table/components/empty_state.tsx similarity index 78% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.tsx rename to packages/response-ops/alerts_table/components/empty_state.tsx index 8d7954756ea3d..eab43e2c1af0f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.tsx +++ b/packages/response-ops/alerts_table/components/empty_state.tsx @@ -1,17 +1,19 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { ReactNode } from 'react'; import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiImage, EuiText, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EsQuerySnapshot } from '@kbn/alerts-ui-shared'; -import icon from './assets/illustration_product_no_results_magnifying_glass.svg'; -import { InspectButton } from './toolbar/components/inspect'; -import { ALERTS_TABLE_TITLE } from './translations'; +import type { EsQuerySnapshot } from '@kbn/alerting-types'; +import icon from '../assets/illustration_product_no_results_magnifying_glass.svg'; +import { AlertsQueryInspector } from './alerts_query_inspector'; +import { ALERTS_TABLE_TITLE } from '../translations'; const heights = { tall: 490, @@ -33,7 +35,7 @@ export const EmptyState: React.FC<{ {alertsQuerySnapshot && showInspectButton && ( - diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/error_boundary.test.tsx b/packages/response-ops/alerts_table/components/error_boundary.test.tsx similarity index 71% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/error_boundary.test.tsx rename to packages/response-ops/alerts_table/components/error_boundary.test.tsx index b47536e8b4b6d..40f03951e63bc 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/error_boundary.test.tsx +++ b/packages/response-ops/alerts_table/components/error_boundary.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/error_boundary.tsx b/packages/response-ops/alerts_table/components/error_boundary.tsx similarity index 68% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/error_boundary.tsx rename to packages/response-ops/alerts_table/components/error_boundary.tsx index 7a48dd8fb1a01..91ecfc8906454 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/error_boundary.tsx +++ b/packages/response-ops/alerts_table/components/error_boundary.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; diff --git a/packages/response-ops/alerts_table/components/error_cell.test.tsx b/packages/response-ops/alerts_table/components/error_cell.test.tsx new file mode 100644 index 0000000000000..6c7a7d5677ca1 --- /dev/null +++ b/packages/response-ops/alerts_table/components/error_cell.test.tsx @@ -0,0 +1,28 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ErrorCell } from './error_cell'; + +describe('ErrorCell', () => { + it('should render the error stack in a code block', () => { + const error = new Error('An error occurred'); + error.stack = 'Error: An error occurred\n at :1:1'; + render( + + + + ); + expect(screen.getByRole('code')).toHaveTextContent( + 'Error: An error occurred at :1:1' + ); + }); +}); diff --git a/packages/response-ops/alerts_table/components/error_cell.tsx b/packages/response-ops/alerts_table/components/error_cell.tsx new file mode 100644 index 0000000000000..086563f167bee --- /dev/null +++ b/packages/response-ops/alerts_table/components/error_cell.tsx @@ -0,0 +1,55 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +import React from 'react'; +import { css } from '@emotion/react'; +import { EuiCodeBlock, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +// Here we force the error callout to be the same height as the cell content +// so that the error detail gets hidden in the overflow area and only shown in +// the cell popover +const errorCalloutStyles = css` + height: 1lh; +`; + +/** + * An error callout that displays the error stack in a code block + */ +export const ErrorCell = ({ error }: { error: Error }) => ( + <> + + + + + + + + + + + + + + {error.stack} + +); diff --git a/packages/response-ops/alerts_table/components/error_fallback.test.tsx b/packages/response-ops/alerts_table/components/error_fallback.test.tsx new file mode 100644 index 0000000000000..0f16e059e25b4 --- /dev/null +++ b/packages/response-ops/alerts_table/components/error_fallback.test.tsx @@ -0,0 +1,25 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ErrorFallback } from './error_fallback'; + +describe('ErrorFallback', () => { + it('should render the error message in a code block', () => { + const error = new Error('An error occurred'); + render( + + + + ); + expect(screen.getByRole('code')).toHaveTextContent('An error occurred'); + }); +}); diff --git a/packages/response-ops/alerts_table/components/error_fallback.tsx b/packages/response-ops/alerts_table/components/error_fallback.tsx new file mode 100644 index 0000000000000..3f00851610b1b --- /dev/null +++ b/packages/response-ops/alerts_table/components/error_fallback.tsx @@ -0,0 +1,42 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { EuiButton, EuiCode, EuiCopy, EuiEmptyPrompt } from '@elastic/eui'; +import { FallbackComponent } from './error_boundary'; +import { + ALERTS_TABLE_UNKNOWN_ERROR_COPY_TO_CLIPBOARD_LABEL, + ALERTS_TABLE_UNKNOWN_ERROR_MESSAGE, + ALERTS_TABLE_UNKNOWN_ERROR_TITLE, +} from '../translations'; + +export const ErrorFallback: FallbackComponent = ({ error }) => { + return ( + {ALERTS_TABLE_UNKNOWN_ERROR_TITLE}} + body={ + <> +

{ALERTS_TABLE_UNKNOWN_ERROR_MESSAGE}

+ {error.message && {error.message}} + + } + actions={ + + {(copy) => ( + + {ALERTS_TABLE_UNKNOWN_ERROR_COPY_TO_CLIPBOARD_LABEL} + + )} + + } + /> + ); +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.test.tsx b/packages/response-ops/alerts_table/components/hover_visibility_container.test.tsx similarity index 50% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.test.tsx rename to packages/response-ops/alerts_table/components/hover_visibility_container.test.tsx index 74f65a171c205..8adf0f00f2deb 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.test.tsx +++ b/packages/response-ops/alerts_table/components/hover_visibility_container.test.tsx @@ -1,14 +1,18 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; import { render, screen } from '@testing-library/react'; +import { HoverVisibilityContainer } from './hover_visibility_container'; +import { matchers } from '@emotion/jest'; -import { HoverVisibilityContainer } from '.'; +expect.extend(matchers); describe('HoverVisibilityContainer', () => { const targetClass1 = 'Component1'; @@ -24,13 +28,8 @@ describe('HoverVisibilityContainer', () => { ); - expect(await screen.findByTestId('hoverVisibilityContainer')).toHaveStyleRule('opacity', '0', { - modifier: `.${targetClass1}`, - }); - - expect(await screen.getByTestId(`hoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', { - modifier: `:hover .${targetClass2}`, - }); + expect(getComputedStyle(await screen.findByTestId('component1')).opacity).toBe('0'); + expect(getComputedStyle(await screen.findByTestId('component2')).opacity).toBe('0'); }); test('it renders an opaque inspect button when it has mouse focus', async () => { @@ -40,11 +39,14 @@ describe('HoverVisibilityContainer', () => { ); - expect(await screen.findByTestId('hoverVisibilityContainer')).toHaveStyleRule('opacity', '1', { - modifier: `:hover .${targetClass1}`, + + const hoverVisibilityContainer = await screen.findByTestId('hoverVisibilityContainer'); + + expect(hoverVisibilityContainer).toHaveStyleRule('opacity', '1', { + target: `:hover .${targetClass1}`, }); - expect(await screen.findByTestId('hoverVisibilityContainer')).toHaveStyleRule('opacity', '1', { - modifier: `:hover .${targetClass2}`, + expect(hoverVisibilityContainer).toHaveStyleRule('opacity', '1', { + target: `:hover .${targetClass2}`, }); }); }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx b/packages/response-ops/alerts_table/components/hover_visibility_container.tsx similarity index 73% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx rename to packages/response-ops/alerts_table/components/hover_visibility_container.tsx index e76ac557a50fb..81aef4894fbcd 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx +++ b/packages/response-ops/alerts_table/components/hover_visibility_container.tsx @@ -1,19 +1,21 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React from 'react'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import styled from '@emotion/styled'; import { getOr } from 'lodash/fp'; interface StyledDivProps { targetClassNames: string[]; } -const StyledDiv = euiStyled.div` +const StyledDiv = styled.div` width: 100%; display: flex; flex-grow: 1; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx b/packages/response-ops/alerts_table/components/last_updated_at.tsx similarity index 82% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx rename to packages/response-ops/alerts_table/components/last_updated_at.tsx index 73005464a80c7..5a41ac4adb93e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx +++ b/packages/response-ops/alerts_table/components/last_updated_at.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EuiText, EuiToolTip } from '@elastic/eui'; @@ -10,7 +12,7 @@ import { FormattedRelative } from '@kbn/i18n-react'; import React, { useEffect, useMemo, useState } from 'react'; import { css } from '@emotion/react'; -import * as i18n from './translations'; +import * as i18n from '../translations'; export interface LastUpdatedAtProps { compact?: boolean; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.test.tsx b/packages/response-ops/alerts_table/components/maintenance_windows_cell.test.tsx similarity index 60% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.test.tsx rename to packages/response-ops/alerts_table/components/maintenance_windows_cell.test.tsx index 4bdb064e638d4..a4359c34ccd3e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.test.tsx +++ b/packages/response-ops/alerts_table/components/maintenance_windows_cell.test.tsx @@ -1,56 +1,60 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React from 'react'; import { screen, render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { ALERT_MAINTENANCE_WINDOW_IDS } from '@kbn/rule-data-utils'; -import { MaintenanceWindowCell } from './cell'; +import { MaintenanceWindowsCell } from './maintenance_windows_cell'; import { CellComponentProps } from '../types'; -import { Alert } from '../../../../types'; -import { getMaintenanceWindowsMapMock } from './index.mock'; -import { getCasesMapMock } from '../cases/index.mock'; -import userEvent from '@testing-library/user-event'; +import { getMaintenanceWindowsMapMock } from '../mocks/maintenance_windows.mock'; +import { getCasesMapMock } from '../mocks/cases.mock'; +import { Alert } from '@kbn/alerting-types'; +import { createPartialObjectMock } from '../utils/test'; const casesMap = getCasesMapMock(); const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); -const alert = { +const alert: Alert = { _id: 'alert-id', _index: 'alert-index', [ALERT_MAINTENANCE_WINDOW_IDS]: ['test-mw-id-1', 'test-mw-id-2'], -} as Alert; +}; -const props = { +const props = createPartialObjectMock({ isLoading: false, alert, cases: casesMap, maintenanceWindows: maintenanceWindowsMap, columnId: ALERT_MAINTENANCE_WINDOW_IDS, showAlertStatusWithFlapping: false, -} as CellComponentProps; +}); -describe('MaintenanceWindowCell', () => { +describe('MaintenanceWindowsCell', () => { it('renders the maintenance window cell', async () => { - render(); + render(); expect(screen.getByText('test-title,')).toBeInTheDocument(); }); it('renders the loading skeleton', async () => { - render(); + render(); expect(screen.getByTestId('maintenance-window-cell-loading')).toBeInTheDocument(); }); it('shows the tooltip', async () => { - render(); + render(); expect(screen.getByText('test-title,')).toBeInTheDocument(); await userEvent.hover(screen.getByText('test-title,')); expect(await screen.findByTestId('maintenance-window-tooltip-content')).toBeInTheDocument(); }); it('renders the maintenance window IDs if the endpoint could not be fetched', async () => { - render(); + render(); expect(screen.queryByText('test-title,')).not.toBeInTheDocument(); expect(screen.queryByText('test-title-2')).not.toBeInTheDocument(); expect(screen.getByText('test-mw-id-1,')).toBeInTheDocument(); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx b/packages/response-ops/alerts_table/components/maintenance_windows_cell.tsx similarity index 74% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx rename to packages/response-ops/alerts_table/components/maintenance_windows_cell.tsx index 7a81d8a5ac37a..2e890332968b2 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx +++ b/packages/response-ops/alerts_table/components/maintenance_windows_cell.tsx @@ -1,16 +1,18 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { memo, useMemo } from 'react'; import { EuiSkeletonText, EuiToolTip } from '@elastic/eui'; -import { MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; import { ALERT_MAINTENANCE_WINDOW_IDS, TIMESTAMP } from '@kbn/rule-data-utils'; import { CellComponent } from '../types'; -import { TooltipContent } from './tooltip_content'; +import { TooltipContent } from './maintenance_windows_tooltip_content'; const isMaintenanceWindowValid = (mw: MaintenanceWindow | undefined): mw is MaintenanceWindow => { return !!mw; @@ -63,18 +65,18 @@ export const MaintenanceWindowBaseCell = memo((props: MaintenanceWindowBaseCellP ); }); -export const MaintenanceWindowCell: CellComponent = memo((props) => { +export const MaintenanceWindowsCell: CellComponent = memo((props) => { const { alert, maintenanceWindows, isLoading } = props; const validMaintenanceWindows = useMemo(() => { - const maintenanceWindowIds = (alert && alert[ALERT_MAINTENANCE_WINDOW_IDS]) || []; + const maintenanceWindowIds = (alert && (alert[ALERT_MAINTENANCE_WINDOW_IDS] as string[])) || []; return maintenanceWindowIds .map((id) => maintenanceWindows?.get(id)) .filter(isMaintenanceWindowValid); }, [alert, maintenanceWindows]); const idsWithoutMaintenanceWindow = useMemo(() => { - const maintenanceWindowIds = (alert && alert[ALERT_MAINTENANCE_WINDOW_IDS]) || []; + const maintenanceWindowIds = (alert && (alert[ALERT_MAINTENANCE_WINDOW_IDS] as string[])) || []; return maintenanceWindowIds.filter((id) => !maintenanceWindows?.get(id)); }, [alert, maintenanceWindows]); @@ -87,7 +89,7 @@ export const MaintenanceWindowCell: CellComponent = memo((props) => { maintenanceWindows={validMaintenanceWindows} maintenanceWindowIds={idsWithoutMaintenanceWindow} isLoading={isLoading} - timestamp={alert && alert[TIMESTAMP]?.[0]} + timestamp={alert && (alert[TIMESTAMP]?.[0] as string)} /> ); }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx b/packages/response-ops/alerts_table/components/maintenance_windows_tooltip_content.tsx similarity index 84% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx rename to packages/response-ops/alerts_table/components/maintenance_windows_tooltip_content.tsx index 27c21b879297f..ed91aab1c4ce0 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx +++ b/packages/response-ops/alerts_table/components/maintenance_windows_tooltip_content.tsx @@ -1,16 +1,19 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import React, { memo, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiText, formatDate, EuiHorizontalRule } from '@elastic/eui'; -import { MaintenanceWindow, MAINTENANCE_WINDOW_DATE_FORMAT } from '@kbn/alerting-plugin/common'; +import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; import { css } from '@emotion/react'; +import { MAINTENANCE_WINDOW_DATE_FORMAT } from '../constants'; const START_TIME = i18n.translate( 'xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.startTime', diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mark_as_untracked_alert_action.tsx b/packages/response-ops/alerts_table/components/mark_as_untracked_alert_action.tsx similarity index 54% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mark_as_untracked_alert_action.tsx rename to packages/response-ops/alerts_table/components/mark_as_untracked_alert_action.tsx index 7743af7be7268..66f08e0b4d5ee 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mark_as_untracked_alert_action.tsx +++ b/packages/response-ops/alerts_table/components/mark_as_untracked_alert_action.tsx @@ -1,23 +1,34 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { memo, useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiContextMenuItem } from '@elastic/eui'; import { ALERT_STATUS, ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; -import type { AlertActionsProps } from '../../../../types'; -import { useBulkUntrackAlerts } from '../../../..'; +import type { AdditionalContext, AlertActionsProps } from '../types'; +import { useBulkUntrackAlerts } from '../hooks/use_bulk_untrack_alerts'; +import { typedMemo } from '../utils/react'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; /** * Alerts table row action to mark the selected alert as untracked */ -export const MarkAsUntrackedAlertAction = memo( - ({ alert, refresh, onActionExecuted }: AlertActionsProps) => { - const { mutateAsync: untrackAlerts } = useBulkUntrackAlerts(); +export const MarkAsUntrackedAlertAction = typedMemo( + ({ + alert, + refresh, + onActionExecuted, + }: AlertActionsProps) => { + const { + services: { http, notifications }, + } = useAlertsTableContext(); + const { mutateAsync: untrackAlerts } = useBulkUntrackAlerts({ http, notifications }); const isAlertActive = useMemo(() => alert[ALERT_STATUS]?.[0] === ALERT_STATUS_ACTIVE, [alert]); const handleUntrackAlert = useCallback(async () => { diff --git a/packages/response-ops/alerts_table/components/mute_alert_action.tsx b/packages/response-ops/alerts_table/components/mute_alert_action.tsx new file mode 100644 index 0000000000000..8eedfaa2e653e --- /dev/null +++ b/packages/response-ops/alerts_table/components/mute_alert_action.tsx @@ -0,0 +1,73 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EuiContextMenuItem } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { ALERT_STATUS, ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; +import { useMuteAlertInstance } from '@kbn/response-ops-alerts-apis/hooks/use_mute_alert_instance'; +import { useUnmuteAlertInstance } from '@kbn/response-ops-alerts-apis/hooks/use_unmute_alert_instance'; +import type { AdditionalContext, AlertActionsProps } from '../types'; +import { MUTE, UNMUTE } from '../translations'; +import { useAlertMutedState } from '../hooks/use_alert_muted_state'; +import { typedMemo } from '../utils/react'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; + +/** + * Alerts table row action to mute/unmute the selected alert + */ +export const MuteAlertAction = typedMemo( + ({ + alert, + refresh, + onActionExecuted, + }: AlertActionsProps) => { + const { + services: { http, notifications }, + } = useAlertsTableContext(); + const { isMuted, ruleId, rule, alertInstanceId } = useAlertMutedState(alert); + const { mutateAsync: muteAlert } = useMuteAlertInstance({ http, notifications }); + const { mutateAsync: unmuteAlert } = useUnmuteAlertInstance({ http, notifications }); + const isAlertActive = useMemo(() => alert[ALERT_STATUS]?.[0] === ALERT_STATUS_ACTIVE, [alert]); + + const toggleAlert = useCallback(async () => { + if (ruleId == null || alertInstanceId == null) { + return; + } + if (isMuted) { + await unmuteAlert({ ruleId, alertInstanceId }); + } else { + await muteAlert({ ruleId, alertInstanceId }); + } + onActionExecuted?.(); + refresh(); + }, [alertInstanceId, isMuted, muteAlert, onActionExecuted, refresh, ruleId, unmuteAlert]); + + if ((!isAlertActive && !isMuted) || ruleId == null || alertInstanceId == null) { + return null; + } + + return ( + + {!rule + ? i18n.translate('xpack.triggersActionsUI.alertsTable.loadingMutedState', { + defaultMessage: 'Loading muted state', + }) + : isMuted + ? UNMUTE + : MUTE} + + ); + } +); diff --git a/packages/response-ops/alerts_table/components/non_virtualized_grid_body.test.tsx b/packages/response-ops/alerts_table/components/non_virtualized_grid_body.test.tsx new file mode 100644 index 0000000000000..3c5676462babd --- /dev/null +++ b/packages/response-ops/alerts_table/components/non_virtualized_grid_body.test.tsx @@ -0,0 +1,62 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { CustomGridBodyProps, NonVirtualizedGridBody } from './non_virtualized_grid_body'; +import { mockAlerts } from '../mocks/context.mock'; +import { ALERT_RULE_UUID, ALERT_STATUS } from '@kbn/rule-data-utils'; +import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; + +describe('NonVirtualizedGridBody', () => { + const props: CustomGridBodyProps = { + alerts: mockAlerts, + isLoading: false, + pageIndex: 0, + pageSize: 10, + actualGridStyle: {}, + visibleColumns: [{ id: ALERT_RULE_NAME }, { id: ALERT_RULE_UUID }, { id: ALERT_STATUS }], + Cell: ({ visibleRowIndex, colIndex }) => ( +
+ {visibleRowIndex}:{colIndex} +
+ ), + stripes: false, + }; + + it('should render `alerts.length` rows (<= `pageSize`) when not loading', () => { + render(); + expect(screen.getAllByRole('row')).toHaveLength(mockAlerts.length); + }); + + it('should render `pageSize` rows when loading', () => { + render(); + expect(screen.getAllByRole('row')).toHaveLength(props.pageSize); + }); + + it('should render the provided Cell component for each row', () => { + render(); + mockAlerts.forEach((_a, visibleRowIndex) => { + props.visibleColumns.forEach((_c, colIndex) => { + expect(screen.getByText(`${visibleRowIndex}:${colIndex}`)).toBeInTheDocument(); + }); + }); + }); + + it('should add striped class when stripes are enabled', () => { + render(); + screen.getAllByRole('row').forEach((row, i) => { + if (i % 2 !== 0) { + expect(row).toHaveClass('euiDataGridRow--striped'); + } else { + expect(row).not.toHaveClass('euiDataGridRow--striped'); + } + }); + }); +}); diff --git a/packages/response-ops/alerts_table/components/non_virtualized_grid_body.tsx b/packages/response-ops/alerts_table/components/non_virtualized_grid_body.tsx new file mode 100644 index 0000000000000..9adaface3d5aa --- /dev/null +++ b/packages/response-ops/alerts_table/components/non_virtualized_grid_body.tsx @@ -0,0 +1,74 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { memo } from 'react'; +import styled from '@emotion/styled'; +import { EuiDataGridProps, EuiDataGridStyle } from '@elastic/eui'; +import type { Alert } from '@kbn/alerting-types'; + +const Row = styled.div` + display: flex; + min-width: fit-content; +`; + +export type CustomGridBodyProps = Pick< + Parameters>['0'], + 'Cell' | 'visibleColumns' +> & { + alerts: Alert[]; + isLoading: boolean; + pageIndex: number; + pageSize: number; + actualGridStyle: EuiDataGridStyle; + stripes?: boolean; +}; + +/** + * Renders a non-virtualized grid body with the provided Cell component + */ +export const NonVirtualizedGridBody = memo( + ({ + alerts, + isLoading, + pageIndex, + pageSize, + actualGridStyle, + visibleColumns, + Cell, + stripes, + }: CustomGridBodyProps) => { + return ( + <> + {alerts + .concat(isLoading ? Array.from({ length: pageSize - alerts.length }) : []) + .map((_row, rowIndex) => ( + + {visibleColumns.map((_col, colIndex) => ( + + ))} + + ))} + + ); + } +); diff --git a/packages/response-ops/alerts_table/components/system_cell.test.tsx b/packages/response-ops/alerts_table/components/system_cell.test.tsx new file mode 100644 index 0000000000000..53252441c4854 --- /dev/null +++ b/packages/response-ops/alerts_table/components/system_cell.test.tsx @@ -0,0 +1,74 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { ComponentProps } from 'react'; +import { render, screen } from '@testing-library/react'; +import { SystemCell } from './system_cell'; +import { AdditionalContext, CellComponentProps, RenderContext } from '../types'; +import { getCasesMapMock } from '../mocks/cases.mock'; +import { getMaintenanceWindowsMapMock } from '../mocks/maintenance_windows.mock'; +import { Alert } from '@kbn/alerting-types'; +import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; +import { createPartialObjectMock } from '../utils/test'; +import { ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS, ALERT_STATUS } from '@kbn/rule-data-utils'; +import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; + +const casesMap = getCasesMapMock(); +const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); + +const alert: Alert = { + _id: 'alert-id', + _index: 'alert-index', + [ALERT_STATUS]: ['active'], + [ALERT_CASE_IDS]: ['test-id'], + [ALERT_MAINTENANCE_WINDOW_IDS]: ['test-mw-id-1'], +}; + +const props = createPartialObjectMock({ + isLoading: false, + alert, + cases: casesMap, + maintenanceWindows: maintenanceWindowsMap, + columnId: 'kibana.alert.status', + showAlertStatusWithFlapping: true, +}); + +const context = createPartialObjectMock>({ + services: { + application: applicationServiceMock.createStartContract(), + }, +}); + +const TestComponent = (_props: ComponentProps) => ( + + + +); + +describe('SystemCell', () => { + it('shows the status cell', async () => { + render(); + expect(screen.getByText('Active')).toBeInTheDocument(); + }); + + it('shows the cases cell', async () => { + render(); + expect(screen.getByText('Test case')).toBeInTheDocument(); + }); + + it('shows the maintenance windows cell', async () => { + render(); + expect(screen.getByText('test-title')).toBeInTheDocument(); + }); + + it('shows the cell if the columnId is not registered to the map', async () => { + render(); + expect(screen.getByText('--')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.tsx b/packages/response-ops/alerts_table/components/system_cell.tsx similarity index 61% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.tsx rename to packages/response-ops/alerts_table/components/system_cell.tsx index 2314c96b2482f..ea29ca8987d75 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.tsx +++ b/packages/response-ops/alerts_table/components/system_cell.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { memo, useMemo } from 'react'; @@ -10,8 +12,8 @@ import { ALERT_STATUS, ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS } from '@kbn import type { CellComponent, SystemCellComponentMap, SystemCellId } from '../types'; import { DefaultCell } from './default_cell'; import { AlertLifecycleStatusCell } from './alert_lifecycle_status_cell'; -import { CasesCell } from '../cases/cell'; -import { MaintenanceWindowCell } from '../maintenance_windows/cell'; +import { CasesCell } from './cases_cell'; +import { MaintenanceWindowsCell } from './maintenance_windows_cell'; export const systemCells: SystemCellId[] = [ ALERT_STATUS, @@ -19,13 +21,13 @@ export const systemCells: SystemCellId[] = [ ALERT_MAINTENANCE_WINDOW_IDS, ]; -export const SystemCellFactory: CellComponent = memo((props) => { +export const SystemCell: CellComponent = memo((props) => { const columnId = props.columnId as SystemCellId; const cellComponents: SystemCellComponentMap = useMemo( () => ({ [ALERT_STATUS]: AlertLifecycleStatusCell, [ALERT_CASE_IDS]: CasesCell, - [ALERT_MAINTENANCE_WINDOW_IDS]: MaintenanceWindowCell, + [ALERT_MAINTENANCE_WINDOW_IDS]: MaintenanceWindowsCell, }), [] ); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_alert_details_alert_action.tsx b/packages/response-ops/alerts_table/components/view_alert_details_alert_action.tsx similarity index 61% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_alert_details_alert_action.tsx rename to packages/response-ops/alerts_table/components/view_alert_details_alert_action.tsx index e53b8edee8af1..7323615c505fa 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_alert_details_alert_action.tsx +++ b/packages/response-ops/alerts_table/components/view_alert_details_alert_action.tsx @@ -1,35 +1,40 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { memo } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiContextMenuItem } from '@elastic/eui'; import { ALERT_UUID } from '@kbn/rule-data-utils'; -import { useKibana } from '../../../../common/lib/kibana'; -import type { AlertActionsProps } from '../../../../types'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; +import type { AdditionalContext, AlertActionsProps } from '../types'; +import { typedMemo } from '../utils/react'; /** * Alerts table row action to open the selected alert detail page */ -export const ViewAlertDetailsAlertAction = memo( - ({ +export const ViewAlertDetailsAlertAction = typedMemo( + ({ alert, openAlertInFlyout, onActionExecuted, isAlertDetailsEnabled, resolveAlertPagePath, tableId, - }: AlertActionsProps) => { + }: AlertActionsProps) => { const { - http: { - basePath: { prepend }, + services: { + http: { + basePath: { prepend }, + }, }, - } = useKibana().services; - const alertId = alert[ALERT_UUID]?.[0] ?? null; + } = useAlertsTableContext(); + const alertId = (alert[ALERT_UUID]?.[0] as string) ?? null; const pagePath = alertId && tableId && resolveAlertPagePath?.(alertId, tableId); const linkToAlert = pagePath ? prepend(pagePath) : null; diff --git a/packages/response-ops/alerts_table/components/view_rule_details_alert_action.tsx b/packages/response-ops/alerts_table/components/view_rule_details_alert_action.tsx new file mode 100644 index 0000000000000..d1221aa40d34d --- /dev/null +++ b/packages/response-ops/alerts_table/components/view_rule_details_alert_action.tsx @@ -0,0 +1,55 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiContextMenuItem } from '@elastic/eui'; +import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; +import type { AdditionalContext, AlertActionsProps } from '../types'; +import { typedMemo } from '../utils/react'; + +/** + * Alerts table row action to open the rule to which the selected alert is associated + */ +export const ViewRuleDetailsAlertAction = typedMemo( + ({ + alert, + resolveRulePagePath, + tableId, + }: AlertActionsProps) => { + const { + services: { + http: { + basePath: { prepend }, + }, + }, + } = useAlertsTableContext(); + const ruleId = (alert[ALERT_RULE_UUID]?.[0] as string) ?? null; + const pagePath = ruleId && tableId && resolveRulePagePath?.(ruleId, tableId); + const linkToRule = pagePath ? prepend(pagePath) : null; + + if (!linkToRule) { + return null; + } + + return ( + + {i18n.translate('xpack.triggersActionsUI.alertsTable.viewRuleDetails', { + defaultMessage: 'View rule details', + })} + + ); + } +); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx b/packages/response-ops/alerts_table/configuration.ts similarity index 87% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx rename to packages/response-ops/alerts_table/configuration.ts index 8bd49a0f0e447..75e75785833c9 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/configuration.tsx +++ b/packages/response-ops/alerts_table/configuration.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { @@ -20,7 +22,7 @@ import { } from '@kbn/rule-data-utils'; import { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; -import { FEATURE_LABEL } from '../translations'; +import { FEATURE_LABEL } from './translations'; const columns = [ { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/constants.ts b/packages/response-ops/alerts_table/constants.ts similarity index 64% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/constants.ts rename to packages/response-ops/alerts_table/constants.ts index f119dca0fe1cc..d6b89eb64d5a6 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/constants.ts +++ b/packages/response-ops/alerts_table/constants.ts @@ -1,12 +1,15 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import { AlertConsumers } from '@kbn/rule-data-utils'; +import type { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; import { APM_DISPLAY_NAME, INFRASTRUCTURE_DISPLAY_NAME, @@ -18,7 +21,7 @@ import { STACK_DISPLAY_NAME, STACK_MONITORING_DISPLAY_NAME, UPTIME_DISPLAY_NAME, -} from '../translations'; +} from './translations'; import { AlertsTableSupportedConsumers } from './types'; interface AlertProducerData { @@ -99,6 +102,36 @@ export const alertProducersData: Record [queryKeys.root, 'alerts'] as const, + cases: () => [queryKeys.root, 'cases'] as const, + casesBulkGet: (caseIds: string[]) => [...queryKeys.cases(), 'bulkGet', caseIds] as const, + maintenanceWindows: () => [queryKeys.root, 'maintenanceWindows'] as const, + maintenanceWindowsBulkGet: (maintenanceWindowIds: string[]) => [ + ...queryKeys.maintenanceWindows(), + maintenanceWindowIds, + ], +}; + +export const mutationKeys = { + root: 'alertsTable', + bulkUntrackAlerts: () => [mutationKeys.root, 'bulkUntrackAlerts'] as const, + bulkUntrackAlertsByQuery: () => [mutationKeys.root, 'bulkUntrackAlertsByQuery'] as const, +}; + +export const INTERNAL_BASE_ALERTING_API_PATH = '/internal/alerting' as const; +export const INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH = + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window` as const; +export const MAINTENANCE_WINDOW_DATE_FORMAT = 'MM/DD/YY hh:mm A'; export const CELL_ACTIONS_POPOVER_TEST_ID = 'euiDataGridExpansionPopover'; export const CELL_ACTIONS_EXPAND_TEST_ID = 'euiDataGridCellExpandButton'; export const FIELD_BROWSER_TEST_ID = 'fields-browser-container'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/contexts/alerts_table_context.tsx b/packages/response-ops/alerts_table/contexts/alerts_table_context.tsx similarity index 58% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/contexts/alerts_table_context.tsx rename to packages/response-ops/alerts_table/contexts/alerts_table_context.tsx index 2e4863de95413..9f089f2f51c7e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/contexts/alerts_table_context.tsx +++ b/packages/response-ops/alerts_table/contexts/alerts_table_context.tsx @@ -1,13 +1,15 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { PropsWithChildren, createContext, useContext } from 'react'; -import { typedMemo } from '../utils'; -import { AdditionalContext, RenderContext } from '../../../../types'; +import { typedMemo } from '../utils/react'; +import { AdditionalContext, RenderContext } from '../types'; const AlertsTableContext = createContext({}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx b/packages/response-ops/alerts_table/hooks/toggle_column.test.tsx similarity index 81% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx rename to packages/response-ops/alerts_table/hooks/toggle_column.test.tsx index df1f2b79c5eb0..df8b2a1a8195f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.test.tsx +++ b/packages/response-ops/alerts_table/hooks/toggle_column.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { ALERT_CASE_IDS } from '@kbn/rule-data-utils'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts b/packages/response-ops/alerts_table/hooks/toggle_column.ts similarity index 87% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts rename to packages/response-ops/alerts_table/hooks/toggle_column.ts index 4ad8a4694e997..a7ac433ae6970 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/toggle_column.ts +++ b/packages/response-ops/alerts_table/hooks/toggle_column.ts @@ -1,13 +1,15 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { EuiDataGridColumn } from '@elastic/eui'; import { ALERT_CASE_IDS, ALERT_MAINTENANCE_WINDOW_IDS } from '@kbn/rule-data-utils'; -import * as i18n from '../../translations'; +import * as i18n from '../translations'; const remove = ({ columns, index }: { columns: EuiDataGridColumn[]; index: number }) => { return [...columns.slice(0, index), ...columns.slice(index + 1)]; @@ -88,7 +90,6 @@ export const toggleColumn = ({ const currentIndex = columns.findIndex( (currentColumn: EuiDataGridColumn) => currentColumn.id === column.id ); - const isVisible = currentIndex >= 0; /** diff --git a/packages/response-ops/alerts_table/hooks/use_alert_muted_state.ts b/packages/response-ops/alerts_table/hooks/use_alert_muted_state.ts new file mode 100644 index 0000000000000..dada59bcf06da --- /dev/null +++ b/packages/response-ops/alerts_table/hooks/use_alert_muted_state.ts @@ -0,0 +1,28 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { useMemo } from 'react'; +import { ALERT_INSTANCE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; +import type { Alert } from '@kbn/alerting-types'; +import { useAlertsTableContext } from '../contexts/alerts_table_context'; + +export const useAlertMutedState = (alert?: Alert) => { + const { mutedAlerts } = useAlertsTableContext(); + const alertInstanceId = alert && (alert[ALERT_INSTANCE_ID]?.[0] as string); + const ruleId = alert && (alert[ALERT_RULE_UUID]?.[0] as string); + return useMemo(() => { + const rule = ruleId ? mutedAlerts?.[ruleId] ?? [] : []; + return { + isMuted: alertInstanceId ? rule?.includes(alertInstanceId) : null, + ruleId, + rule, + alertInstanceId, + }; + }, [alertInstanceId, mutedAlerts, ruleId]); +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_actions.test.tsx b/packages/response-ops/alerts_table/hooks/use_bulk_actions.test.tsx similarity index 61% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_actions.test.tsx rename to packages/response-ops/alerts_table/hooks/use_bulk_actions.test.tsx index 7baf89c0fcdab..695b06c6a9e00 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_actions.test.tsx +++ b/packages/response-ops/alerts_table/hooks/use_bulk_actions.test.tsx @@ -1,51 +1,57 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ +import React, { PropsWithChildren } from 'react'; import { renderHook } from '@testing-library/react'; -import { useBulkActions, useBulkAddToCaseActions, useBulkUntrackActions } from './use_bulk_actions'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { createCasesServiceMock } from '../index.mock'; -import { AdditionalContext, BulkActionsVerbs, RenderContext } from '../../../../types'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import { useBulkActions, useBulkAddToCaseActions, useBulkUntrackActions } from './use_bulk_actions'; +import { createCasesServiceMock } from '../mocks/cases.mock'; +import { BulkActionsVerbs, type PublicAlertsDataGridProps } from '../types'; +import { AdditionalContext, RenderContext } from '../types'; import { useAlertsTableContext } from '../contexts/alerts_table_context'; +import { createPartialObjectMock, testQueryClientConfig } from '../utils/test'; +import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; -jest.mock('./apis/bulk_get_cases'); -jest.mock('../../../../common/lib/kibana'); - -const mockCaseService = createCasesServiceMock(); -const mockKibana = jest.fn().mockReturnValue({ - services: { - cases: mockCaseService, - }, -}); - -jest.mock('@kbn/kibana-react-plugin/public', () => { - const original = jest.requireActual('@kbn/kibana-react-plugin/public'); - - return { - ...original, - useKibana: () => mockKibana(), - }; -}); - +jest.mock('../apis/bulk_get_cases'); jest.mock('../contexts/alerts_table_context'); -jest.mocked(useAlertsTableContext).mockReturnValue({ - bulkActionsStore: [{}, jest.fn()], -} as unknown as RenderContext); +jest.mocked(useAlertsTableContext).mockReturnValue( + createPartialObjectMock>({ + bulkActionsStore: [{}, jest.fn()], + }) +); + +const mockCasesService = createCasesServiceMock(); +const http = httpServiceMock.createStartContract(); +const notifications = notificationServiceMock.createStartContract(); +const application = applicationServiceMock.createStartContract(); +application.capabilities = { ...application.capabilities, infrastructure: { show: true } }; +const queryClient = new QueryClient(testQueryClientConfig); +const wrapper = ({ children }: PropsWithChildren) => { + return ( + + {children} + + ); +}; const caseId = 'test-case'; +const casesConfig: PublicAlertsDataGridProps['casesConfiguration'] = { + featureId: 'test-feature-id', + owner: ['cases'], +}; describe('bulk action hooks', () => { - const casesConfig = { featureId: 'test-feature-id', owner: ['test-owner'] }; - let appMockRender: AppMockRenderer; - beforeEach(() => { jest.clearAllMocks(); - appMockRender = createAppMockRenderer({ queryClientContext: AlertsQueryContext }); }); const refresh = jest.fn(); @@ -57,17 +63,19 @@ describe('bulk action hooks', () => { getAttachments({ theCase: { id: caseId } }); }); - mockCaseService.helpers.canUseCases = jest.fn().mockReturnValue({ create: true, read: true }); - mockCaseService.ui.getCasesContext = jest.fn().mockReturnValue(() => 'Cases context'); + mockCasesService.helpers.canUseCases = jest.fn().mockReturnValue({ create: true, read: true }); + mockCasesService.ui.getCasesContext = jest.fn().mockReturnValue(() => 'Cases context'); - const mockAddNewCase = mockCaseService.hooks.useCasesAddToNewCaseFlyout.mockReturnValue({ + const mockAddNewCase = mockCasesService.hooks.useCasesAddToNewCaseFlyout.mockReturnValue({ open: mockOpenNewCase, close: jest.fn(), }); - const mockAddExistingCase = mockCaseService.hooks.useCasesAddToExistingCaseModal.mockReturnValue({ - open: mockOpenExistingCase, - close: jest.fn(), - }); + const mockAddExistingCase = mockCasesService.hooks.useCasesAddToExistingCaseModal.mockReturnValue( + { + open: mockOpenExistingCase, + close: jest.fn(), + } + ); describe('useBulkAddToCaseActions', () => { beforeEach(() => { @@ -75,27 +83,60 @@ describe('bulk action hooks', () => { }); it('should refetch when calling onSuccess of useCasesAddToNewCaseFlyout', async () => { - renderHook(() => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), { - wrapper: appMockRender.AppWrapper, - }); + renderHook( + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), + { + wrapper, + } + ); mockAddNewCase.mock.calls[0][0]!.onSuccess(); expect(refresh).toHaveBeenCalled(); }); it('should refetch when calling onSuccess of useCasesAddToExistingCaseModal', async () => { - renderHook(() => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), { - wrapper: appMockRender.AppWrapper, - }); + renderHook( + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), + { + wrapper, + } + ); mockAddExistingCase.mock.calls[0][0]!.onSuccess(); expect(refresh).toHaveBeenCalled(); }); it('should useCasesAddToExistingCaseModal with correct toaster params', async () => { - renderHook(() => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), { - wrapper: appMockRender.AppWrapper, - }); + renderHook( + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), + { + wrapper, + } + ); expect(mockAddExistingCase).toHaveBeenCalledWith({ noAttachmentsToaster: { @@ -108,31 +149,47 @@ describe('bulk action hooks', () => { it('should open the case flyout', async () => { const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); // @ts-expect-error: cases do not need all arguments result.current[0].onClick([]); - expect(mockCaseService.helpers.groupAlertsByRule).toHaveBeenCalled(); + expect(mockCasesService.helpers.groupAlertsByRule).toHaveBeenCalled(); expect(mockOpenNewCase).toHaveBeenCalled(); }); it('should open the case modal', async () => { const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); // @ts-expect-error: cases do not need all arguments result.current[1].onClick([]); - expect(mockCaseService.helpers.groupAlertsByRule).toHaveBeenCalled(); + expect(mockCasesService.helpers.groupAlertsByRule).toHaveBeenCalled(); expect(mockOpenExistingCase).toHaveBeenCalled(); }); @@ -173,23 +230,39 @@ describe('bulk action hooks', () => { ]; const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); // @ts-expect-error: cases do not need all arguments result.current[1].onClick(alerts); - expect(mockCaseService.helpers.groupAlertsByRule).toHaveBeenCalledWith(alerts); + expect(mockCasesService.helpers.groupAlertsByRule).toHaveBeenCalledWith(alerts); }); it('should remove alerts that are already attached to the case', async () => { const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -225,7 +298,7 @@ describe('bulk action hooks', () => { }, ]); - expect(mockCaseService.helpers.groupAlertsByRule).toHaveBeenCalledWith([ + expect(mockCasesService.helpers.groupAlertsByRule).toHaveBeenCalledWith([ { _id: 'alert1', _index: 'idx1', @@ -244,14 +317,22 @@ describe('bulk action hooks', () => { }); it('should not show the bulk actions when the user does not have write access', async () => { - mockCaseService.helpers.canUseCases = jest + mockCasesService.helpers.canUseCases = jest .fn() .mockReturnValue({ create: false, read: true }); const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -259,14 +340,22 @@ describe('bulk action hooks', () => { }); it('should not show the bulk actions when the user does not have read access', async () => { - mockCaseService.helpers.canUseCases = jest + mockCasesService.helpers.canUseCases = jest .fn() .mockReturnValue({ create: true, read: false }); const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -274,30 +363,56 @@ describe('bulk action hooks', () => { }); it('should call canUseCases with an empty owner when casesConfig is missing', async () => { - renderHook(() => useBulkAddToCaseActions({ refresh, clearSelection }), { - wrapper: appMockRender.AppWrapper, - }); + renderHook( + () => + useBulkAddToCaseActions({ + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), + { + wrapper, + } + ); - expect(mockCaseService.helpers.canUseCases).toHaveBeenCalledWith([]); + expect(mockCasesService.helpers.canUseCases).toHaveBeenCalledWith([]); }); it('should not show the bulk actions when the cases context is missing', async () => { - mockCaseService.ui.getCasesContext = jest.fn().mockReturnValue(() => null); + mockCasesService.ui.getCasesContext = jest.fn().mockReturnValue(() => null); - const { result } = renderHook(() => useBulkAddToCaseActions({ refresh, clearSelection }), { - wrapper: appMockRender.AppWrapper, - }); + const { result } = renderHook( + () => + useBulkAddToCaseActions({ + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), + { + wrapper, + } + ); expect(result.current.length).toBe(0); }); it('should not show the bulk actions when the case service is not available', async () => { - mockKibana.mockImplementation(() => ({ services: {} })); - const { result } = renderHook( - () => useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }), + () => + useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + http, + notifications, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -310,11 +425,6 @@ describe('bulk action hooks', () => { jest.clearAllMocks(); }); it('should not show the bulk actions when the user lacks any observability permissions', () => { - mockKibana.mockImplementation(() => ({ - services: { - application: { capabilities: {} }, - }, - })); const { result } = renderHook( () => useBulkUntrackActions({ @@ -331,9 +441,16 @@ describe('bulk action hooks', () => { }, }, }, + http, + notifications, + application: { + ...application, + // Force no permissions + capabilities: {} as unknown as typeof application.capabilities, + }, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -344,20 +461,26 @@ describe('bulk action hooks', () => { describe('useBulkActions', () => { beforeEach(() => { jest.clearAllMocks(); - mockKibana.mockImplementation(() => ({ - services: { - cases: mockCaseService, - application: { capabilities: { infrastructure: { show: true } } }, - }, - })); - mockCaseService.helpers.canUseCases = jest.fn().mockReturnValue({ create: true, read: true }); + mockCasesService.helpers.canUseCases = jest + .fn() + .mockReturnValue({ create: true, read: true }); }); it('appends the case and untrack bulk actions', async () => { const { result } = renderHook( - () => useBulkActions({ alertsCount: 0, query: {}, casesConfig, refresh }), + () => + useBulkActions({ + alertsCount: 0, + query: {}, + casesConfig, + refresh, + http, + notifications, + application, + casesService: mockCasesService, + }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -405,9 +528,13 @@ describe('bulk action hooks', () => { casesConfig, refresh, ruleTypeIds: ['siem.esqlRule'], + http, + notifications, + application, + casesService: mockCasesService, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -465,9 +592,12 @@ describe('bulk action hooks', () => { casesConfig, refresh, getBulkActions: useBulkActionsConfig, + http, + notifications, + application, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); const initialBulkActions = result.current.bulkActions[0].items @@ -496,9 +626,12 @@ describe('bulk action hooks', () => { casesConfig, refresh, ruleTypeIds: ['observability'], + http, + notifications, + application, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -513,9 +646,12 @@ describe('bulk action hooks', () => { refresh, ruleTypeIds: ['observability'], hideBulkActions: true, + http, + notifications, + application, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_actions.ts b/packages/response-ops/alerts_table/hooks/use_bulk_actions.ts similarity index 84% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_actions.ts rename to packages/response-ops/alerts_table/hooks/use_bulk_actions.ts index ee9d9afe2b505..5321aa6b865e9 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_actions.ts +++ b/packages/response-ops/alerts_table/hooks/use_bulk_actions.ts @@ -1,31 +1,35 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { useCallback, useEffect, useMemo } from 'react'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ALERT_CASE_IDS, isSiemRuleType } from '@kbn/rule-data-utils'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import type { ApplicationStart } from '@kbn/core-application-browser'; import { useAlertsTableContext } from '../contexts/alerts_table_context'; -import { +import type { BulkActionsConfig, BulkActionsPanelConfig, BulkActionsState, - BulkActionsVerbs, BulkActionsReducerAction, - PublicAlertsDataGridProps, -} from '../../../../types'; -import { CasesService } from '../types'; + TimelineItem, +} from '../types'; +import { BulkActionsVerbs } from '../types'; +import type { CasesService, PublicAlertsDataGridProps } from '../types'; import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE, ALERTS_ALREADY_ATTACHED_TO_CASE, MARK_AS_UNTRACKED, NO_ALERTS_ADDED_TO_CASE, -} from './translations'; -import { TimelineItem } from '../bulk_actions/components/toolbar'; +} from '../translations'; import { useBulkUntrackAlerts } from './use_bulk_untrack_alerts'; import { useBulkUntrackAlertsByQuery } from './use_bulk_untrack_alerts_by_query'; @@ -37,6 +41,10 @@ interface BulkActionsProps { getBulkActions?: PublicAlertsDataGridProps['getBulkActions']; refresh: () => void; hideBulkActions?: boolean; + application: ApplicationStart; + casesService?: CasesService; + http: HttpStart; + notifications: NotificationsStart; } export interface UseBulkActions { @@ -48,10 +56,16 @@ export interface UseBulkActions { updateBulkActionsState: React.Dispatch; } -type UseBulkAddToCaseActionsProps = Pick & +type UseBulkAddToCaseActionsProps = Pick< + BulkActionsProps, + 'casesConfig' | 'refresh' | 'casesService' | 'http' | 'notifications' +> & Pick; -type UseBulkUntrackActionsProps = Pick & +type UseBulkUntrackActionsProps = Pick< + BulkActionsProps, + 'refresh' | 'query' | 'ruleTypeIds' | 'application' | 'http' | 'notifications' +> & Pick & { isAllSelected: boolean; }; @@ -97,12 +111,11 @@ const addItemsToInitialPanel = ({ }; export const useBulkAddToCaseActions = ({ + casesService, casesConfig, refresh, clearSelection, }: UseBulkAddToCaseActionsProps): BulkActionsConfig[] => { - const { cases: casesService } = useKibana<{ cases?: CasesService }>().services; - const userCasesPermissions = useMemo(() => { return casesService?.helpers.canUseCases(casesConfig?.owner ?? []); }, [casesConfig?.owner, casesService]); @@ -187,15 +200,19 @@ export const useBulkUntrackActions = ({ query, ruleTypeIds = [], isAllSelected, + http, + notifications, + application, }: UseBulkUntrackActionsProps) => { const onSuccess = useCallback(() => { refresh(); clearSelection(); }, [clearSelection, refresh]); - - const { application } = useKibana().services; - const { mutateAsync: untrackAlerts } = useBulkUntrackAlerts(); - const { mutateAsync: untrackAlertsByQuery } = useBulkUntrackAlertsByQuery(); + const { mutateAsync: untrackAlerts } = useBulkUntrackAlerts({ http, notifications }); + const { mutateAsync: untrackAlertsByQuery } = useBulkUntrackAlertsByQuery({ + http, + notifications, + }); const hasApmPermission = application?.capabilities.apm?.['alerting:show']; const hasInfrastructurePermission = application?.capabilities.infrastructure?.show; @@ -265,14 +282,20 @@ export const useBulkUntrackActions = ({ ]); }; +const EMPTY_BULK_ACTIONS_CONFIG = () => []; + export function useBulkActions({ alertsCount, casesConfig, query, refresh, - getBulkActions = () => [], + getBulkActions = EMPTY_BULK_ACTIONS_CONFIG, ruleTypeIds, hideBulkActions, + http, + notifications, + application, + casesService, }: BulkActionsProps): UseBulkActions { const { bulkActionsStore: [bulkActionsState, updateBulkActionsState], @@ -288,14 +311,24 @@ export function useBulkActions({ }, [updateBulkActionsState] ); - const caseBulkActions = useBulkAddToCaseActions({ casesConfig, refresh, clearSelection }); + const caseBulkActions = useBulkAddToCaseActions({ + casesConfig, + refresh, + clearSelection, + casesService, + http, + notifications, + }); const untrackBulkActions = useBulkUntrackActions({ + application, setIsBulkActionsLoading, refresh, clearSelection, query, ruleTypeIds, isAllSelected: bulkActionsState.isAllSelected, + http, + notifications, }); const initialItems = useMemo(() => { diff --git a/packages/response-ops/alerts_table/hooks/use_bulk_get_cases.test.tsx b/packages/response-ops/alerts_table/hooks/use_bulk_get_cases.test.tsx new file mode 100644 index 0000000000000..751584fa8a21b --- /dev/null +++ b/packages/response-ops/alerts_table/hooks/use_bulk_get_cases.test.tsx @@ -0,0 +1,95 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { renderHook, waitFor } from '@testing-library/react'; +import * as api from '../apis/bulk_get_cases'; +import { useBulkGetCasesQuery } from './use_bulk_get_cases'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { testQueryClientConfig } from '../utils/test'; +import React, { PropsWithChildren } from 'react'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; + +jest.mock('../apis/bulk_get_cases'); + +const response = { + cases: [], + errors: [], +}; + +const http = httpServiceMock.createStartContract(); +const notifications = notificationServiceMock.createStartContract(); +const queryClient = new QueryClient(testQueryClientConfig); + +const wrapper = ({ children }: PropsWithChildren) => { + return ( + + {children} + + ); +}; + +describe('useBulkGetCasesQuery', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls the api when invoked with the correct parameters', async () => { + const spy = jest.spyOn(api, 'bulkGetCases'); + spy.mockResolvedValue(response); + + renderHook(() => useBulkGetCasesQuery({ caseIds: ['case-1'], http, notifications }), { + wrapper, + }); + + await waitFor(() => + expect(spy).toHaveBeenCalledWith( + expect.anything(), + { + ids: ['case-1'], + }, + expect.any(AbortSignal) + ) + ); + }); + + it('does not call the api if the fetchCases is false', async () => { + const spy = jest.spyOn(api, 'bulkGetCases'); + spy.mockResolvedValue(response); + + renderHook( + () => useBulkGetCasesQuery({ caseIds: ['case-1'], http, notifications }, { enabled: false }), + { + wrapper, + } + ); + + await waitFor(() => expect(spy).not.toHaveBeenCalled()); + }); + + it('shows a toast error when the api return an error', async () => { + const spy = jest.spyOn(api, 'bulkGetCases').mockRejectedValue(new Error('An error')); + + renderHook(() => useBulkGetCasesQuery({ caseIds: ['case-1'], http, notifications }), { + wrapper, + }); + + await waitFor(() => { + expect(spy).toHaveBeenCalledWith( + expect.anything(), + { + ids: ['case-1'], + }, + expect.any(AbortSignal) + ); + expect(notifications.toasts.addError).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.tsx b/packages/response-ops/alerts_table/hooks/use_bulk_get_cases.tsx similarity index 61% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.tsx rename to packages/response-ops/alerts_table/hooks/use_bulk_get_cases.tsx index 7de74949f6df2..baca62c1564f3 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.tsx +++ b/packages/response-ops/alerts_table/hooks/use_bulk_get_cases.tsx @@ -1,18 +1,21 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; import { QueryOptionsOverrides } from '@kbn/alerts-ui-shared/src/common/types/tanstack_query_utility_types'; -import { useKibana } from '../../../../common'; -import { triggersActionsUiQueriesKeys } from '../../../hooks/constants'; -import { ServerError } from '../types'; -import { bulkGetCases, Case, CasesBulkGetResponse } from './apis/bulk_get_cases'; +import { ServerError } from '@kbn/response-ops-alerts-apis/types'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { queryKeys } from '../constants'; +import { bulkGetCases, Case, CasesBulkGetResponse } from '../apis/bulk_get_cases'; const ERROR_TITLE = i18n.translate('xpack.triggersActionsUI.cases.api.bulkGet', { defaultMessage: 'Error fetching cases data', @@ -28,19 +31,16 @@ const transformCases = (data: CasesBulkGetResponse): Map => { export interface UseBulkGetCasesQueryParams { caseIds: string[]; + http: HttpStart; + notifications: NotificationsStart; } export const useBulkGetCasesQuery = ( - { caseIds }: UseBulkGetCasesQueryParams, + { caseIds, http, notifications: { toasts } }: UseBulkGetCasesQueryParams, options?: Pick, 'enabled'> ) => { - const { - http, - notifications: { toasts }, - } = useKibana().services; - return useQuery({ - queryKey: triggersActionsUiQueriesKeys.casesBulkGet(caseIds), + queryKey: queryKeys.casesBulkGet(caseIds), queryFn: ({ signal }) => bulkGetCases(http, { ids: caseIds }, signal), context: AlertsQueryContext, enabled: caseIds.length > 0 && options?.enabled !== false, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.test.ts b/packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.test.tsx similarity index 67% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.test.ts rename to packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.test.tsx index b7b4ded0f0a53..62f88d4238bda 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.test.ts +++ b/packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.test.tsx @@ -1,35 +1,30 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { waitFor, renderHook } from '@testing-library/react'; import { MaintenanceWindowStatus } from '@kbn/alerting-plugin/common'; -import * as api from './apis/bulk_get_maintenance_windows'; +import * as api from '../apis/bulk_get_maintenance_windows'; import { coreMock } from '@kbn/core/public/mocks'; -import type { Capabilities } from '@kbn/core/public'; -import { useKibana } from '../../../../common/lib/kibana'; import { useBulkGetMaintenanceWindowsQuery } from './use_bulk_get_maintenance_windows'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { useLicense } from '../../../hooks/use_license'; -import { createStartServicesMock } from '../../../../common/lib/kibana/kibana_react.mock'; - -const mockUseKibanaReturnValue = createStartServicesMock(); -jest.mock('../../../../common/lib/kibana', () => ({ - __esModule: true, - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); -jest.mock('../../../hooks/use_license'); -jest.mock('./apis/bulk_get_maintenance_windows'); - -const useKibanaMock = useKibana as jest.Mocked; +import { useLicense } from './use_license'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import React, { PropsWithChildren } from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { testQueryClientConfig } from '../utils/test'; + +jest.mock('./use_license'); +jest.mock('../apis/bulk_get_maintenance_windows'); + const useLicenseMock = useLicense as jest.Mock; -const mocks = coreMock.createSetup(); +const { http, notifications, application } = coreMock.createStart(); +const licensing = licensingMock.createStart(); const mockMaintenanceWindow = { id: 'test-id', @@ -67,28 +62,25 @@ const response = { errors: [], }; -let capabilities: Capabilities; +const queryClient = new QueryClient(testQueryClientConfig); -describe('useBulkGetMaintenanceWindows', () => { - let addErrorMock: jest.Mock; - let appMockRender: AppMockRenderer; +const wrapper = ({ children }: PropsWithChildren) => { + return {children}; +}; - beforeAll(async () => { - const services = await mocks.getStartServices(); - capabilities = services[0].application.capabilities; - }); +describe('useBulkGetMaintenanceWindowsQuery', () => { + let addErrorMock: jest.Mock; beforeEach(async () => { jest.clearAllMocks(); - addErrorMock = useKibana().services.notifications.toasts.addError as jest.Mock; - useKibanaMock().services.application.capabilities = { - ...capabilities, + addErrorMock = notifications.toasts.addError as jest.Mock; + application.capabilities = { + ...application.capabilities, maintenanceWindow: { show: true, }, }; useLicenseMock.mockReturnValue({ isAtLeastPlatinum: () => true }); - appMockRender = createAppMockRenderer(); }); it('calls the api when invoked with the correct parameters', async () => { @@ -99,9 +91,13 @@ describe('useBulkGetMaintenanceWindows', () => { () => useBulkGetMaintenanceWindowsQuery({ ids: ['test-id'], + http, + notifications, + application, + licensing, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -122,13 +118,17 @@ describe('useBulkGetMaintenanceWindows', () => { useBulkGetMaintenanceWindowsQuery( { ids: ['test-id'], + http, + notifications, + application, + licensing, }, { enabled: false, } ), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -145,9 +145,13 @@ describe('useBulkGetMaintenanceWindows', () => { () => useBulkGetMaintenanceWindowsQuery({ ids: ['test-id'], + http, + notifications, + application, + licensing, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -155,8 +159,8 @@ describe('useBulkGetMaintenanceWindows', () => { }); it('does not call the api if capabilities are not adequate', async () => { - useKibanaMock().services.application.capabilities = { - ...capabilities, + application.capabilities = { + ...application.capabilities, maintenanceWindow: { show: false, }, @@ -169,9 +173,13 @@ describe('useBulkGetMaintenanceWindows', () => { () => useBulkGetMaintenanceWindowsQuery({ ids: ['test-id'], + http, + notifications, + application, + licensing, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); @@ -187,9 +195,13 @@ describe('useBulkGetMaintenanceWindows', () => { () => useBulkGetMaintenanceWindowsQuery({ ids: ['test-id'], + http, + notifications, + application, + licensing, }), { - wrapper: appMockRender.AppWrapper, + wrapper, } ); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx b/packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx similarity index 59% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx rename to packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx index f1f23e91308b3..42e370107aa42 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx +++ b/packages/response-ops/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx @@ -1,22 +1,27 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; -import { MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; import { QueryOptionsOverrides } from '@kbn/alerts-ui-shared/src/common/types/tanstack_query_utility_types'; -import { useKibana } from '../../../../common/lib/kibana'; -import { ServerError } from '../types'; -import { useLicense } from '../../../hooks/use_license'; -import { triggersActionsUiQueriesKeys } from '../../../hooks/constants'; +import { ServerError } from '@kbn/response-ops-alerts-apis/types'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { ApplicationStart } from '@kbn/core-application-browser'; +import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { bulkGetMaintenanceWindows, BulkGetMaintenanceWindowsResult, -} from './apis/bulk_get_maintenance_windows'; +} from '../apis/bulk_get_maintenance_windows'; +import { queryKeys } from '../constants'; +import { useLicense } from './use_license'; const ERROR_TITLE = i18n.translate( 'xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle', @@ -39,16 +44,15 @@ const transformMaintenanceWindows = ( interface UseBulkGetMaintenanceWindowsQueryParams { ids: string[]; + http: HttpStart; + notifications: NotificationsStart; + application: ApplicationStart; + licensing: LicensingPluginStart; } export const useBulkGetMaintenanceWindowsQuery = ( - { ids }: UseBulkGetMaintenanceWindowsQueryParams, { - enabled, - context, - }: Pick, 'enabled' | 'context'> = {} -) => { - const { + ids, http, notifications: { toasts }, application: { @@ -56,13 +60,18 @@ export const useBulkGetMaintenanceWindowsQuery = ( maintenanceWindow: { show }, }, }, - } = useKibana().services; - - const { isAtLeastPlatinum } = useLicense(); + licensing, + }: UseBulkGetMaintenanceWindowsQueryParams, + { + enabled, + context, + }: Pick, 'enabled' | 'context'> = {} +) => { + const { isAtLeastPlatinum } = useLicense({ licensing }); const hasLicense = isAtLeastPlatinum(); return useQuery({ - queryKey: triggersActionsUiQueriesKeys.maintenanceWindowsBulkGet(ids), + queryKey: queryKeys.maintenanceWindowsBulkGet(ids), queryFn: () => bulkGetMaintenanceWindows({ http, ids }), select: transformMaintenanceWindows, onError: (error) => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts.tsx b/packages/response-ops/alerts_table/hooks/use_bulk_untrack_alerts.tsx similarity index 62% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts.tsx rename to packages/response-ops/alerts_table/hooks/use_bulk_untrack_alerts.tsx index a75f1b3dbfc04..bfa5ade5d1ce2 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts.tsx +++ b/packages/response-ops/alerts_table/hooks/use_bulk_untrack_alerts.tsx @@ -1,24 +1,30 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { i18n } from '@kbn/i18n'; import { useMutation } from '@tanstack/react-query'; -import { INTERNAL_BASE_ALERTING_API_PATH } from '@kbn/alerting-plugin/common'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import { useKibana } from '../../../../common'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { INTERNAL_BASE_ALERTING_API_PATH, mutationKeys } from '../constants'; -export const useBulkUntrackAlerts = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; +export interface UseBulkUntrackAlertsParams { + http: HttpStart; + notifications: NotificationsStart; +} - const untrackAlerts = useMutation( - ['untrackAlerts'], +export const useBulkUntrackAlerts = ({ + http, + notifications: { toasts }, +}: UseBulkUntrackAlertsParams) => { + return useMutation( + mutationKeys.bulkUntrackAlerts(), ({ indices, alertUuids }) => { try { const body = JSON.stringify({ @@ -57,6 +63,4 @@ export const useBulkUntrackAlerts = () => { }, } ); - - return untrackAlerts; }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx b/packages/response-ops/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx similarity index 60% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx rename to packages/response-ops/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx index 88c878aa47a66..fb9c25485d141 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx +++ b/packages/response-ops/alerts_table/hooks/use_bulk_untrack_alerts_by_query.tsx @@ -1,29 +1,35 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { i18n } from '@kbn/i18n'; import { useMutation } from '@tanstack/react-query'; -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { INTERNAL_BASE_ALERTING_API_PATH } from '@kbn/alerting-plugin/common'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import { useKibana } from '../../../../common'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import { INTERNAL_BASE_ALERTING_API_PATH, mutationKeys } from '../constants'; -export const useBulkUntrackAlertsByQuery = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; +export interface UseBulkUntrackAlertsByQueryParams { + http: HttpStart; + notifications: NotificationsStart; +} - const untrackAlertsByQuery = useMutation< +export const useBulkUntrackAlertsByQuery = ({ + http, + notifications: { toasts }, +}: UseBulkUntrackAlertsByQueryParams) => { + return useMutation< string, string, { query: Pick; ruleTypeIds: string[] } >( - ['untrackAlerts'], + mutationKeys.bulkUntrackAlertsByQuery(), ({ query, ruleTypeIds }) => { try { const body = JSON.stringify({ @@ -56,6 +62,4 @@ export const useBulkUntrackAlertsByQuery = () => { }, } ); - - return untrackAlertsByQuery; }; diff --git a/packages/response-ops/alerts_table/hooks/use_case_view_navigation.test.tsx b/packages/response-ops/alerts_table/hooks/use_case_view_navigation.test.tsx new file mode 100644 index 0000000000000..5f6acb776d477 --- /dev/null +++ b/packages/response-ops/alerts_table/hooks/use_case_view_navigation.test.tsx @@ -0,0 +1,72 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { PropsWithChildren } from 'react'; +import { act, waitFor, renderHook } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { BehaviorSubject } from 'rxjs'; +import { applicationServiceMock } from '@kbn/core/public/mocks'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import { useCaseViewNavigation } from './use_case_view_navigation'; +import { testQueryClientConfig } from '../utils/test'; + +const application = applicationServiceMock.createStartContract(); + +const queryClient = new QueryClient(testQueryClientConfig); + +const wrapper = ({ children }: PropsWithChildren) => { + return ( + + {children} + + ); +}; + +describe('useCaseViewNavigation', () => { + const navigateToApp = jest.fn(); + + beforeEach(() => { + application.currentAppId$ = new BehaviorSubject('testAppId'); + application.navigateToApp = navigateToApp; + }); + + it('calls navigateToApp with correct arguments', async () => { + const { result } = renderHook(() => useCaseViewNavigation(application), { + wrapper, + }); + + act(() => { + result.current.navigateToCaseView({ caseId: 'test-id' }); + }); + + await waitFor(() => + expect(navigateToApp).toHaveBeenCalledWith('testAppId', { + deepLinkId: 'cases', + path: '/test-id', + }) + ); + }); + + it('calls navigateToApp with correct arguments and bypass current app id', async () => { + const { result } = renderHook(() => useCaseViewNavigation(application, 'superAppId'), { + wrapper, + }); + + act(() => { + result.current.navigateToCaseView({ caseId: 'test-id' }); + }); + + await waitFor(() => { + expect(navigateToApp).toHaveBeenCalledWith('superAppId', { + deepLinkId: 'cases', + path: '/test-id', + }); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.ts b/packages/response-ops/alerts_table/hooks/use_case_view_navigation.ts similarity index 58% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.ts rename to packages/response-ops/alerts_table/hooks/use_case_view_navigation.ts index 23401994e9d21..ffceec3f6180b 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.ts +++ b/packages/response-ops/alerts_table/hooks/use_case_view_navigation.ts @@ -1,15 +1,16 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { generatePath } from 'react-router-dom'; import useObservable from 'react-use/lib/useObservable'; import { useCallback } from 'react'; - -import { useKibana } from '../../../../common/lib/kibana'; +import { ApplicationStart } from '@kbn/core-application-browser'; type NavigateToCaseView = (pathParams: { caseId: string }) => void; @@ -19,10 +20,8 @@ const generateCaseViewPath = (caseId: string): string => { return generatePath('/:caseId', { caseId }); }; -export const useCaseViewNavigation = (appId?: string) => { - const { - application: { navigateToApp, currentAppId$ }, - } = useKibana().services; +export const useCaseViewNavigation = (application: ApplicationStart, appId?: string) => { + const { navigateToApp, currentAppId$ } = application; const currentAppId = useObservable(currentAppId$) ?? ''; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx b/packages/response-ops/alerts_table/hooks/use_columns.test.tsx similarity index 93% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx rename to packages/response-ops/alerts_table/hooks/use_columns.test.tsx index dac261c0f7e6e..b19a6d22cc2d7 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx +++ b/packages/response-ops/alerts_table/hooks/use_columns.test.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import React, { FunctionComponent } from 'react'; @@ -14,19 +16,13 @@ import { BrowserFields } from '@kbn/alerting-types'; import { testQueryClientConfig } from '@kbn/alerts-ui-shared/src/common/test_utils/test_query_client_config'; import { fetchAlertsFields } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'; import { useColumns } from './use_columns'; -import { AlertsTableStorage } from '../../alerts_table'; -import { createStartServicesMock } from '../../../../../common/lib/kibana/kibana_react.mock'; +import { AlertsTablePersistedConfiguration } from '../components/alerts_table'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; jest.mock('@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'); -const mockUseKibanaReturnValue = createStartServicesMock(); -jest.mock('../../../../../common/lib/kibana', () => ({ - __esModule: true, - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); +const mockHttp = httpServiceMock.createStartContract(); const setItemStorageMock = jest.fn(); const mockStorage = { @@ -84,7 +80,7 @@ describe('useColumns', () => { columns: defaultColumns, visibleColumns: defaultColumns.map((col) => col.id), sort: [], - } as AlertsTableStorage, + } as AlertsTablePersistedConfiguration, }; }; @@ -153,6 +149,7 @@ describe('useColumns', () => { const { result, rerender } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -185,6 +182,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -210,6 +208,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, alertsFields, defaultColumns, ruleTypeIds, @@ -230,6 +229,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -252,6 +252,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -281,6 +282,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -299,6 +301,7 @@ describe('useColumns', () => { const { result, rerender } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns: localDefaultColumns, ruleTypeIds, id, @@ -336,6 +339,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -355,6 +359,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -376,6 +381,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -405,6 +411,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, @@ -438,6 +445,7 @@ describe('useColumns', () => { const { result } = renderHook( () => useColumns({ + http: mockHttp, defaultColumns, ruleTypeIds, id, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts b/packages/response-ops/alerts_table/hooks/use_columns.ts similarity index 92% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts rename to packages/response-ops/alerts_table/hooks/use_columns.ts index d3749b52bd56a..8c9b18d517a2e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts +++ b/packages/response-ops/alerts_table/hooks/use_columns.ts @@ -1,24 +1,26 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ +import { type MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { EuiDataGridColumn, EuiDataGridOnColumnResizeData } from '@elastic/eui'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { BrowserField, BrowserFields } from '@kbn/alerting-types'; -import { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { isEmpty } from 'lodash'; import { useFetchAlertsFieldsQuery } from '@kbn/alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import { AlertsTableStorage } from '../../alerts_table'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { AlertsTablePersistedConfiguration } from '../components/alerts_table'; import { toggleColumn } from './toggle_column'; -import { useKibana } from '../../../../../common'; export interface UseColumnsArgs { ruleTypeIds: string[]; - storageAlertsTable: React.MutableRefObject; + storageAlertsTable: React.MutableRefObject; storage: React.MutableRefObject; id: string; defaultColumns: EuiDataGridColumn[]; @@ -27,6 +29,7 @@ export interface UseColumnsArgs { * from the alerting APIs */ alertsFields?: BrowserFields; + http: HttpStart; } export interface UseColumnsResp { @@ -140,7 +143,7 @@ const persist = ({ visibleColumns, }: { id: string; - storageAlertsTable: MutableRefObject; + storageAlertsTable: MutableRefObject; storage: MutableRefObject; columns: EuiDataGridColumn[]; visibleColumns: string[]; @@ -160,9 +163,8 @@ export const useColumns = ({ id, defaultColumns, alertsFields, + http, }: UseColumnsArgs): UseColumnsResp => { - const { http } = useKibana().services; - const fieldsQuery = useFetchAlertsFieldsQuery( { http, diff --git a/packages/response-ops/alerts_table/hooks/use_field_formatter.ts b/packages/response-ops/alerts_table/hooks/use_field_formatter.ts new file mode 100644 index 0000000000000..e51e6bafe411f --- /dev/null +++ b/packages/response-ops/alerts_table/hooks/use_field_formatter.ts @@ -0,0 +1,34 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { FieldFormatParams } from '@kbn/field-formats-plugin/common'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; + +const defaultFieldFormatParams: Record = { + duration: { + inputFormat: 'milliseconds', + outputFormat: 'humanizePrecise', + }, + number: { + pattern: '00.00', + }, +}; + +/** + * Extracts field formatters from the field formats service + */ +export const useFieldFormatter = (fieldFormats: FieldFormatsStart) => { + return (fieldType: string, params?: FieldFormatParams) => { + const fieldFormatter = fieldFormats.deserialize({ + id: fieldType, + params: params ?? defaultFieldFormatParams[fieldType], + }); + return fieldFormatter.convert.bind(fieldFormatter); + }; +}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/use_license.test.ts b/packages/response-ops/alerts_table/hooks/use_license.test.ts similarity index 64% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/use_license.test.ts rename to packages/response-ops/alerts_table/hooks/use_license.test.ts index ec203e4bdacf8..8e452d17ca927 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/use_license.test.ts +++ b/packages/response-ops/alerts_table/hooks/use_license.test.ts @@ -1,17 +1,18 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { BehaviorSubject } from 'rxjs'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { renderHook } from '@testing-library/react'; import { useLicense } from './use_license'; -import { useKibana } from '../../common/lib/kibana'; -jest.mock('../../common/lib/kibana'); -const useKibanaMock = useKibana as jest.Mocked; +const mockLicensing = licensingMock.createStart(); describe('useLicense', () => { beforeEach(() => { @@ -24,13 +25,13 @@ describe('useLicense', () => { license: { type: 'platinum' }, }); - useKibanaMock().services.licensing = { - ...useKibanaMock().services.licensing, + const licensing = { + ...mockLicensing, license$: new BehaviorSubject(license), }; const { result } = renderHook(() => { - return useLicense(); + return useLicense({ licensing }); }); expect(result.current.isAtLeastPlatinum()).toBeTruthy(); @@ -41,13 +42,13 @@ describe('useLicense', () => { license: { type: 'gold' }, }); - useKibanaMock().services.licensing = { - ...useKibanaMock().services.licensing, + const licensing = { + ...mockLicensing, license$: new BehaviorSubject(license), }; const { result } = renderHook(() => { - return useLicense(); + return useLicense({ licensing }); }); expect(result.current.isAtLeastPlatinum()).toBeFalsy(); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/use_license.ts b/packages/response-ops/alerts_table/hooks/use_license.ts similarity index 56% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/use_license.ts rename to packages/response-ops/alerts_table/hooks/use_license.ts index fdac89bf994ce..a379aff7d857f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/use_license.ts +++ b/packages/response-ops/alerts_table/hooks/use_license.ts @@ -1,22 +1,27 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import type { ILicense, LicenseType } from '@kbn/licensing-plugin/public'; import { useCallback } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; -import { useKibana } from '../../common/lib/kibana'; +import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; interface UseLicenseReturnValue { isAtLeastPlatinum: () => boolean; } -export const useLicense = (): UseLicenseReturnValue => { - const { licensing } = useKibana().services; +interface UseLicenseProps { + licensing: LicensingPluginStart; +} + +export const useLicense = ({ licensing }: UseLicenseProps): UseLicenseReturnValue => { const license = useObservable(licensing?.license$ ?? new Observable(), null); const isAtLeast = useCallback( diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts b/packages/response-ops/alerts_table/hooks/use_pagination.test.ts similarity index 92% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts rename to packages/response-ops/alerts_table/hooks/use_pagination.test.ts index 740b0d3985f0e..76191f3e38c15 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts +++ b/packages/response-ops/alerts_table/hooks/use_pagination.test.ts @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { renderHook, act } from '@testing-library/react'; import { PaginationProps, usePagination } from './use_pagination'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts b/packages/response-ops/alerts_table/hooks/use_pagination.ts similarity index 87% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts rename to packages/response-ops/alerts_table/hooks/use_pagination.ts index 2f939e5db1ce7..65121a983d3b4 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts +++ b/packages/response-ops/alerts_table/hooks/use_pagination.ts @@ -1,12 +1,16 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { useCallback, useEffect, useState } from 'react'; import type { RuleRegistrySearchRequestPagination } from '@kbn/rule-registry-plugin/common'; -import { AdditionalContext, BulkActionsVerbs, RenderContext } from '../../../../types'; +import { BulkActionsVerbs } from '../types'; +import { AdditionalContext, RenderContext } from '../types'; export type PaginationProps = RuleRegistrySearchRequestPagination & { bulkActionsStore: RenderContext['bulkActionsStore']; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts b/packages/response-ops/alerts_table/hooks/use_sorting.test.ts similarity index 82% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts rename to packages/response-ops/alerts_table/hooks/use_sorting.test.ts index 95efe7c9c8c5a..e0be04c602389 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts +++ b/packages/response-ops/alerts_table/hooks/use_sorting.test.ts @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { useSorting } from './use_sorting'; import { renderHook, act } from '@testing-library/react'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts b/packages/response-ops/alerts_table/hooks/use_sorting.ts similarity index 76% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts rename to packages/response-ops/alerts_table/hooks/use_sorting.ts index 78eee5cf9657c..0eae65918b362 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts +++ b/packages/response-ops/alerts_table/hooks/use_sorting.ts @@ -1,16 +1,17 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import type { SortCombinations } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EuiDataGridSorting } from '@elastic/eui'; -import { useCallback, useMemo, useState } from 'react'; - import { EuiDataGridColumnSortingConfig } from '@elastic/eui/src/components/datagrid/data_grid_types'; -import { DefaultSort } from './constants'; +import { useCallback, useMemo, useState } from 'react'; +import { defaultSort } from '../constants'; const formatGridColumns = (cols: SortCombinations[]): EuiDataGridSorting['columns'] => { const colsSorting: EuiDataGridSorting['columns'] = []; @@ -24,7 +25,7 @@ const formatGridColumns = (cols: SortCombinations[]): EuiDataGridSorting['column export type UseSorting = ( onSortChange: (sort: EuiDataGridSorting['columns']) => void, - defaultSort: SortCombinations[] + initialSort: SortCombinations[] ) => { sortingColumns: EuiDataGridSorting['columns']; onSort: (newSort: EuiDataGridSorting['columns']) => void; @@ -33,12 +34,12 @@ export type UseSorting = ( export function useSorting( onSortChange: (sort: EuiDataGridSorting['columns']) => void, visibleColumns: string[], - defaultSort: SortCombinations[] = DefaultSort + initialSort: SortCombinations[] = defaultSort ) { const [visibleColumnsSort, invisibleColumnsSort] = useMemo(() => { const visibleSort: SortCombinations[] = []; const invisibleSort: EuiDataGridColumnSortingConfig[] = []; - defaultSort.forEach((sortCombinations) => { + initialSort.forEach((sortCombinations) => { if (visibleColumns.includes(Object.keys(sortCombinations)[0])) { visibleSort.push(sortCombinations); } else { @@ -46,7 +47,7 @@ export function useSorting( } }); return [visibleSort, invisibleSort]; - }, [defaultSort, visibleColumns]); + }, [initialSort, visibleColumns]); const [sortingColumns, setSortingColumns] = useState( formatGridColumns(visibleColumnsSort) ); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx b/packages/response-ops/alerts_table/hooks/use_toolbar_visibility.tsx similarity index 86% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx rename to packages/response-ops/alerts_table/hooks/use_toolbar_visibility.tsx index 3d2b4c4835df8..c00026efe3b83 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/toolbar_visibility.tsx +++ b/packages/response-ops/alerts_table/hooks/use_toolbar_visibility.tsx @@ -1,8 +1,10 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ import { @@ -10,18 +12,17 @@ import { EuiDataGridToolBarVisibilityOptions, } from '@elastic/eui'; import React, { lazy, memo, ReactNode, Suspense, useMemo } from 'react'; -import { BrowserFields } from '@kbn/alerting-types'; -import { EsQuerySnapshot } from '@kbn/alerts-ui-shared'; -import { AlertsCount } from './components/alerts_count/alerts_count'; +import { Alert, BrowserFields, EsQuerySnapshot } from '@kbn/alerting-types'; +import { FieldBrowser, FieldBrowserOptions } from '@kbn/response-ops-alerts-fields-browser'; +import type { SettingsStart } from '@kbn/core-ui-settings-browser'; +import { AlertsCount } from '../components/alerts_count'; import { useAlertsTableContext } from '../contexts/alerts_table_context'; -import type { Alerts, BulkActionsPanelConfig, RowSelection } from '../../../../types'; -import { LastUpdatedAt } from './components/last_updated_at'; -import { FieldBrowser } from '../../field_browser'; -import { FieldBrowserOptions } from '../../field_browser/types'; -import { InspectButton } from './components/inspect'; +import type { BulkActionsPanelConfig, RowSelection } from '../types'; +import { LastUpdatedAt } from '../components/last_updated_at'; +import { AlertsQueryInspector } from '../components/alerts_query_inspector'; import { ALERTS_TABLE_TITLE } from '../translations'; -const BulkActionsToolbar = lazy(() => import('../bulk_actions/components/toolbar')); +const BulkActionsToolbar = lazy(() => import('../components/bulk_actions_toolbar_control')); const RightControl = memo( ({ @@ -39,7 +40,7 @@ const RightControl = memo( return ( <> {showInspectButton && alertsQuerySnapshot && ( - @@ -171,11 +172,12 @@ export const useGetToolbarVisibility = ({ alertsQuerySnapshot, showInspectButton, toolbarVisibilityProp, + settings, }: { bulkActions: BulkActionsPanelConfig[]; alertsCount: number; rowSelection: RowSelection; - alerts: Alerts; + alerts: Alert[]; isLoading: boolean; columnIds: string[]; onToggleColumn: (columnId: string) => void; @@ -189,6 +191,7 @@ export const useGetToolbarVisibility = ({ alertsQuerySnapshot?: EsQuerySnapshot; showInspectButton: boolean; toolbarVisibilityProp?: EuiDataGridToolBarVisibilityOptions; + settings: SettingsStart; }): EuiDataGridToolBarVisibilityOptions => { const selectedRowsCount = rowSelection.size; const defaultVisibilityProps = useMemo(() => { @@ -248,6 +251,7 @@ export const useGetToolbarVisibility = ({ setIsBulkActionsLoading={setIsBulkActionsLoading} clearSelection={clearSelection} refresh={refresh} + settings={settings} /> @@ -258,18 +262,19 @@ export const useGetToolbarVisibility = ({ }; } }, [ - alertsCount, + selectedRowsCount, bulkActions, defaultVisibility, - selectedRowsCount, toolbarVisibilityProp, - alerts, - clearSelection, - refresh, - setIsBulkActionsLoading, additionalToolbarControls, alertsQuerySnapshot, showInspectButton, + alertsCount, + alerts, + setIsBulkActionsLoading, + clearSelection, + refresh, + settings, ]); return options; diff --git a/packages/response-ops/alerts_table/index.ts b/packages/response-ops/alerts_table/index.ts new file mode 100644 index 0000000000000..798caff6903d7 --- /dev/null +++ b/packages/response-ops/alerts_table/index.ts @@ -0,0 +1,14 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { AlertsTable } from './components/alerts_table'; +export { AlertsTable } from './components/alerts_table'; +// Lazy load helper +// eslint-disable-next-line import/no-default-export +export default AlertsTable; diff --git a/packages/response-ops/alerts_table/jest.config.js b/packages/response-ops/alerts_table/jest.config.js new file mode 100644 index 0000000000000..5f08271830cec --- /dev/null +++ b/packages/response-ops/alerts_table/jest.config.js @@ -0,0 +1,15 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/packages/response-ops/alerts_table'], + setupFilesAfterEnv: ['/packages/response-ops/alerts_table/setup_tests.ts'], +}; diff --git a/packages/response-ops/alerts_table/kibana.jsonc b/packages/response-ops/alerts_table/kibana.jsonc new file mode 100644 index 0000000000000..cb86056e89ede --- /dev/null +++ b/packages/response-ops/alerts_table/kibana.jsonc @@ -0,0 +1,7 @@ +{ + "type": "shared-browser", + "id": "@kbn/response-ops-alerts-table", + "owner": "@elastic/response-ops", + "group": "platform", + "visibility": "shared" +} diff --git a/packages/response-ops/alerts_table/mocks/cases.mock.tsx b/packages/response-ops/alerts_table/mocks/cases.mock.tsx new file mode 100644 index 0000000000000..d3f7cc1816557 --- /dev/null +++ b/packages/response-ops/alerts_table/mocks/cases.mock.tsx @@ -0,0 +1,61 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { PropsWithChildren } from 'react'; +import { CaseStatuses } from '@kbn/cases-components'; +import type { Case } from '../apis/bulk_get_cases'; +import type { CasesService } from '../types'; + +export const theCase: Case = { + id: 'test-id', + created_at: '2023-02-16T18:13:37.058Z', + created_by: { full_name: 'Elastic', username: 'elastic', email: 'elastic@elastic.co' }, + description: 'Test description', + status: CaseStatuses.open, + title: 'Test case', + totalComment: 1, + version: 'WzQ3LDFd', + owner: 'cases', +}; + +export const getCasesMock = () => { + return [theCase, { ...theCase, id: 'test-id-2', title: 'Test case 2' }]; +}; + +export const getCasesMapMock = () => + getCasesMock().reduce((acc, val) => acc.set(val.id, val), new Map()); + +export const openAddToExistingCaseModalMock = jest.fn(); +export const openAddToNewCaseFlyoutMock = jest.fn(); + +const uiMock: jest.MockedObject = { + getCasesContext: jest + .fn() + .mockImplementation(() => ({ children }: PropsWithChildren) => <>{children}), +}; + +const hooksMock: jest.MockedObject = { + useCasesAddToNewCaseFlyout: jest.fn().mockImplementation(() => ({ + open: openAddToNewCaseFlyoutMock, + })), + useCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({ + open: openAddToExistingCaseModalMock, + })), +}; + +const helpersMock: jest.MockedObject = { + canUseCases: jest.fn(), + groupAlertsByRule: jest.fn(), +}; + +export const createCasesServiceMock = (): jest.MaybeMockedDeep => ({ + ui: uiMock, + hooks: hooksMock, + helpers: helpersMock, +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx b/packages/response-ops/alerts_table/mocks/context.mock.tsx similarity index 66% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx rename to packages/response-ops/alerts_table/mocks/context.mock.tsx index ede59e5fe63e6..0f0ffac9b6108 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_data_grid.mock.tsx +++ b/packages/response-ops/alerts_table/mocks/context.mock.tsx @@ -1,23 +1,18 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import { BrowserFields } from '@kbn/alerting-types'; -import { - AdditionalContext, - Alerts, - AlertsDataGridProps, - AlertsField, - FetchAlertData, - RenderContext, - RowSelectionState, -} from '../../../types'; -import { getCasesMapMock } from './cases/index.mock'; -import { getMaintenanceWindowsMock } from './maintenance_windows/index.mock'; -import { EuiButton, EuiButtonIcon, EuiFlexItem } from '@elastic/eui'; +import { Alert, BrowserFields, LegacyField } from '@kbn/alerting-types'; +import { AlertsField, RowSelectionState } from '../types'; +import { AdditionalContext, RenderContext } from '../types'; +import { createCasesServiceMock, getCasesMapMock } from './cases.mock'; +import { getMaintenanceWindowsMock } from './maintenance_windows.mock'; +import { EuiButtonIcon, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { identity } from 'lodash'; import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; @@ -26,14 +21,16 @@ import { ALERT_FLAPPING, ALERT_REASON, ALERT_RULE_NAME, + ALERT_RULE_UUID, ALERT_STATUS, } from '@kbn/rule-data-utils'; -import { FIELD_BROWSER_CUSTOM_CREATE_BTN_TEST_ID } from './constants'; - -export type BaseAlertsDataGridProps = AlertsDataGridProps; -export type TestAlertsDataGridProps = Partial> & { - renderContext?: Partial>; -}; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; +import { settingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import { createPartialObjectMock } from '../utils/test'; export const mockFieldFormatsRegistry = { deserialize: jest.fn(() => ({ @@ -80,15 +77,20 @@ export const mockColumns = [ }, ]; -export const mockAlerts = [ +export const mockAlerts: Alert[] = [ { + _id: 'test-1', + _index: 'alerts', [ALERT_RULE_NAME]: ['one'], + [ALERT_RULE_UUID]: ['rule-uuid'], [ALERT_REASON]: ['two'], [ALERT_STATUS]: ['active'], [ALERT_FLAPPING]: [true], [ALERT_CASE_IDS]: ['test-id'], }, { + _id: 'test-2', + _index: 'alerts', [ALERT_RULE_NAME]: ['three'], [ALERT_REASON]: ['four'], [ALERT_STATUS]: ['active'], @@ -96,18 +98,22 @@ export const mockAlerts = [ [ALERT_CASE_IDS]: ['test-id-2'], }, { + _id: 'test-3', + _index: 'alerts', [ALERT_RULE_NAME]: ['five'], [ALERT_REASON]: ['six'], [ALERT_STATUS]: ['recovered'], [ALERT_FLAPPING]: [true], }, { + _id: 'test-4', + _index: 'alerts', [ALERT_RULE_NAME]: ['seven'], [ALERT_REASON]: ['eight'], [ALERT_STATUS]: ['recovered'], [ALERT_FLAPPING]: [false], }, -] as unknown as Alerts; +]; export const mockOldAlertsData = [ [ { @@ -129,7 +135,7 @@ export const mockOldAlertsData = [ value: ['four'], }, ], -] as FetchAlertData['oldAlertsData']; +] as LegacyField[][]; export const mockEcsData = [ [ { @@ -161,7 +167,7 @@ export const mockEcsData = [ }, }, ], -] as FetchAlertData['ecsAlertsData']; +]; export const mockCases = getCasesMapMock(); export const mockMaintenanceWindows = getMaintenanceWindowsMock().reduce( @@ -169,16 +175,17 @@ export const mockMaintenanceWindows = getMaintenanceWindowsMock().reduce( new Map() ); -export const mockBulkActionsState = { +export const createMockBulkActionsState = () => ({ rowSelection: new Map(), isAllSelected: false, areAllVisibleRowsSelected: false, rowCount: 4, updatedAt: Date.now(), -}; +}); -export const mockRenderContext = { +export const mockRenderContext = createPartialObjectMock>({ tableId: 'test-table', + dataGridRef: { current: null }, showAlertStatusWithFlapping: true, columns: mockColumns, refresh: jest.fn(), @@ -197,10 +204,9 @@ export const mockRenderContext = { mutedAlerts: {}, pageIndex: 0, pageSize: 1, - fieldFormats: mockFieldFormatsRegistry, openAlertInFlyout: jest.fn(), bulkActionsStore: [ - mockBulkActionsState, + createMockBulkActionsState(), jest.fn(), ] as unknown as RenderContext['bulkActionsStore'], renderCellValue: jest.fn().mockImplementation((props) => { @@ -221,39 +227,14 @@ export const mockRenderContext = { />
), -} as RenderContext; - -export const mockDataGridProps: Partial = { - pageSizeOptions: [1, 10, 20, 50, 100], - leadingControlColumns: [], - trailingControlColumns: [], - visibleColumns: mockColumns.map((c) => c.id), - 'data-test-subj': 'testTable', - onToggleColumn: jest.fn(), - onResetColumns: jest.fn(), - onChangeVisibleColumns: jest.fn(), - query: {}, - sort: [], - alertsQuerySnapshot: { request: [], response: [] }, - onSortChange: jest.fn(), - onChangePageIndex: jest.fn(), - onChangePageSize: jest.fn(), - getBulkActions: () => [ - { - id: 0, - items: [ - { - label: 'Fake Bulk Action', - key: 'fakeBulkAction', - 'data-test-subj': 'fake-bulk-action', - disableOnQuery: false, - onClick: () => {}, - }, - ], - }, - ], - fieldsBrowserOptions: { - createFieldButton: () => , + services: { + http: httpServiceMock.createStartContract(), + data: dataPluginMock.createStartContract(), + fieldFormats: mockFieldFormatsRegistry, + notifications: notificationServiceMock.createStartContract(), + application: applicationServiceMock.createStartContract(), + settings: settingsServiceMock.createStartContract(), + licensing: licensingMock.createStart(), + cases: createCasesServiceMock(), }, - casesConfiguration: { featureId: 'test-feature-id', owner: ['test-owner'] }, -}; +}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/index.mock.ts b/packages/response-ops/alerts_table/mocks/maintenance_windows.mock.ts similarity index 76% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/index.mock.ts rename to packages/response-ops/alerts_table/mocks/maintenance_windows.mock.ts index 35896282a9a65..96b38deee3c65 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/index.mock.ts +++ b/packages/response-ops/alerts_table/mocks/maintenance_windows.mock.ts @@ -1,9 +1,12 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import { MaintenanceWindowStatus } from '@kbn/alerting-plugin/common'; const mockMaintenanceWindow = { diff --git a/packages/response-ops/alerts_table/package.json b/packages/response-ops/alerts_table/package.json new file mode 100644 index 0000000000000..61543bb5695c2 --- /dev/null +++ b/packages/response-ops/alerts_table/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/response-ops-alerts-table", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0" +} \ No newline at end of file diff --git a/packages/response-ops/alerts_table/query_client.ts b/packages/response-ops/alerts_table/query_client.ts new file mode 100644 index 0000000000000..cdea0fb882280 --- /dev/null +++ b/packages/response-ops/alerts_table/query_client.ts @@ -0,0 +1,12 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { QueryClient } from '@tanstack/react-query'; + +export const alertsTableQueryClient = new QueryClient(); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx b/packages/response-ops/alerts_table/reducers/bulk_actions_reducer.test.tsx similarity index 83% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx rename to packages/response-ops/alerts_table/reducers/bulk_actions_reducer.test.tsx index ac27d6713978c..c85eb2126ec4a 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx +++ b/packages/response-ops/alerts_table/reducers/bulk_actions_reducer.test.tsx @@ -1,41 +1,32 @@ /* * 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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ + import React, { useMemo, useReducer } from 'react'; import { render, screen, within, waitFor, act } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; -import { AlertsDataGrid } from '../alerts_data_grid'; -import { - Alerts, - AlertsField, - BulkActionsConfig, - BulkActionsState, - RenderContext, - AdditionalContext, - Alert, -} from '../../../../types'; -import { bulkActionsReducer } from './reducer'; -import { createAppMockRenderer, getJsDomPerformanceFix } from '../../test_utils'; -import { createCasesServiceMock } from '../index.mock'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import type { Alert } from '@kbn/alerting-types'; +import { AlertsDataGrid } from '../components/alerts_data_grid'; +import { AlertsField, BulkActionsConfig, BulkActionsState } from '../types'; +import { RenderContext, AdditionalContext } from '../types'; +import { bulkActionsReducer } from './bulk_actions_reducer'; +import { createMockBulkActionsState, mockRenderContext } from '../mocks/context.mock'; import { TestAlertsDataGridProps, - mockBulkActionsState, - mockDataGridProps, - mockRenderContext, BaseAlertsDataGridProps, -} from '../alerts_data_grid.mock'; + mockDataGridProps, +} from '../components/alerts_data_grid.test'; import { AlertsTableContextProvider } from '../contexts/alerts_table_context'; -import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; -import userEvent from '@testing-library/user-event'; - -jest.mock('@kbn/data-plugin/public'); -jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ - useUiSetting$: jest.fn((value: string) => ['0,0']), -})); +import { getJsDomPerformanceFix, testQueryClientConfig } from '../utils/test'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; const columns = [ { @@ -48,34 +39,15 @@ const columns = [ }, ]; -const mockCaseService = createCasesServiceMock(); -const mockApplication = applicationServiceMock.createStartContract(); -jest.mock('@kbn/kibana-react-plugin/public', () => { - const original = jest.requireActual('@kbn/kibana-react-plugin/public'); - - return { - ...original, - useKibana: () => ({ - services: { - application: mockApplication, - cases: mockCaseService, - notifications: { - toasts: { - addDanger: jest.fn(), - addSuccess: jest.fn(), - }, - }, - }, - }), - }; -}); - type AlertsTableWithBulkActionsContextProps = TestAlertsDataGridProps & { initialBulkActionsState?: BulkActionsState; renderContext?: Partial>; }; const mockRefresh = jest.mocked(mockRenderContext.refresh); +const mockCaseService = mockRenderContext.services.cases!; + +const queryClient = new QueryClient(testQueryClientConfig); const { fix, cleanup } = getJsDomPerformanceFix(); beforeAll(() => { @@ -86,22 +58,22 @@ afterAll(() => { }); describe('AlertsDataGrid bulk actions', () => { - const alerts = [ + const alerts: Alert[] = [ { + _id: 'alert0', + _index: 'idx0', [AlertsField.name]: ['one'], [AlertsField.reason]: ['two'], [AlertsField.uuid]: ['uuidone'], - _id: 'alert0', - _index: 'idx0', }, { + _id: 'alert1', + _index: 'idx1', [AlertsField.name]: ['three'], [AlertsField.reason]: ['four'], [AlertsField.uuid]: ['uuidtwo'], - _id: 'alert1', - _index: 'idx1', }, - ] as unknown as Alerts; + ]; const dataGridProps: TestAlertsDataGridProps = { ...mockDataGridProps, @@ -167,24 +139,19 @@ describe('AlertsDataGrid bulk actions', () => { ], }; - const defaultBulkActionsState = { - ...mockBulkActionsState, + const createDefaultBulkActionsState = () => ({ + ...createMockBulkActionsState(), rowCount: 2, - }; + }); const TestComponent = ({ initialBulkActionsState, renderContext: renderContextOverrides, ...props }: AlertsTableWithBulkActionsContextProps) => { - const { AppWrapper } = useMemo( - () => createAppMockRenderer({ queryClientContext: AlertsQueryContext }), - [] - ); - const bulkActionsStore = useReducer( bulkActionsReducer, - initialBulkActionsState || defaultBulkActionsState + initialBulkActionsState || createDefaultBulkActionsState() ); const renderContext = useMemo( () => ({ @@ -196,11 +163,13 @@ describe('AlertsDataGrid bulk actions', () => { ); return ( - - - - - + + + + + + + ); }; @@ -261,21 +230,21 @@ describe('AlertsDataGrid bulk actions', () => { it('should pass the case ids when selecting alerts', async () => { const mockOnClick = jest.fn(); - const newAlerts = [ + const newAlerts: Alert[] = [ { + _id: 'alert0', + _index: 'idx0', [AlertsField.name]: ['one'], [AlertsField.reason]: ['two'], [AlertsField.uuid]: ['uuidone'], [AlertsField.case_ids]: ['test-case'], - _id: 'alert0', - _index: 'idx0', - } as Alert, + }, ]; const props: AlertsTableWithBulkActionsContextProps = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), isAllSelected: true, rowCount: 1, rowSelection: new Map([[0, { isLoading: false }]]), @@ -301,10 +270,10 @@ describe('AlertsDataGrid bulk actions', () => { render(); - userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); + await userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(await screen.findByText('Fake Bulk Action')); + await userEvent.click(await screen.findByText('Fake Bulk Action')); expect(mockOnClick.mock.calls[0][0]).toEqual([ { @@ -358,7 +327,7 @@ describe('AlertsDataGrid bulk actions', () => { expect(bulkActionsCells[0].checked).toBeFalsy(); expect(bulkActionsCells[1].checked).toBeFalsy(); - userEvent.click(screen.getByTestId('bulk-actions-header')); + await userEvent.click(screen.getByTestId('bulk-actions-header')); bulkActionsCells = screen.getAllByTestId('bulk-actions-row-cell') as HTMLInputElement[]; expect(bulkActionsCells[0].checked).toBeTruthy(); @@ -369,7 +338,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, rowSelection: new Map([ [0, { isLoading: false }], @@ -383,12 +352,12 @@ describe('AlertsDataGrid bulk actions', () => { }); describe('and clicking on a single row', () => { - it('should uncheck the select all header column', () => { + it('should uncheck the select all header column', async () => { // State after having already clicked on select all before const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, rowSelection: new Map([ [0, { isLoading: false }], @@ -404,7 +373,7 @@ describe('AlertsDataGrid bulk actions', () => { (screen.getByTestId('bulk-actions-header') as HTMLInputElement).checked ).toBeTruthy(); - userEvent.click(bulkActionsCells[1]); + await userEvent.click(bulkActionsCells[1]); expect( (screen.getByTestId('bulk-actions-header') as HTMLInputElement).checked ).toBeFalsy(); @@ -413,13 +382,14 @@ describe('AlertsDataGrid bulk actions', () => { describe('and its a page with count of alerts different than page size', () => { it('should show the right amount of alerts selected', async () => { - const secondPageAlerts = [ + const secondPageAlerts: Alert[] = [ { + _id: 'alert2', + _index: 'alerts', [AlertsField.name]: ['five'], [AlertsField.reason]: ['six'], - _id: 'alert2', }, - ] as unknown as Alerts; + ]; const allAlerts = [...alerts, ...secondPageAlerts]; const props: AlertsTableWithBulkActionsContextProps = { ...dataGridPropsWithBulkActions, @@ -430,7 +400,7 @@ describe('AlertsDataGrid bulk actions', () => { pageSize: 2, }, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, rowSelection: new Map([[0, { isLoading: false }]]), }, @@ -449,7 +419,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, rowSelection: new Map([ [0, { isLoading: false }], @@ -465,7 +435,7 @@ describe('AlertsDataGrid bulk actions', () => { ((await screen.findAllByTestId('bulk-actions-row-cell')) as HTMLInputElement[])[1].checked ).toBeTruthy(); - userEvent.click(await screen.findByTestId('bulk-actions-header')); + await userEvent.click(await screen.findByTestId('bulk-actions-header')); expect( ((await screen.findAllByTestId('bulk-actions-row-cell')) as HTMLInputElement[])[0].checked @@ -480,40 +450,40 @@ describe('AlertsDataGrid bulk actions', () => { it('should show the toolbar', async () => { render(); - expect(screen.queryByTestId('selectedShowBulkActionsButton')).toBeNull(); - expect(screen.queryByTestId('selectAllAlertsButton')).toBeNull(); + expect(screen.queryByTestId('selectedShowBulkActionsButton')).not.toBeInTheDocument(); + expect(screen.queryByTestId('selectAllAlertsButton')).not.toBeInTheDocument(); const bulkActionsCells = screen.getAllByTestId( 'bulk-actions-row-cell' ) as HTMLInputElement[]; - userEvent.click(bulkActionsCells[0]); + await userEvent.click(bulkActionsCells[0]); - expect(await screen.findByTestId('selectedShowBulkActionsButton')).toBeDefined(); - expect(await screen.findByTestId('selectAllAlertsButton')).toBeDefined(); + expect(await screen.findByTestId('selectedShowBulkActionsButton')).toBeInTheDocument(); + expect(await screen.findByTestId('selectAllAlertsButton')).toBeInTheDocument(); }); describe('and the last remaining row is unchecked', () => { - it('should hide the toolbar', () => { + it('should hide the toolbar', async () => { // state after having already clicked on select all before const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), rowSelection: new Map([[0, { isLoading: false }]]), }, }; - const { queryByTestId, getAllByTestId, getByTestId } = render( - - ); + render(); - expect(getByTestId('selectedShowBulkActionsButton')).toBeDefined(); - expect(getByTestId('selectAllAlertsButton')).toBeDefined(); + expect(screen.getByTestId('selectedShowBulkActionsButton')).toBeDefined(); + expect(screen.getByTestId('selectAllAlertsButton')).toBeDefined(); - const bulkActionsCells = getAllByTestId('bulk-actions-row-cell') as HTMLInputElement[]; - userEvent.click(bulkActionsCells[0]); + const bulkActionsCells = screen.getAllByTestId( + 'bulk-actions-row-cell' + ) as HTMLInputElement[]; + await userEvent.click(bulkActionsCells[0]); - expect(queryByTestId('selectAllAlertsButton')).toBeNull(); - expect(queryByTestId('selectedShowBulkActionsButton')).toBeNull(); + expect(screen.queryByTestId('selectAllAlertsButton')).toBeNull(); + expect(screen.queryByTestId('selectedShowBulkActionsButton')).toBeNull(); }); }); }); @@ -525,7 +495,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), rowSelection: new Map([[1, { isLoading: false }]]), }, getBulkActions: () => [ @@ -546,10 +516,10 @@ describe('AlertsDataGrid bulk actions', () => { render(); - userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); + await userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(await screen.findByText('Fake Bulk Action')); + await userEvent.click(await screen.findByText('Fake Bulk Action')); expect(mockOnClick.mock.calls[0][0]).toEqual([ { _id: 'alert1', @@ -608,15 +578,15 @@ describe('AlertsDataGrid bulk actions', () => { it('should show the loading state on each selected row', async () => { const initialBulkActionsState = { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), rowSelection: new Map([[1, { isLoading: false }]]), }; render(); - userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); + await userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(await screen.findByTestId('fake-bulk-action')); + await userEvent.click(await screen.findByTestId('fake-bulk-action')); // The callback given to our clients to run when they want to update the loading state act(() => { @@ -625,25 +595,26 @@ describe('AlertsDataGrid bulk actions', () => { expect(await screen.findAllByTestId('row-loader')).toHaveLength(1); const selectedOptions = await screen.findAllByTestId('dataGridRowCell'); + // First row, first column expect(within(selectedOptions[0]).queryByLabelText('Loading')).not.toBeInTheDocument(); expect(within(selectedOptions[0]).getByRole('checkbox')).toBeInTheDocument(); // Second row, first column - expect(within(selectedOptions[3]).getByLabelText('Loading')).toBeDefined(); + expect(within(selectedOptions[3]).getByLabelText('Loading')).toBeInTheDocument(); expect(within(selectedOptions[3]).queryByRole('checkbox')).not.toBeInTheDocument(); }); it('should hide the loading state on each selected row', async () => { const initialBulkActionsState = { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), rowSelection: new Map([[1, { isLoading: true }]]), }; render(); - userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); + await userEvent.click(await screen.findByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(await screen.findByText('Fake Bulk Action')); + await userEvent.click(await screen.findByText('Fake Bulk Action')); // the callback given to our clients to run when they want to update the loading state mockOnClick.mock.calls[0][2](false); @@ -658,7 +629,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), rowSelection: new Map([[0, { isLoading: false }]]), }, }; @@ -674,7 +645,7 @@ describe('AlertsDataGrid bulk actions', () => { .checked ).toBeFalsy(); - userEvent.click(screen.getByTestId('selectAllAlertsButton')); + await userEvent.click(screen.getByTestId('selectAllAlertsButton')); expect( ((await screen.findAllByTestId('bulk-actions-row-cell')) as HTMLInputElement[])[0] @@ -691,7 +662,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, isAllSelected: true, rowSelection: new Map([ @@ -712,7 +683,7 @@ describe('AlertsDataGrid bulk actions', () => { .checked ).toBeTruthy(); - userEvent.click(screen.getByTestId('selectAllAlertsButton')); + await userEvent.click(screen.getByTestId('selectAllAlertsButton')); expect( ((await screen.findAllByTestId('bulk-actions-row-cell')) as HTMLInputElement[])[0] @@ -731,7 +702,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), isAllSelected: true, rowCount: 2, rowSelection: new Map([ @@ -757,10 +728,10 @@ describe('AlertsDataGrid bulk actions', () => { render(); - userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); + await userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(screen.getByText('Fake Bulk Action')); + await userEvent.click(screen.getByText('Fake Bulk Action')); expect(mockOnClick.mock.calls[0][0]).toEqual([ { _id: 'alert0', @@ -832,7 +803,7 @@ describe('AlertsDataGrid bulk actions', () => { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, rowSelection: new Map(), }, @@ -843,7 +814,7 @@ describe('AlertsDataGrid bulk actions', () => { 'bulk-actions-row-cell' ) as HTMLInputElement[]; - userEvent.click(screen.getByTestId('bulk-actions-header')); + await userEvent.click(screen.getByTestId('bulk-actions-header')); await waitFor(async () => { bulkActionsCells = screen.getAllByTestId( @@ -854,10 +825,10 @@ describe('AlertsDataGrid bulk actions', () => { expect(screen.getByTestId('selectedShowBulkActionsButton')).toBeDefined(); }); - userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); + await userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(screen.getByTestId('fake-bulk-action-loading')); + await userEvent.click(screen.getByTestId('fake-bulk-action-loading')); await waitFor(() => { expect(screen.queryAllByTestId('row-loader')).toHaveLength(2); @@ -868,7 +839,7 @@ describe('AlertsDataGrid bulk actions', () => { const props = { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: false, rowSelection: new Map(), }, @@ -879,7 +850,7 @@ describe('AlertsDataGrid bulk actions', () => { 'bulk-actions-row-cell' ) as HTMLInputElement[]; - userEvent.click(screen.getByTestId('bulk-actions-header')); + await userEvent.click(screen.getByTestId('bulk-actions-header')); await waitFor(async () => { bulkActionsCells = screen.getAllByTestId( @@ -890,12 +861,12 @@ describe('AlertsDataGrid bulk actions', () => { expect(screen.getByTestId('selectedShowBulkActionsButton')).toBeDefined(); }); - userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); + await userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); mockRefresh.mockClear(); expect(mockRefresh.mock.calls.length).toBe(0); - userEvent.click(screen.getByTestId('fake-bulk-action-refresh')); + await userEvent.click(screen.getByTestId('fake-bulk-action-refresh')); expect(mockRefresh.mock.calls.length).toBeGreaterThan(0); }); @@ -904,7 +875,7 @@ describe('AlertsDataGrid bulk actions', () => { ...dataGridPropsWithBulkActions, initialBulkActionsState: { - ...defaultBulkActionsState, + ...createDefaultBulkActionsState(), areAllVisibleRowsSelected: true, rowSelection: new Map([[0, { isLoading: true }]]), }, @@ -915,14 +886,14 @@ describe('AlertsDataGrid bulk actions', () => { 'bulk-actions-row-cell' ) as HTMLInputElement[]; - userEvent.click(screen.getByTestId('bulk-actions-header')); + await userEvent.click(screen.getByTestId('bulk-actions-header')); expect(screen.getByTestId('selectedShowBulkActionsButton')).toBeVisible(); - userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); + await userEvent.click(screen.getByTestId('selectedShowBulkActionsButton')); await waitForEuiPopoverOpen(); - userEvent.click(screen.getByTestId('fake-bulk-action-clear')); + await userEvent.click(screen.getByTestId('fake-bulk-action-clear')); // clear Selection happens after 150ms await waitFor(() => { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/reducer.ts b/packages/response-ops/alerts_table/reducers/bulk_actions_reducer.ts similarity index 83% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/reducer.ts rename to packages/response-ops/alerts_table/reducers/bulk_actions_reducer.ts index 38f79e1eba9e0..c0c270baaecd8 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/reducer.ts +++ b/packages/response-ops/alerts_table/reducers/bulk_actions_reducer.ts @@ -1,11 +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. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -import { BulkActionsReducerAction, BulkActionsState, BulkActionsVerbs } from '../../../../types'; +import { BulkActionsReducerAction, BulkActionsState, BulkActionsVerbs } from '../types'; const getAllRowsInPage = (rowCount: number) => new Map(Array.from(Array(rowCount).keys()).map((idx) => [idx, { isLoading: false }])); diff --git a/packages/response-ops/alerts_table/setup_tests.ts b/packages/response-ops/alerts_table/setup_tests.ts new file mode 100644 index 0000000000000..b3862573ca0f9 --- /dev/null +++ b/packages/response-ops/alerts_table/setup_tests.ts @@ -0,0 +1,11 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +/* eslint-disable import/no-extraneous-dependencies */ +import '@testing-library/jest-dom'; +import '@emotion/jest'; diff --git a/packages/response-ops/alerts_table/translations.ts b/packages/response-ops/alerts_table/translations.ts new file mode 100644 index 0000000000000..a19e1d245da7d --- /dev/null +++ b/packages/response-ops/alerts_table/translations.ts @@ -0,0 +1,283 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { i18n } from '@kbn/i18n'; + +export const FEATURE_LABEL = i18n.translate('responseOpsAlertsTable.feature.label', { + defaultMessage: 'Feature', +}); + +export const CASES = i18n.translate('responseOpsAlertsTable.cases.label', { + defaultMessage: 'Cases', +}); + +export const MAINTENANCE_WINDOWS = i18n.translate( + 'responseOpsAlertsTable.maintenanceWindows.label', + { + defaultMessage: 'Maintenance Windows', + } +); + +export const OBSERVABILITY_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.observability', + { + defaultMessage: 'Observability', + } +); + +export const SECURITY_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.security', + { + defaultMessage: 'Security', + } +); + +export const STACK_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.stack', + { + defaultMessage: 'Stack', + } +); + +export const STACK_MONITORING_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.stackMonitoring', + { + defaultMessage: 'Stack Monitoring', + } +); + +export const UPTIME_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.uptime', + { + defaultMessage: 'Uptime', + } +); + +export const APM_DISPLAY_NAME = i18n.translate('responseOpsAlertsTable.sections.alertsTable.apm', { + defaultMessage: 'APM', +}); + +export const INFRASTRUCTURE_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.infrastructure', + { + defaultMessage: 'Infrastructure', + } +); + +export const SLO_DISPLAY_NAME = i18n.translate('responseOpsAlertsTable.sections.alertsTable.slos', { + defaultMessage: 'SLOs', +}); + +export const LOGS_DISPLAY_NAME = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.logs', + { + defaultMessage: 'Logs', + } +); + +export const ML_DISPLAY_NAME = i18n.translate('responseOpsAlertsTable.sections.alertsTable.ml', { + defaultMessage: 'Machine Learning', +}); + +export const ALERTS_TABLE_CONF_ERROR_TITLE = i18n.translate( + 'responseOpsAlertsTable.alertsTable.configuration.errorTitle', + { + defaultMessage: 'Unable to load alerts table', + } +); + +export const ALERTS_TABLE_CONF_ERROR_MESSAGE = i18n.translate( + 'responseOpsAlertsTable.alertsTable.configuration.errorBody', + { + defaultMessage: + 'There was an error loading the alerts table. This table is missing the necessary configuration. Please contact your administrator for help', + } +); + +export const ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.column.actions', + { + defaultMessage: 'Actions', + } +); + +export const ALERTS_TABLE_TITLE = i18n.translate( + 'responseOpsAlertsTable.sections.alertsTable.title', + { + defaultMessage: 'Alerts table', + } +); + +export const ALERTS_TABLE_FILTERS_ERROR_TITLE = i18n.translate( + 'responseOpsAlertsTable.alertsTable.filters.errorTitle', + { + defaultMessage: 'Unsupported alerts filters set', + } +); + +export const ALERTS_TABLE_UNKNOWN_ERROR_TITLE = i18n.translate( + 'responseOpsAlertsTable.alertsTable.unknownErrorTitle', + { + defaultMessage: 'Cannot display alerts', + } +); + +export const ALERTS_TABLE_UNKNOWN_ERROR_MESSAGE = i18n.translate( + 'responseOpsAlertsTable.alertsTable.unknownErrorBody', + { + defaultMessage: 'An error occurred while rendering the alerts table', + } +); + +export const ALERTS_TABLE_UNKNOWN_ERROR_COPY_TO_CLIPBOARD_LABEL = i18n.translate( + 'responseOpsAlertsTable.alertsTable.unknownErrorCopyToClipboardLabel', + { + defaultMessage: 'Copy error to clipboard', + } +); + +export const UPDATING = i18n.translate('responseOpsAlertsTable.alertsTable.lastUpdated.updating', { + defaultMessage: 'Updating...', +}); + +export const UPDATED = i18n.translate('responseOpsAlertsTable.alertsTable.lastUpdated.updated', { + defaultMessage: 'Updated', +}); + +export const INSPECT = i18n.translate('responseOpsAlertsTable.inspectDescription', { + defaultMessage: 'Inspect', +}); + +export const CLOSE = i18n.translate('responseOpsAlertsTable.inspect.modal.closeTitle', { + defaultMessage: 'Close', +}); + +export const SOMETHING_WENT_WRONG = i18n.translate( + 'responseOpsAlertsTable.inspect.modal.somethingWentWrongDescription', + { + defaultMessage: 'Sorry about that, something went wrong.', + } +); +export const INDEX_PATTERN = i18n.translate( + 'responseOpsAlertsTable.inspect.modal.indexPatternLabel', + { + defaultMessage: 'Index pattern', + } +); + +export const INDEX_PATTERN_DESC = i18n.translate( + 'responseOpsAlertsTable.inspect.modal.indexPatternDescription', + { + defaultMessage: + 'The index pattern that connected to the Elasticsearch indices. These indices can be configured in Kibana > Advanced Settings.', + } +); + +export const QUERY_TIME = i18n.translate('responseOpsAlertsTable.inspect.modal.queryTimeLabel', { + defaultMessage: 'Query time', +}); + +export const QUERY_TIME_DESC = i18n.translate( + 'responseOpsAlertsTable.inspect.modal.queryTimeDescription', + { + defaultMessage: + 'The time it took to process the query. Does not include the time to send the request or parse it in the browser.', + } +); + +export const REQUEST_TIMESTAMP = i18n.translate( + 'responseOpsAlertsTable.inspect.modal.reqTimestampLabel', + { + defaultMessage: 'Request timestamp', + } +); + +export const REQUEST_TIMESTAMP_DESC = i18n.translate( + 'responseOpsAlertsTable.inspect.modal.reqTimestampDescription', + { + defaultMessage: 'Time when the start of the request has been logged', + } +); + +export const SELECTED_ALERTS = (selectedAlertsFormatted: string, selectedAlerts: number) => + i18n.translate('responseOpsAlertsTable.toolbar.bulkActions.selectedAlertsTitle', { + values: { selectedAlertsFormatted, selectedAlerts }, + defaultMessage: + 'Selected {selectedAlertsFormatted} {selectedAlerts, plural, =1 {alert} other {alerts}}', + }); + +export const SELECT_ALL_ALERTS = (totalAlertsFormatted: string, totalAlerts: number) => + i18n.translate('responseOpsAlertsTable.toolbar.bulkActions.selectAllAlertsTitle', { + values: { totalAlertsFormatted, totalAlerts }, + defaultMessage: + 'Select all {totalAlertsFormatted} {totalAlerts, plural, =1 {alert} other {alerts}}', + }); + +export const CLEAR_SELECTION = i18n.translate( + 'responseOpsAlertsTable.toolbar.bulkActions.clearSelectionTitle', + { + defaultMessage: 'Clear selection', + } +); + +export const COLUMN_HEADER_ARIA_LABEL = i18n.translate( + 'responseOpsAlertsTable.bulkActions.columnHeader.AriaLabel', + { + defaultMessage: 'Select all rows', + } +); + +export const SELECT_ROW_ARIA_LABEL = (displayedRowIndex: number) => + i18n.translate('responseOpsAlertsTable.bulkActions.selectRowCheckbox.AriaLabel', { + values: { displayedRowIndex }, + defaultMessage: 'Select row {displayedRowIndex}', + }); + +export const ADD_TO_EXISTING_CASE = i18n.translate( + 'xpack.triggersActionsUI.alerts.table.actions.addToCase', + { + defaultMessage: 'Add to existing case', + } +); + +export const ADD_TO_NEW_CASE = i18n.translate( + 'xpack.triggersActionsUI.alerts.table.actions.addToNewCase', + { + defaultMessage: 'Add to new case', + } +); + +export const NO_ALERTS_ADDED_TO_CASE = i18n.translate( + 'xpack.triggersActionsUI.alerts.table.actions.noAlertsAddedToCaseTitle', + { + defaultMessage: 'No alerts added to the case', + } +); + +export const ALERTS_ALREADY_ATTACHED_TO_CASE = i18n.translate( + 'xpack.triggersActionsUI.alerts.table.actions.alertsAlreadyAttachedToCase', + { + defaultMessage: 'All selected alerts are already attached to the case', + } +); + +export const MARK_AS_UNTRACKED = i18n.translate( + 'xpack.triggersActionsUI.alerts.table.actions.markAsUntracked', + { + defaultMessage: 'Mark as untracked', + } +); + +export const MUTE = i18n.translate('xpack.triggersActionsUI.alerts.table.actions.mute', { + defaultMessage: 'Mute', +}); + +export const UNMUTE = i18n.translate('xpack.triggersActionsUI.alerts.table.actions.unmute', { + defaultMessage: 'Unmute', +}); diff --git a/packages/response-ops/alerts_table/tsconfig.json b/packages/response-ops/alerts_table/tsconfig.json new file mode 100644 index 0000000000000..99d9f4e260b86 --- /dev/null +++ b/packages/response-ops/alerts_table/tsconfig.json @@ -0,0 +1,50 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "@emotion/react/types/css-prop", + "@kbn/ambient-ui-types" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/rule-data-utils", + "@kbn/i18n", + "@kbn/core", + "@kbn/alerting-types", + "@kbn/cases-components", + "@kbn/core-http-browser", + "@kbn/alerting-plugin", + "@kbn/actions-plugin", + "@kbn/rule-registry-plugin", + "@kbn/kibana-utils-plugin", + "@kbn/alerts-ui-shared", + "@kbn/field-formats-plugin", + "@kbn/data-plugin", + "@kbn/response-ops-alerts-fields-browser", + "@kbn/core-notifications-browser", + "@kbn/i18n-react", + "@kbn/test-jest-helpers", + "@kbn/ui-theme", + "@kbn/react-kibana-context-theme", + "@kbn/core-http-browser-mocks", + "@kbn/core-application-browser-mocks", + "@kbn/response-ops-alerts-apis", + "@kbn/core-notifications-browser-mocks", + "@kbn/licensing-plugin", + "@kbn/core-application-browser", + "@kbn/core-ui-settings-browser-mocks", + "@kbn/core-ui-settings-browser", + "@kbn/core-user-profile-browser-mocks", + "@kbn/utility-types-jest", + ] +} diff --git a/packages/response-ops/alerts_table/types.ts b/packages/response-ops/alerts_table/types.ts new file mode 100644 index 0000000000000..f86b388438aa8 --- /dev/null +++ b/packages/response-ops/alerts_table/types.ts @@ -0,0 +1,569 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { + JSX, + ComponentClass, + ComponentProps, + ComponentType, + Dispatch, + FC, + Key, + MutableRefObject, + ReactNode, + RefAttributes, + SetStateAction, +} from 'react'; +import { + AlertConsumers, + ALERT_CASE_IDS, + ALERT_STATUS, + ALERT_MAINTENANCE_WINDOW_IDS, + type ValidFeatureId, +} from '@kbn/rule-data-utils'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { EsQuerySnapshot, LegacyField } from '@kbn/alerting-types'; +import type { + EuiDataGridColumn, + EuiDataGridColumnCellAction, + EuiDataGridControlColumn, + EuiDataGridOnColumnResizeHandler, + EuiDataGridProps, + EuiDataGridRefProps, + EuiDataGridSorting, + EuiDataGridToolBarVisibilityOptions, +} from '@elastic/eui'; +import type { + MappingRuntimeFields, + QueryDslQueryContainer, + SortCombinations, +} from '@elastic/elasticsearch/lib/api/types'; +import type { BrowserFields } from '@kbn/alerting-types'; +import type { SetRequired } from 'type-fest'; +import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { Alert } from '@kbn/alerting-types'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { FieldBrowserOptions } from '@kbn/response-ops-alerts-fields-browser'; +import type { MutedAlerts } from '@kbn/response-ops-alerts-apis/types'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; +import type { ApplicationStart } from '@kbn/core-application-browser'; +import type { SettingsStart } from '@kbn/core-ui-settings-browser'; +import type { Case } from './apis/bulk_get_cases'; + +export interface Consumer { + id: AlertConsumers; + name: string; +} + +export type AlertsTableSupportedConsumers = Exclude; + +export type CellComponent = NonNullable; + +export type CellComponentProps = ComponentProps; + +export interface SystemCellComponentMap { + [ALERT_STATUS]: CellComponent; + [ALERT_CASE_IDS]: CellComponent; + [ALERT_MAINTENANCE_WINDOW_IDS]: CellComponent; +} + +export type SystemCellId = keyof SystemCellComponentMap; + +type UseCasesAddToNewCaseFlyout = (props?: Record & { onSuccess: () => void }) => { + open: ({ attachments }: { attachments: any[] }) => void; + close: () => void; +}; + +type UseCasesAddToExistingCaseModal = ( + props?: Record & { onSuccess: () => void } +) => { + open: ({ + getAttachments, + }: { + getAttachments: ({ theCase }: { theCase?: { id: string } }) => any[]; + }) => void; + close: () => void; +}; + +/** + * Minimal cases service interface required by the alerts table + * + * We don't use the full cases service interface to avoid circular dependencies + */ +export interface CasesService { + ui: { + getCasesContext: () => FC; + }; + hooks: { + useCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout; + useCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal; + }; + helpers: { + groupAlertsByRule: (items: any[]) => any[]; + canUseCases: (owners: Array<'securitySolution' | 'observability' | 'cases'>) => any; + }; +} + +type MergeProps = T extends (args: infer Props) => unknown + ? (args: Props & AP) => ReactNode + : T extends ComponentClass + ? ComponentClass + : never; + +export interface AlertWithLegacyFormats { + alert: Alert; + /** + * @deprecated + */ + legacyAlert: LegacyField[]; + /** + * @deprecated + */ + ecsAlert: any; +} + +export interface AlertsTableProps + extends PublicAlertsDataGridProps { + /** + * A unique identifier used to persist the table state in localStorage + */ + id: string; + /** + * The columns to be displayed in the table (row selection checkboxes + * and actions column are prepended automatically) + */ + columns?: EuiDataGridProps['columns']; + /** + * A boolean expression or list of ids to refine the alerts search query + */ + query: Pick; + /** + * The initial sort configuration + */ + initialSort?: SortCombinations[]; + /** + * The initial page size. Allowed values are 10, 20, 50, 100 + */ + initialPageSize?: number; + /** + * Alert document fields available to be displayed in the table as columns + * + * If provided, the table will not fetch fields from the alerts index and + * use these instead. + */ + browserFields?: BrowserFields; + /** + * Update callback fired when any render context prop is changed + * + * Suitable to extract updated alerts information and other context properties + */ + onUpdate?: (context: RenderContext) => void; + /** + * Callback fired when the alerts have been first loaded + */ + onLoaded?: (alerts: Alert[], columns: EuiDataGridColumn[]) => void; + /** + * Any runtime mappings to be applied to the alerts search request + */ + runtimeMappings?: MappingRuntimeFields; + /** + * Toggles the built-in alert status column visibility + */ + showAlertStatusWithFlapping?: boolean; + /** + * Customizations to the data grid toolbar + */ + toolbarVisibility?: EuiDataGridToolBarVisibilityOptions; + /** + * Allows to consumers of the table to decide to highlight a row based on the current alert. + */ + shouldHighlightRow?: (alert: Alert) => boolean; + /** + * Enable when rows may have variable heights (disables virtualization) + */ + dynamicRowHeight?: boolean; + emptyStateHeight?: 'tall' | 'short'; + /** + * An object used to compose the render context passed to all render functions as part of their + * props + */ + additionalContext?: AC; + /** + * Cell content render function + */ + renderCellValue?: MergeProps< + EuiDataGridProps['renderCellValue'], + RenderContext & AlertWithLegacyFormats + >; + /** + * Cell popover render function + */ + renderCellPopover?: MergeProps< + EuiDataGridProps['renderCellPopover'], + RenderContext & { alert: Alert } + >; + /** + * Actions cell render function + */ + renderActionsCell?: MergeProps< + EuiDataGridControlColumn['rowCellRender'], + RenderContext & + AlertWithLegacyFormats & { setIsActionLoading?: (isLoading: boolean) => void } + >; + /** + * Additional toolbar controls render function + */ + renderAdditionalToolbarControls?: ComponentRenderer; + /** + * Flyout header render function + */ + renderFlyoutHeader?: FlyoutSectionRenderer; + /** + * Flyout body render function + */ + renderFlyoutBody?: FlyoutSectionRenderer; + /** + * Flyout footer render function + */ + renderFlyoutFooter?: FlyoutSectionRenderer; + /** + * Timestamp of the last data refetch request + */ + lastReloadRequestTime?: number; + /** + * Dependencies + */ + services: { + data: DataPublicPluginStart; + http: HttpStart; + notifications: NotificationsStart; + fieldFormats: FieldFormatsStart; + application: ApplicationStart; + licensing: LicensingPluginStart; + settings: SettingsStart; + /** + * The cases service is optional: cases features will be disabled if not provided + */ + cases?: CasesService; + }; +} + +/** + * A utility type to extract the type of a prop from `AlertsTableProps`, excluding `undefined`. + */ +export type GetAlertsTableProp = NonNullable< + AlertsTableProps[PropKey] +>; + +export interface AlertsTableImperativeApi { + refresh: () => void; + toggleColumn: (columnId: string) => void; +} + +export type AlertsTablePropsWithRef = AlertsTableProps & + RefAttributes; + +export type FlyoutSectionProps = + RenderContext & { + alert: Alert; + flyoutIndex: number; + isLoading: boolean; + onClose: () => void; + onPaginate: (pageIndex: number) => void; + }; + +export type FlyoutSectionRenderer = ComponentType< + FlyoutSectionProps +>; + +// Intentional empty interface since using `object` is too permissive +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AdditionalContext {} + +export type RenderContext = { + tableId?: string; + dataGridRef: MutableRefObject; + + /** + * Refetches all the queries, resetting the alerts pagination if necessary + */ + refresh: () => void; + + /** + * True if any of the active queries is fetching + */ + isLoading: boolean; + + isLoadingAlerts: boolean; + alerts: Alert[]; + /** + * @deprecated + */ + oldAlertsData: LegacyField[][]; + /** + * @deprecated + */ + ecsAlertsData: any[]; + alertsCount: number; + browserFields: BrowserFields; + + isLoadingMutedAlerts: boolean; + mutedAlerts?: MutedAlerts; + + isLoadingCases: boolean; + cases?: Map; + + isLoadingMaintenanceWindows: boolean; + maintenanceWindows?: Map; + + pageIndex: number; + pageSize: number; + + openAlertInFlyout: (alertId: string) => void; + + showAlertStatusWithFlapping?: boolean; + + bulkActionsStore: [BulkActionsState, Dispatch]; +} & SetRequired< + Pick< + AlertsTableProps, + | 'columns' + | 'renderCellValue' + | 'renderCellPopover' + | 'renderActionsCell' + | 'renderFlyoutHeader' + | 'renderFlyoutBody' + | 'renderFlyoutFooter' + | 'services' + >, + 'columns' +> & + AC; + +export type ComponentRenderer = ComponentType>; + +export interface CellActionsOptions { + /** + * Resolves the cell actions for a given column + */ + getCellActionsForColumn: (columnId: string, columnIndex: number) => EuiDataGridColumnCellAction[]; + visibleCellActions?: number; + disabledCellActions?: string[]; +} + +export interface PublicAlertsDataGridProps + extends Omit< + EuiDataGridProps, + | 'renderCellPopover' + | 'renderCellValue' + | 'aria-labelledby' + | 'columnVisibility' + | 'rowCount' + | 'sorting' + | 'cellContext' + | 'pagination' + | 'columns' + > { + ruleTypeIds: string[]; + consumers?: string[]; + /** + * If true, shows a button in the table toolbar to inspect the search alerts request + */ + showInspectButton?: boolean; + /** + * Cases-specific configuration options + */ + casesConfiguration?: { + featureId: string; + owner: Parameters[0]; + appId?: string; + syncAlerts?: boolean; + }; + /** + * If true, hides the bulk actions controls + */ + hideBulkActions?: boolean; + /** + * A getter to customize the bulk actions menu items + * based on the current alerts search query used + */ + getBulkActions?: ( + query: Pick, + refresh: () => void + ) => BulkActionsPanelConfig[]; + /** + * Width of the actions column + */ + actionsColumnWidth?: number; + /** + * Options passed to the fields browser modal + */ + fieldsBrowserOptions?: FieldBrowserOptions; + /** + * Options to customize the actions menu for each cell + */ + cellActionsOptions?: CellActionsOptions; +} + +export interface AlertsDataGridProps + extends PublicAlertsDataGridProps { + renderContext: RenderContext; + additionalToolbarControls?: ReactNode; + pageSizeOptions?: number[]; + leadingControlColumns?: EuiDataGridControlColumn[]; + trailingControlColumns?: EuiDataGridControlColumn[]; + visibleColumns: string[]; + 'data-test-subj': string; + onToggleColumn: (columnId: string) => void; + onResetColumns: () => void; + onChangeVisibleColumns: (newColumns: string[]) => void; + onColumnResize?: EuiDataGridOnColumnResizeHandler; + query: Pick; + showInspectButton?: boolean; + toolbarVisibility?: EuiDataGridToolBarVisibilityOptions; + /** + * Allows to consumers of the table to decide to highlight a row based on the current alert. + */ + shouldHighlightRow?: (alert: Alert) => boolean; + /** + * Enable when rows may have variable heights (disables virtualization) + */ + dynamicRowHeight?: boolean; + featureIds?: ValidFeatureId[]; + sort: SortCombinations[]; + alertsQuerySnapshot?: EsQuerySnapshot; + onSortChange: (sort: EuiDataGridSorting['columns']) => void; + flyoutAlertIndex: number; + setFlyoutAlertIndex: Dispatch>; + onPaginateFlyout: (nextPageIndex: number) => void; + onChangePageSize: (size: number) => void; + onChangePageIndex: (index: number) => void; +} + +export type AlertActionsProps = + RenderContext & { + key?: Key; + alert: Alert; + onActionExecuted?: () => void; + isAlertDetailsEnabled?: boolean; + /** + * Implement this to resolve your app's specific rule page path, return null to avoid showing the link + */ + resolveRulePagePath?: (ruleId: string, currentPageId: string) => string | null; + /** + * Implement this to resolve your app's specific alert page path, return null to avoid showing the link + */ + resolveAlertPagePath?: (alertId: string, currentPageId: string) => string | null; + }; + +export interface BulkActionsConfig { + label: string; + key: string; + 'data-test-subj'?: string; + disableOnQuery: boolean; + disabledLabel?: string; + onClick?: ( + selectedIds: TimelineItem[], + isAllSelected: boolean, + setIsBulkActionsLoading: (isLoading: boolean) => void, + clearSelection: () => void, + refresh: () => void + ) => void; + panel?: number; +} + +interface PanelConfig { + id: number; + title?: JSX.Element | string; + 'data-test-subj'?: string; +} + +export interface RenderContentPanelProps { + alertItems: TimelineItem[]; + setIsBulkActionsLoading: (isLoading: boolean) => void; + isAllSelected?: boolean; + clearSelection?: () => void; + refresh?: () => void; + closePopoverMenu: () => void; +} + +interface ContentPanelConfig extends PanelConfig { + renderContent: (args: RenderContentPanelProps) => JSX.Element; + items?: never; +} + +interface ItemsPanelConfig extends PanelConfig { + content?: never; + items: BulkActionsConfig[]; +} + +export type BulkActionsPanelConfig = ItemsPanelConfig | ContentPanelConfig; + +export enum BulkActionsVerbs { + add = 'add', + delete = 'delete', + clear = 'clear', + selectCurrentPage = 'selectCurrentPage', + selectAll = 'selectAll', + rowCountUpdate = 'rowCountUpdate', + updateRowLoadingState = 'updateRowLoadingState', + updateAllLoadingState = 'updateAllLoadingState', +} + +export interface BulkActionsReducerAction { + action: BulkActionsVerbs; + rowIndex?: number; + rowCount?: number; + isLoading?: boolean; +} + +export interface BulkActionsState { + rowSelection: Map; + isAllSelected: boolean; + areAllVisibleRowsSelected: boolean; + rowCount: number; + updatedAt: number; +} + +export interface AlertsTableFlyoutBaseProps { + alert: Alert; + isLoading: boolean; + id?: string; +} + +export type RowSelection = Map; + +export interface RowSelectionState { + isLoading: boolean; +} + +export enum AlertsField { + name = 'kibana.alert.rule.name', + reason = 'kibana.alert.reason', + uuid = 'kibana.alert.rule.uuid', + case_ids = 'kibana.alert.case_ids', +} + +/* + * Duplicated just for legacy reasons. Timelines plugin will be removed but + * as long as the integration still work with Timelines we have to keep it + */ +export interface TimelineItem { + _id: string; + _index?: string | null; + data: TimelineNonEcsData[]; + ecs: { _id: string; _index?: string }; +} + +export interface TimelineNonEcsData { + field: string; + value?: string[] | null; +} diff --git a/packages/response-ops/alerts_table/utils/react.ts b/packages/response-ops/alerts_table/utils/react.ts new file mode 100644 index 0000000000000..fbd117e4c1378 --- /dev/null +++ b/packages/response-ops/alerts_table/utils/react.ts @@ -0,0 +1,24 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { forwardRef, memo, ReactElement } from 'react'; + +/** + * A `React.memo` variant that keeps generic type information + */ +export const typedMemo: (c: T) => T = memo; + +/** + * A `React.forwardRef` variant that keeps generic type information + */ +export function typedForwardRef( + render: (props: P, ref: React.Ref) => ReactElement +): (props: P & React.RefAttributes) => ReactElement { + return forwardRef(render) as any; +} diff --git a/packages/response-ops/alerts_table/utils/storage.test.ts b/packages/response-ops/alerts_table/utils/storage.test.ts new file mode 100644 index 0000000000000..b7e5c5c379a55 --- /dev/null +++ b/packages/response-ops/alerts_table/utils/storage.test.ts @@ -0,0 +1,100 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { MockedKeys } from '@kbn/utility-types-jest'; +import { Storage } from './storage'; +import { IStorageWrapper, IStorage } from '@kbn/kibana-utils-plugin/public'; + +const payload = { first: 'john', last: 'smith' }; +const createMockStore = (): MockedKeys => { + let store: Record = {}; + return { + getItem: jest.fn().mockImplementation((key) => store[key]), + setItem: jest.fn().mockImplementation((key, value) => (store[key] = value)), + removeItem: jest.fn().mockImplementation((key: string) => delete store[key]), + clear: jest.fn().mockImplementation(() => (store = {})), + }; +}; + +describe('StorageService', () => { + let storage: IStorageWrapper; + let mockStore: MockedKeys; + + beforeEach(() => { + jest.resetAllMocks(); + mockStore = createMockStore(); + storage = new Storage(mockStore); + }); + + describe('expected API', () => { + test('should have expected methods', () => { + expect(typeof storage.get).toBe('function'); + expect(typeof storage.set).toBe('function'); + expect(typeof storage.remove).toBe('function'); + expect(typeof storage.clear).toBe('function'); + }); + }); + + describe('call behavior', () => { + test('should call getItem on the store', () => { + storage.get('name'); + + expect(mockStore.getItem).toHaveBeenCalledTimes(1); + }); + + test('should call setItem on the store', () => { + storage.set('name', 'john smith'); + + expect(mockStore.setItem).toHaveBeenCalledTimes(1); + }); + + test('should call removeItem on the store', () => { + storage.remove('name'); + + expect(mockStore.removeItem).toHaveBeenCalledTimes(1); + }); + + test('should call clear on the store', () => { + storage.clear(); + + expect(mockStore.clear).toHaveBeenCalledTimes(1); + }); + }); + + describe('json data', () => { + test('should parse JSON when reading from the store', () => { + mockStore.getItem = jest.fn().mockImplementationOnce(() => JSON.stringify(payload)); + + const data = storage.get('name'); + expect(data).toEqual(payload); + }); + + test('should write JSON string to the store', () => { + const key = 'name'; + const value = payload; + + storage.set(key, value); + expect(mockStore.setItem).toHaveBeenCalledWith(key, JSON.stringify(value)); + }); + }); + + describe('expected responses', () => { + test('should return null when not exists', () => { + const data = storage.get('notexists'); + expect(data).toBe(null); + }); + + test('should return null when invalid JSON', () => { + mockStore.getItem = jest.fn().mockImplementationOnce(() => 'not: json'); + + const data = storage.get('name'); + expect(data).toBe(null); + }); + }); +}); diff --git a/packages/response-ops/alerts_table/utils/storage.ts b/packages/response-ops/alerts_table/utils/storage.ts new file mode 100644 index 0000000000000..1ec42311c4310 --- /dev/null +++ b/packages/response-ops/alerts_table/utils/storage.ts @@ -0,0 +1,55 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { IStorage, IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; + +export class Storage implements IStorageWrapper { + public store: IStorage; + + constructor(store: IStorage) { + this.store = store; + } + + public get = (key: string) => { + if (!this.store) { + return null; + } + + const storageItem = this.store.getItem(key); + if (storageItem === null) { + return null; + } + + try { + return JSON.parse(storageItem); + } catch (error) { + return null; + } + }; + + public set = (key: string, value: any, includeUndefined: boolean = false) => { + const replacer = includeUndefined + ? (_: string, currentValue: any) => + typeof currentValue === 'undefined' ? null : currentValue + : undefined; + try { + return this.store.setItem(key, JSON.stringify(value, replacer)); + } catch (e) { + return false; + } + }; + + public remove = (key: string) => { + return this.store.removeItem(key); + }; + + public clear = () => { + return this.store.clear(); + }; +} diff --git a/packages/response-ops/alerts_table/utils/test.ts b/packages/response-ops/alerts_table/utils/test.ts new file mode 100644 index 0000000000000..908127ff6850d --- /dev/null +++ b/packages/response-ops/alerts_table/utils/test.ts @@ -0,0 +1,76 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { DeepPartial } from 'utility-types'; +import MaybeMockedDeep = jest.MaybeMockedDeep; + +/** + * Utility function to create a partial object mock without losing type information + * + * @example + * ```ts + * const mockedObject = createPartialObjectMock({ prop1: 'value' }); + * ``` + */ +export const createPartialObjectMock = (partialObject: DeepPartial) => + partialObject as MaybeMockedDeep; + +/* eslint-disable no-console */ +export const testQueryClientConfig = { + defaultOptions: { + queries: { + retry: false, + }, + }, + logger: { + log: console.log, + warn: console.warn, + error: () => {}, + }, +}; + +export const getJsDomPerformanceFix = () => { + const originalGetComputedStyle = Object.assign({}, window.getComputedStyle); + + return { + fix: () => { + // The JSDOM implementation is too slow + // Especially for dropdowns that try to position themselves + // perf issue - https://github.com/jsdom/jsdom/issues/3234 + Object.defineProperty(window, 'getComputedStyle', { + value: (el: HTMLElement) => { + /** + * This is based on the jsdom implementation of getComputedStyle + * https://github.com/jsdom/jsdom/blob/9dae17bf0ad09042cfccd82e6a9d06d3a615d9f4/lib/jsdom/browser/Window.js#L779-L820 + * + * It is missing global style parsing and will only return styles applied directly to an element. + * Will not return styles that are global or from emotion + */ + const declaration = new CSSStyleDeclaration(); + const { style } = el; + + Array.prototype.forEach.call(style, (property: string) => { + declaration.setProperty( + property, + style.getPropertyValue(property), + style.getPropertyPriority(property) + ); + }); + + return declaration; + }, + configurable: true, + writable: true, + }); + }, + cleanup: () => { + Object.defineProperty(window, 'getComputedStyle', originalGetComputedStyle); + }, + }; +}; diff --git a/src/platform/packages/shared/kbn-alerting-types/alerts_types.ts b/src/platform/packages/shared/kbn-alerting-types/alerts_types.ts new file mode 100644 index 0000000000000..059815fe0dc61 --- /dev/null +++ b/src/platform/packages/shared/kbn-alerting-types/alerts_types.ts @@ -0,0 +1,40 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { TechnicalRuleDataFieldName } from '@kbn/rule-data-utils'; +import { JsonValue } from '@kbn/utility-types'; + +export interface MetaAlertFields { + _id: string; + _index: string; +} + +export interface LegacyField { + field: string; + value: Array; +} + +export interface EsQuerySnapshot { + request: string[]; + response: string[]; +} + +export type KnownAlertFields = { + [Property in TechnicalRuleDataFieldName]?: JsonValue[]; +}; + +export type UnknownAlertFields = Record; + +/** + * Alert document type as returned by alerts search requests + * + * Use the AdditionalFields type parameter to add additional fields to the alert type + */ +export type Alert = + KnownAlertFields & AdditionalFields & MetaAlertFields; diff --git a/src/platform/packages/shared/kbn-alerting-types/index.ts b/src/platform/packages/shared/kbn-alerting-types/index.ts index b2288900a1248..b8d1e7f59286b 100644 --- a/src/platform/packages/shared/kbn-alerting-types/index.ts +++ b/src/platform/packages/shared/kbn-alerting-types/index.ts @@ -10,7 +10,7 @@ export * from './action_group_types'; export * from './action_variable'; export * from './alert_fields_type'; -export * from './alert_type'; +export * from './alerts_types'; export * from './alerting_framework_health_types'; export * from './builtin_action_groups_types'; export * from './circuit_breaker_message_header'; diff --git a/src/platform/packages/shared/kbn-alerting-types/search_strategy_types.ts b/src/platform/packages/shared/kbn-alerting-types/search_strategy_types.ts index 9df72e4fa7886..3568882d6f891 100644 --- a/src/platform/packages/shared/kbn-alerting-types/search_strategy_types.ts +++ b/src/platform/packages/shared/kbn-alerting-types/search_strategy_types.ts @@ -13,8 +13,8 @@ import type { QueryDslFieldAndFormat, QueryDslQueryContainer, SortCombinations, -} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { Alert } from './alert_type'; +} from '@elastic/elasticsearch/lib/api/types'; +import type { Alert } from './alerts_types'; export type RuleRegistrySearchRequest = IEsSearchRequest & { ruleTypeIds: string[]; diff --git a/src/platform/packages/shared/kbn-alerting-types/tsconfig.json b/src/platform/packages/shared/kbn-alerting-types/tsconfig.json index feac5e5147156..5802cef6614b8 100644 --- a/src/platform/packages/shared/kbn-alerting-types/tsconfig.json +++ b/src/platform/packages/shared/kbn-alerting-types/tsconfig.json @@ -24,6 +24,7 @@ "@kbn/es-query", "@kbn/field-formats-plugin", "@kbn/data-views-plugin", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/utility-types" ] } diff --git a/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_fields_table/index.tsx b/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_fields_table/index.tsx index d00f888feff00..1b4fd1da6e2c2 100644 --- a/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_fields_table/index.tsx +++ b/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_fields_table/index.tsx @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiInMemoryTable, @@ -18,8 +19,7 @@ import { useEuiFontSize, } from '@elastic/eui'; import { css } from '@emotion/react'; -import React, { memo, useMemo } from 'react'; -import { Alert } from '@kbn/alerting-types'; +import type { Alert } from '@kbn/alerting-types'; import { useEuiTablePersist } from '@kbn/shared-ux-table-persist'; export const search = { diff --git a/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx b/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx index 7f08a1de7e7f7..fca33ef752661 100644 --- a/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx +++ b/src/platform/packages/shared/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx @@ -14,7 +14,7 @@ import { AlertStatus, ALERT_STATUS_RECOVERED, ALERT_STATUS_UNTRACKED } from '@kb export interface AlertLifecycleStatusBadgeProps { alertStatus: AlertStatus; - flapping?: boolean | string; + flapping?: boolean; } const ACTIVE_LABEL = i18n.translate( @@ -83,19 +83,9 @@ const getBadgeProps = (alertStatus: AlertStatus, flapping: boolean | undefined): }; }; -const castFlapping = (flapping: boolean | string | undefined) => { - if (typeof flapping === 'string') { - return flapping === 'true'; - } - return flapping; -}; - export const AlertLifecycleStatusBadge = memo((props: AlertLifecycleStatusBadgeProps) => { const { alertStatus, flapping } = props; - - const castedFlapping = castFlapping(flapping); - - const { label, color, iconProps, isDisabled } = getBadgeProps(alertStatus, castedFlapping); + const { label, color, iconProps, isDisabled } = getBadgeProps(alertStatus, flapping ?? false); return ( ; enabled?: boolean; + context?: UseQueryOptions['context']; } const getFilteredIndex = ({ @@ -67,6 +68,7 @@ export const useLoadRuleTypesQuery = ({ toasts, filteredRuleTypes, registeredRuleTypes, + context, enabled = true, }: UseRuleTypesProps) => { const queryFn = () => { @@ -91,6 +93,7 @@ export const useLoadRuleTypesQuery = ({ // other state-sharing solutions turned out to be overly complex and less readable staleTime: 60 * 1000, enabled, + context, }); const filteredIndex = useMemo( diff --git a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/test_utils/wrapper.tsx b/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/test_utils/wrapper.tsx new file mode 100644 index 0000000000000..347af198f7cc6 --- /dev/null +++ b/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/test_utils/wrapper.tsx @@ -0,0 +1,23 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import React, { PropsWithChildren } from 'react'; +import { AlertsQueryContext } from '../contexts/alerts_query_context'; +import { testQueryClientConfig } from './test_query_client_config'; + +const queryClient = new QueryClient(testQueryClientConfig); + +export const Wrapper = ({ children }: PropsWithChildren) => { + return ( + + {children} + + ); +}; diff --git a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/index.ts b/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/index.ts index f9421ebec5a48..97316cf58ab29 100644 --- a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/index.ts +++ b/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/types/index.ts @@ -8,5 +8,4 @@ */ export * from './action_types'; -export * from './alerts_types'; export * from './rule_types'; diff --git a/tsconfig.base.json b/tsconfig.base.json index 125ee1b4c53c9..ae6966f0fc447 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1504,6 +1504,12 @@ "@kbn/resizable-layout-examples-plugin/*": ["examples/resizable_layout_examples/*"], "@kbn/resolver-test-plugin": ["x-pack/test/plugin_functional/plugins/resolver_test"], "@kbn/resolver-test-plugin/*": ["x-pack/test/plugin_functional/plugins/resolver_test/*"], + "@kbn/response-ops-alerts-apis": ["packages/response-ops/alerts_apis"], + "@kbn/response-ops-alerts-apis/*": ["packages/response-ops/alerts_apis/*"], + "@kbn/response-ops-alerts-fields-browser": ["packages/response-ops/alerts_fields_browser"], + "@kbn/response-ops-alerts-fields-browser/*": ["packages/response-ops/alerts_fields_browser/*"], + "@kbn/response-ops-alerts-table": ["packages/response-ops/alerts_table"], + "@kbn/response-ops-alerts-table/*": ["packages/response-ops/alerts_table/*"], "@kbn/response-ops-rule-form": ["src/platform/packages/shared/response-ops/rule_form"], "@kbn/response-ops-rule-form/*": ["src/platform/packages/shared/response-ops/rule_form/*"], "@kbn/response-ops-rule-params": ["src/platform/packages/shared/response-ops/rule_params"], diff --git a/x-pack/examples/triggers_actions_ui_example/kibana.jsonc b/x-pack/examples/triggers_actions_ui_example/kibana.jsonc index e3d9a82a2c368..4fe5504cd5ab0 100644 --- a/x-pack/examples/triggers_actions_ui_example/kibana.jsonc +++ b/x-pack/examples/triggers_actions_ui_example/kibana.jsonc @@ -17,7 +17,9 @@ "charts", "dataViews", "dataViewEditor", - "unifiedSearch" + "unifiedSearch", + "fieldFormats", + "licensing" ], "optionalPlugins": ["spaces"] } diff --git a/x-pack/examples/triggers_actions_ui_example/public/application.tsx b/x-pack/examples/triggers_actions_ui_example/public/application.tsx index 638233dd47a88..08bdadbeedaaf 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/application.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/application.tsx @@ -22,6 +22,8 @@ import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { CREATE_RULE_ROUTE, EDIT_RULE_ROUTE, RuleForm } from '@kbn/response-ops-rule-form'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { TriggersActionsUiExamplePublicStartDeps } from './plugin'; import { Page } from './components/page'; @@ -54,6 +56,8 @@ export interface TriggersActionsUiExampleComponentParams { dataViews: DataViewsPublicPluginStart; dataViewsEditor: DataViewEditorStart; unifiedSearch: UnifiedSearchPublicPluginStart; + fieldFormats: FieldFormatsStart; + licensing: LicensingPluginStart; } const TriggersActionsUiExampleApp = ({ @@ -68,6 +72,8 @@ const TriggersActionsUiExampleApp = ({ charts, dataViews, unifiedSearch, + fieldFormats, + licensing, ...startServices }: TriggersActionsUiExampleComponentParams) => { return ( @@ -169,7 +175,17 @@ const TriggersActionsUiExampleApp = ({ path="/alerts_table" render={() => ( - + )} /> @@ -266,6 +282,8 @@ export const renderApp = ( dataViews={deps.dataViews} dataViewsEditor={deps.dataViewsEditor} unifiedSearch={deps.unifiedSearch} + fieldFormats={deps.fieldFormats} + licensing={deps.licensing} {...core} /> diff --git a/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx b/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx index fd1adbc3239d2..fcf2f09d90b68 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/components/alerts_table_sandbox.tsx @@ -5,24 +5,20 @@ * 2.0. */ import React from 'react'; -import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; -import type { AlertsTableProps } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import type { AlertsTableProps } from '@kbn/response-ops-alerts-table/types'; -interface SandboxProps { - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; -} - -export const AlertsTableSandbox = ({ triggersActionsUi }: SandboxProps) => { - const { getAlertsStateTable: AlertsTable } = triggersActionsUi; - const alertStateProps: AlertsTableProps = { - id: 'observabilityCases', - ruleTypeIds: ['.es-query'], - query: { - bool: { - filter: [], - }, - }, - }; - - return ; +export const AlertsTableSandbox = ({ services }: Pick) => { + return ( + + ); }; diff --git a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx index 59d7dd254a4b6..116083b821a54 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx @@ -17,6 +17,8 @@ import { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { getConnectorType as getSystemLogExampleConnectorType } from './connector_types/system_log_example/system_log_example'; export interface TriggersActionsUiExamplePublicSetupDeps { @@ -33,6 +35,8 @@ export interface TriggersActionsUiExamplePublicStartDeps { dataViews: DataViewsPublicPluginStart; dataViewsEditor: DataViewEditorStart; unifiedSearch: UnifiedSearchPublicPluginStart; + fieldFormats: FieldFormatsStart; + licensing: LicensingPluginStart; } export class TriggersActionsUiExamplePlugin diff --git a/x-pack/examples/triggers_actions_ui_example/tsconfig.json b/x-pack/examples/triggers_actions_ui_example/tsconfig.json index d0622a4c64965..95285541dcb88 100644 --- a/x-pack/examples/triggers_actions_ui_example/tsconfig.json +++ b/x-pack/examples/triggers_actions_ui_example/tsconfig.json @@ -30,6 +30,9 @@ "@kbn/unified-search-plugin", "@kbn/data-view-editor-plugin", "@kbn/react-kibana-context-render", + "@kbn/response-ops-alerts-table", + "@kbn/field-formats-plugin", + "@kbn/licensing-plugin", "@kbn/response-ops-rule-form", ] } diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 2e0dff5f01423..01ba026afc864 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -44673,49 +44673,8 @@ "xpack.transform.wizard.nextStepButton": "Suivant", "xpack.transform.wizard.previousStepButton": "Précédent", "xpack.triggersActionsUI.alerts.breadcrumbTitle": "Alertes", - "xpack.triggersActionsUI.alerts.table.actions.addToCase": "Ajouter à un cas existant", - "xpack.triggersActionsUI.alerts.table.actions.addToNewCase": "Ajouter au nouveau cas", - "xpack.triggersActionsUI.alerts.table.actions.alertsAlreadyAttachedToCase": "Toutes les alertes sélectionnées sont déjà jointes au cas", - "xpack.triggersActionsUI.alerts.table.actions.markAsUntracked": "Marquer comme non suivi", - "xpack.triggersActionsUI.alerts.table.actions.mute": "Muet", - "xpack.triggersActionsUI.alerts.table.actions.noAlertsAddedToCaseTitle": "Aucune alerte ajoutée au cas", - "xpack.triggersActionsUI.alerts.table.actions.unmute": "Réactiver le son", "xpack.triggersActionsUI.alertsSearchBar.placeholder": "Alertes de recherche (par exemple, kibana.alert.evaluation.threshold > 75)", - "xpack.triggersActionsUI.alertsTable.actions.untrack": "Marquer comme non suivi", - "xpack.triggersActionsUI.alertsTable.alertMuted": "Alerte en sourdine", - "xpack.triggersActionsUI.alertsTable.alertsCountUnit": "{totalCount, plural, =1 {alerte} other {alertes}}", - "xpack.triggersActionsUI.alertsTable.alertUnmuted": "Son de l'alerte réactivé", - "xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle": "Une erreur s’est produite lors de la récupération des données de la fenêtre de maintenance", - "xpack.triggersActionsUI.alertsTable.configuration.errorBody": "Une erreur s'est produite lors du chargement du tableau d'alertes. Ce tableau ne dispose pas de la configuration nécessaire. Veuillez contacter votre administrateur pour obtenir de l'aide", - "xpack.triggersActionsUI.alertsTable.configuration.errorTitle": "Impossible de charger le tableau d'alertes", - "xpack.triggersActionsUI.alertsTable.evaluationThresholdColumnDescription": "Seuil d'évaluation", - "xpack.triggersActionsUI.alertsTable.evaluationValuesColumnDescription": "Valeurs d'évaluation", - "xpack.triggersActionsUI.alertsTable.filters.errorTitle": "Filtres d'alerte non pris en charge", - "xpack.triggersActionsUI.alertsTable.lastUpdated.updated": "Mis à jour", - "xpack.triggersActionsUI.alertsTable.lastUpdated.updating": "Mise à jour...", - "xpack.triggersActionsUI.alertsTable.lastUpdatedColumnDescription": "Dernière mise à jour", - "xpack.triggersActionsUI.alertsTable.loadingMutedState": "Chargement de l'état de sourdine", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowsColumnDescription": "Fenêtres de maintenance", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.endTime": "Fin", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.startTime": "Début", - "xpack.triggersActionsUI.alertsTable.moreActionsTextLabel": "Plus d'actions", - "xpack.triggersActionsUI.alertsTable.reasonColumnDescription": "Raison", - "xpack.triggersActionsUI.alertsTable.ruleCategoryColumnDescription": "Catégorie de règle", - "xpack.triggersActionsUI.alertsTable.ruleColumnDescription": "Règle", - "xpack.triggersActionsUI.alertsTable.ruleTagsColumnDescription": "Balises de règle", - "xpack.triggersActionsUI.alertsTable.startedColumnDescription": "Démarré", - "xpack.triggersActionsUI.alertsTable.statusColumnDescription": "Statut de l'alerte", - "xpack.triggersActionsUI.alertsTable.unknownErrorBody": "Une erreur s'est produite lors de l'affichage du tableau des alertes", - "xpack.triggersActionsUI.alertsTable.unknownErrorCopyToClipboardLabel": "Copier l'erreur dans le presse-papiers", - "xpack.triggersActionsUI.alertsTable.unknownErrorTitle": "Impossible d'afficher les alertes", - "xpack.triggersActionsUI.alertsTable.untrackByQuery.failedMessage": "Échec de l'arrêt du suivi des alertes par requête", - "xpack.triggersActionsUI.alertsTable.untrackByQuery.successMessage": "Alertes non suivies", - "xpack.triggersActionsUI.alertsTable.viewAlertDetails": "Afficher les détails de l'alerte", - "xpack.triggersActionsUI.alertsTable.viewRuleDetails": "Afficher les détails de la règle", "xpack.triggersActionsUI.appName": "Règles", - "xpack.triggersActionsUI.bulkActions.columnHeader.AriaLabel": "Sélectionner toutes les lignes", - "xpack.triggersActionsUI.bulkActions.selectRowCheckbox.AriaLabel": "Sélectionner la ligne {displayedRowIndex}", - "xpack.triggersActionsUI.cases.api.bulkGet": "Erreur lors de la récupération des données sur les cas", "xpack.triggersActionsUI.cases.label": "Cas", "xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "Ce type de règle requiert une licence {minimumLicenseRequired}.", "xpack.triggersActionsUI.common.constants.comparators.groupByTypes.allDocumentsLabel": "tous les documents", @@ -44739,8 +44698,6 @@ "xpack.triggersActionsUI.common.expressionItems.threshold.andLabel": "AND", "xpack.triggersActionsUI.common.expressionItems.threshold.descriptionLabel": "quand", "xpack.triggersActionsUI.common.expressionItems.threshold.popoverTitle": "quand", - "xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText": "Une erreur s'est produite lors de la recherche des alertes", - "xpack.triggersActionsUI.components.alertTable.useFetchBrowserFieldsCapabilities.errorMessageText": "Une erreur s'est produite lors du chargement des champs du navigateur", "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "Choisir…", "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel": "Basé sur vos vues de données", "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "Les informations sensibles ne sont pas importées. Veuillez entrer {encryptedFieldsLength, plural, one {la valeur} other {les valeurs}} pour {encryptedFieldsLength, plural, one {le champ suivant} other {les champs suivants}} {secretFieldsLabel}.", @@ -44803,28 +44760,6 @@ "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "Annuler", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "Supprimer {numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.descriptionText": "Vous ne pourrez pas récupérer {numIdsToDelete, plural, one {un {singleTitle} supprimé} other {des {multipleTitle} supprimés}}.", - "xpack.triggersActionsUI.empty.description": "Essayer de rechercher sur une période plus longue ou de modifier votre recherche", - "xpack.triggersActionsUI.empty.title": "Aucun résultat ne correspond à vos critères de recherche.", - "xpack.triggersActionsUI.fieldBrowser.categoriesCountTitle": "{totalCount} {totalCount, plural, =1 {catégorie} other {catégories}}", - "xpack.triggersActionsUI.fieldBrowser.categoriesTitle": "Catégories", - "xpack.triggersActionsUI.fieldBrowser.categoryLabel": "Catégorie", - "xpack.triggersActionsUI.fieldBrowser.closeButton": "Fermer", - "xpack.triggersActionsUI.fieldBrowser.descriptionForScreenReaderOnly": "Description pour le champ {field} :", - "xpack.triggersActionsUI.fieldBrowser.descriptionLabel": "Description", - "xpack.triggersActionsUI.fieldBrowser.fieldBrowserTitle": "Champs", - "xpack.triggersActionsUI.fieldBrowser.fieldLabel": "Champ", - "xpack.triggersActionsUI.fieldBrowser.fieldName": "Nom", - "xpack.triggersActionsUI.fieldBrowser.fieldsCountShowing": "Affichage", - "xpack.triggersActionsUI.fieldBrowser.fieldsCountTitle": "{totalCount, plural, =1 {champ} other {champs}}", - "xpack.triggersActionsUI.fieldBrowser.fieldsTitle": "Champs", - "xpack.triggersActionsUI.fieldBrowser.filterPlaceholder": "Nom du champ", - "xpack.triggersActionsUI.fieldBrowser.noFieldsMatchInputLabel": "Aucun champ ne correspond à {searchInput}", - "xpack.triggersActionsUI.fieldBrowser.noFieldsMatchLabel": "Aucun champ ne correspond", - "xpack.triggersActionsUI.fieldBrowser.resetFieldsLink": "Réinitialiser les champs", - "xpack.triggersActionsUI.fieldBrowser.viewAll": "tous", - "xpack.triggersActionsUI.fieldBrowser.viewColumnCheckboxAriaLabel": "Afficher la colonne {field}", - "xpack.triggersActionsUI.fieldBrowser.viewLabel": "Afficher", - "xpack.triggersActionsUI.fieldBrowser.viewSelected": "sélectionné", "xpack.triggersActionsUI.globalAlerts.alertStats.disabled": "Désactivé", "xpack.triggersActionsUI.globalAlerts.alertStats.errors": "Erreurs", "xpack.triggersActionsUI.globalAlerts.alertStats.loadError": "Impossible de charger les statistiques de règles", @@ -44837,15 +44772,6 @@ "xpack.triggersActionsUI.home.logsTabTitle": "Logs", "xpack.triggersActionsUI.home.rulesTabTitle": "Règles", "xpack.triggersActionsUI.home.sectionDescription": "Détectez les conditions à l'aide de règles.", - "xpack.triggersActionsUI.inspect.modal.closeTitle": "Fermer", - "xpack.triggersActionsUI.inspect.modal.indexPatternDescription": "Modèle d'indexation qui se connecte aux index Elasticsearch. Ces index peuvent être configurés dans Kibana > Paramètres avancés.", - "xpack.triggersActionsUI.inspect.modal.indexPatternLabel": "Modèle d'indexation", - "xpack.triggersActionsUI.inspect.modal.queryTimeDescription": "Le temps qu'il a fallu pour traiter la requête. Ne comprend pas le temps nécessaire pour envoyer la requête ni l'analyser dans le navigateur.", - "xpack.triggersActionsUI.inspect.modal.queryTimeLabel": "Heure de la requête", - "xpack.triggersActionsUI.inspect.modal.reqTimestampDescription": "Heure à laquelle le début de la requête a été enregistré", - "xpack.triggersActionsUI.inspect.modal.reqTimestampLabel": "Horodatage de la requête", - "xpack.triggersActionsUI.inspect.modal.somethingWentWrongDescription": "Désolé, un problème est survenu.", - "xpack.triggersActionsUI.inspectDescription": "Inspecter", "xpack.triggersActionsUI.jsonFieldWrapper.defaultLabel": "Éditeur JSON", "xpack.triggersActionsUI.logs.breadcrumbTitle": "Logs", "xpack.triggersActionsUI.maintenanceWindows.label": "Fenêtres de maintenance", @@ -44855,8 +44781,6 @@ "xpack.triggersActionsUI.managementSection.connectors.displayName": "Connecteurs", "xpack.triggersActionsUI.managementSection.displayDescription": "Détectez les conditions à l'aide de règles.", "xpack.triggersActionsUI.managementSection.displayName": "Règles", - "xpack.triggersActionsUI.muteAlert.error": "Erreur de sourdine de l'alerte", - "xpack.triggersActionsUI.mutedAlerts.api.get": "Erreur de récupération des données des alertes en sourdine", "xpack.triggersActionsUI.parseInterval.errorMessage": "{value} n'est pas une chaîne d'intervalle", "xpack.triggersActionsUI.ruleDetails.actions": "Actions", "xpack.triggersActionsUI.ruleDetails.cannotReadActions": "Les privilèges de la fonctionnalité du connecteur sont requises pour afficher les actions", @@ -44873,8 +44797,6 @@ "xpack.triggersActionsUI.rules.breadcrumbTitle": "Règles", "xpack.triggersActionsUI.rules.create.breadcrumbTitle": "Créer", "xpack.triggersActionsUI.rules.createRule.breadcrumbTitle": "Créer une règle", - "xpack.triggersActionsUI.rules.deleteConfirmationModal.errorNotification.descriptionText": "Impossible d'arrêter le suivi pour {uuidsCount, plural, one {alerte} other {alertes}}", - "xpack.triggersActionsUI.rules.deleteConfirmationModal.successNotification.descriptionText": "{uuidsCount, plural, one {alerte non suivie} other {alertes non suivies}}", "xpack.triggersActionsUI.rules.edit.breadcrumbTitle": "Modifier", "xpack.triggersActionsUI.rules.editRule.breadcrumbTitle": "Modifier la règle", "xpack.triggersActionsUI.ruleSnoozeScheduler.addSchedule": "Ajouter un calendrier", @@ -45018,23 +44940,6 @@ "xpack.triggersActionsUI.sections.alertsSummaryWidget.errorBody": "Une erreur s'est produite lors du chargement du récapitulatif des alertes. Contactez votre administrateur pour obtenir de l'aide.", "xpack.triggersActionsUI.sections.alertsSummaryWidget.errorTitle": "Impossible de charger le récapitulatif des alertes", "xpack.triggersActionsUI.sections.alertsSummaryWidget.title": "Activité des alertes", - "xpack.triggersActionsUI.sections.alertsTable.alertMuted": "Alerte en sourdine", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.overview": "Aperçu", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel": "Navigation dans les alertes", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.table": "Tableau", - "xpack.triggersActionsUI.sections.alertsTable.apm": "APM", - "xpack.triggersActionsUI.sections.alertsTable.column.actions": "Actions", - "xpack.triggersActionsUI.sections.alertsTable.infrastructure": "Infrastructure", - "xpack.triggersActionsUI.sections.alertsTable.logs": "Logs", - "xpack.triggersActionsUI.sections.alertsTable.ml": "Machine Learning", - "xpack.triggersActionsUI.sections.alertsTable.observability": "Observabilité", - "xpack.triggersActionsUI.sections.alertsTable.security": "Sécurité", - "xpack.triggersActionsUI.sections.alertsTable.slos": "SLO", - "xpack.triggersActionsUI.sections.alertsTable.stack": "Suite", - "xpack.triggersActionsUI.sections.alertsTable.stackMonitoring": "Monitoring de la Suite", - "xpack.triggersActionsUI.sections.alertsTable.title": "Tableau d’alertes", - "xpack.triggersActionsUI.sections.alertsTable.uptime": "Uptime", - "xpack.triggersActionsUI.sections.alertTable.viewError": "Une erreur s'est produite", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.cancelButtonLabel": "Annuler", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.confirmConnectorCloseMessage": "Vous ne pouvez pas récupérer de modifications non enregistrées.", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.discardButtonLabel": "Abandonner les modifications", @@ -45114,7 +45019,6 @@ "xpack.triggersActionsUI.sections.executionDurationChart.numberOfExecutionsOption": "{value} exécutions", "xpack.triggersActionsUI.sections.executionDurationChart.recentDurationsTitle": "Durées d'exécution récentes", "xpack.triggersActionsUI.sections.executionDurationChart.selectNumberOfExecutionDurationsLabel": "Sélectionner le nombre d'exécutions", - "xpack.triggersActionsUI.sections.globalAlerts.quickFilters.feature": "Fonctionnalité", "xpack.triggersActionsUI.sections.globalAlerts.quickFilters.status": "Statut", "xpack.triggersActionsUI.sections.isDeprecatedDescription": "Ce connecteur est déclassé. Mettez-le à jour, ou créez-en un nouveau.", "xpack.triggersActionsUI.sections.manageLicense.manageLicenseCancelButtonText": "Annuler", @@ -45496,10 +45400,6 @@ "xpack.triggersActionsUI.timeUnits.hourLabel": "{timeValue, plural, one {heure} other {heures}}", "xpack.triggersActionsUI.timeUnits.minuteLabel": "{timeValue, plural, one {minute} other {minutes}}", "xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, one {seconde} other {secondes}}", - "xpack.triggersActionsUI.toolbar.bulkActions.clearSelectionTitle": "Effacer la sélection", - "xpack.triggersActionsUI.toolbar.bulkActions.selectAllAlertsTitle": "Sélectionner un total de {totalAlertsFormatted} {totalAlerts, plural, =1 {alerte} other {alertes}}", - "xpack.triggersActionsUI.toolbar.bulkActions.selectedAlertsTitle": "{selectedAlertsFormatted} {selectedAlerts, plural, =1 {alerte sélectionnée} other {alertes sélectionnées}}", - "xpack.triggersActionsUI.unmuteAlert.error": "Erreur lors de la réactivation du son des alertes", "xpack.triggersActionsUI.updateApiKeyConfirmModal.cancelButton": "Annuler", "xpack.triggersActionsUI.updateApiKeyConfirmModal.confirmButton": "Mettre à jour", "xpack.triggersActionsUI.updateApiKeyConfirmModal.description": "Vous ne pourrez pas récupérer {idsToUpdate, plural, one {la clé} other {les clés}} de l'ancienne API", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index d79d47d56085b..e27ba7713b35f 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -44525,49 +44525,8 @@ "xpack.transform.wizard.nextStepButton": "次へ", "xpack.transform.wizard.previousStepButton": "前へ", "xpack.triggersActionsUI.alerts.breadcrumbTitle": "アラート", - "xpack.triggersActionsUI.alerts.table.actions.addToCase": "既存のケースに追加", - "xpack.triggersActionsUI.alerts.table.actions.addToNewCase": "新しいケースに追加", - "xpack.triggersActionsUI.alerts.table.actions.alertsAlreadyAttachedToCase": "すべての選択されたアラートはすでにケースに関連付けられています", - "xpack.triggersActionsUI.alerts.table.actions.markAsUntracked": "未追跡に設定", - "xpack.triggersActionsUI.alerts.table.actions.mute": "ミュート", - "xpack.triggersActionsUI.alerts.table.actions.noAlertsAddedToCaseTitle": "ケースに追加されたアラートはありません", - "xpack.triggersActionsUI.alerts.table.actions.unmute": "ミュート解除", "xpack.triggersActionsUI.alertsSearchBar.placeholder": "検索アラート(例:kibana.alert.evaluation.threshold > 75)", - "xpack.triggersActionsUI.alertsTable.actions.untrack": "未追跡に設定", - "xpack.triggersActionsUI.alertsTable.alertMuted": "アラートがミュートされました", - "xpack.triggersActionsUI.alertsTable.alertsCountUnit": "{totalCount, plural, other {アラート}}", - "xpack.triggersActionsUI.alertsTable.alertUnmuted": "アラートがミュート解除されました", - "xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle": "保守時間枠データの取得エラー", - "xpack.triggersActionsUI.alertsTable.configuration.errorBody": "アラートテーブルの読み込みエラーが発生しました。このテーブルには必要な構成がありません。ヘルプについては、管理者にお問い合わせください", - "xpack.triggersActionsUI.alertsTable.configuration.errorTitle": "アラートテーブルを読み込めません", - "xpack.triggersActionsUI.alertsTable.evaluationThresholdColumnDescription": "評価しきい値", - "xpack.triggersActionsUI.alertsTable.evaluationValuesColumnDescription": "評価値", - "xpack.triggersActionsUI.alertsTable.filters.errorTitle": "サポートされていないアラートフィルターセット", - "xpack.triggersActionsUI.alertsTable.lastUpdated.updated": "更新しました", - "xpack.triggersActionsUI.alertsTable.lastUpdated.updating": "更新中...", - "xpack.triggersActionsUI.alertsTable.lastUpdatedColumnDescription": "最終更新", - "xpack.triggersActionsUI.alertsTable.loadingMutedState": "ミュート状態を読み込み中", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowsColumnDescription": "保守時間枠", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.endTime": "終了", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.startTime": "開始", - "xpack.triggersActionsUI.alertsTable.moreActionsTextLabel": "さらにアクションを表示", - "xpack.triggersActionsUI.alertsTable.reasonColumnDescription": "理由", - "xpack.triggersActionsUI.alertsTable.ruleCategoryColumnDescription": "ルールカテゴリー", - "xpack.triggersActionsUI.alertsTable.ruleColumnDescription": "ルール", - "xpack.triggersActionsUI.alertsTable.ruleTagsColumnDescription": "ルールタグ", - "xpack.triggersActionsUI.alertsTable.startedColumnDescription": "開始", - "xpack.triggersActionsUI.alertsTable.statusColumnDescription": "アラートステータス", - "xpack.triggersActionsUI.alertsTable.unknownErrorBody": "アラートテーブルの表示中にエラーが発生しました", - "xpack.triggersActionsUI.alertsTable.unknownErrorCopyToClipboardLabel": "エラーをクリップボードにコピー", - "xpack.triggersActionsUI.alertsTable.unknownErrorTitle": "アラートを表示できません", - "xpack.triggersActionsUI.alertsTable.untrackByQuery.failedMessage": "クエリ別にアラートを追跡解除できませんでした", - "xpack.triggersActionsUI.alertsTable.untrackByQuery.successMessage": "追跡解除されたアラート", - "xpack.triggersActionsUI.alertsTable.viewAlertDetails": "アラート詳細を表示", - "xpack.triggersActionsUI.alertsTable.viewRuleDetails": "ルール詳細を表示", "xpack.triggersActionsUI.appName": "ルール", - "xpack.triggersActionsUI.bulkActions.columnHeader.AriaLabel": "すべての行を選択", - "xpack.triggersActionsUI.bulkActions.selectRowCheckbox.AriaLabel": "行\"{displayedRowIndex}\"を選択", - "xpack.triggersActionsUI.cases.api.bulkGet": "ケースデータの取得エラー", "xpack.triggersActionsUI.cases.label": "ケース", "xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "このルールタイプには{minimumLicenseRequired}ライセンスが必要です。", "xpack.triggersActionsUI.common.constants.comparators.groupByTypes.allDocumentsLabel": "すべてのドキュメント", @@ -44591,8 +44550,6 @@ "xpack.triggersActionsUI.common.expressionItems.threshold.andLabel": "AND", "xpack.triggersActionsUI.common.expressionItems.threshold.descriptionLabel": "タイミング", "xpack.triggersActionsUI.common.expressionItems.threshold.popoverTitle": "タイミング", - "xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText": "アラート検索でエラーが発生しました", - "xpack.triggersActionsUI.components.alertTable.useFetchBrowserFieldsCapabilities.errorMessageText": "ブラウザーフィールドの読み込み中にエラーが発生しました", "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "選択…", "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel": "データビューに基づく", "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "機密情報はインポートされません。次のフィールド{encryptedFieldsLength, plural, other {}}の値{encryptedFieldsLength, plural, other {}} {secretFieldsLabel}を入力してください。", @@ -44654,28 +44611,6 @@ "xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize]:[groupBy]がトップのときにはtermSizeが必要です", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "キャンセル", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "{numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}を削除", - "xpack.triggersActionsUI.empty.description": "期間を長くして検索するか、検索を変更してください", - "xpack.triggersActionsUI.empty.title": "検索条件と一致する結果がありません。", - "xpack.triggersActionsUI.fieldBrowser.categoriesCountTitle": "{totalCount} {totalCount, plural, other {カテゴリ}}", - "xpack.triggersActionsUI.fieldBrowser.categoriesTitle": "カテゴリー", - "xpack.triggersActionsUI.fieldBrowser.categoryLabel": "カテゴリー", - "xpack.triggersActionsUI.fieldBrowser.closeButton": "閉じる", - "xpack.triggersActionsUI.fieldBrowser.descriptionForScreenReaderOnly": "フィールド {field} の説明:", - "xpack.triggersActionsUI.fieldBrowser.descriptionLabel": "説明", - "xpack.triggersActionsUI.fieldBrowser.fieldBrowserTitle": "フィールド", - "xpack.triggersActionsUI.fieldBrowser.fieldLabel": "フィールド", - "xpack.triggersActionsUI.fieldBrowser.fieldName": "名前", - "xpack.triggersActionsUI.fieldBrowser.fieldsCountShowing": "表示中", - "xpack.triggersActionsUI.fieldBrowser.fieldsCountTitle": "{totalCount, plural, other {フィールド}}", - "xpack.triggersActionsUI.fieldBrowser.fieldsTitle": "フィールド", - "xpack.triggersActionsUI.fieldBrowser.filterPlaceholder": "フィールド名", - "xpack.triggersActionsUI.fieldBrowser.noFieldsMatchInputLabel": "{searchInput} に一致するフィールドがありません", - "xpack.triggersActionsUI.fieldBrowser.noFieldsMatchLabel": "一致するフィールドがありません", - "xpack.triggersActionsUI.fieldBrowser.resetFieldsLink": "フィールドをリセット", - "xpack.triggersActionsUI.fieldBrowser.viewAll": "すべて", - "xpack.triggersActionsUI.fieldBrowser.viewColumnCheckboxAriaLabel": "{field} 列を表示", - "xpack.triggersActionsUI.fieldBrowser.viewLabel": "表示", - "xpack.triggersActionsUI.fieldBrowser.viewSelected": "選択済み", "xpack.triggersActionsUI.globalAlerts.alertStats.disabled": "無効", "xpack.triggersActionsUI.globalAlerts.alertStats.errors": "エラー", "xpack.triggersActionsUI.globalAlerts.alertStats.loadError": "ルール統計情報を読み込めません", @@ -44688,15 +44623,6 @@ "xpack.triggersActionsUI.home.logsTabTitle": "ログ", "xpack.triggersActionsUI.home.rulesTabTitle": "ルール", "xpack.triggersActionsUI.home.sectionDescription": "ルールを使用する条件を検出します。", - "xpack.triggersActionsUI.inspect.modal.closeTitle": "閉じる", - "xpack.triggersActionsUI.inspect.modal.indexPatternDescription": "Elasticsearchインデックスに接続したインデックスパターンです。これらのインデックスは Kibana > 高度な設定で構成できます。", - "xpack.triggersActionsUI.inspect.modal.indexPatternLabel": "インデックスパターン", - "xpack.triggersActionsUI.inspect.modal.queryTimeDescription": "クエリの処理の所要時間です。リクエストの送信やブラウザーでのパースの時間は含まれません。", - "xpack.triggersActionsUI.inspect.modal.queryTimeLabel": "クエリー時間", - "xpack.triggersActionsUI.inspect.modal.reqTimestampDescription": "リクエストの開始が記録された時刻です", - "xpack.triggersActionsUI.inspect.modal.reqTimestampLabel": "リクエストのタイムスタンプ", - "xpack.triggersActionsUI.inspect.modal.somethingWentWrongDescription": "申し訳ございませんが、何か問題が発生しました。", - "xpack.triggersActionsUI.inspectDescription": "検査", "xpack.triggersActionsUI.jsonFieldWrapper.defaultLabel": "JSONエディター", "xpack.triggersActionsUI.logs.breadcrumbTitle": "ログ", "xpack.triggersActionsUI.maintenanceWindows.label": "保守時間枠", @@ -44706,8 +44632,6 @@ "xpack.triggersActionsUI.managementSection.connectors.displayName": "コネクター", "xpack.triggersActionsUI.managementSection.displayDescription": "ルールを使用する条件を検出します。", "xpack.triggersActionsUI.managementSection.displayName": "ルール", - "xpack.triggersActionsUI.muteAlert.error": "アラートのミュートエラー", - "xpack.triggersActionsUI.mutedAlerts.api.get": "ミュートされたアラートデータの取得エラー", "xpack.triggersActionsUI.parseInterval.errorMessage": "{value}は間隔文字列ではありません", "xpack.triggersActionsUI.ruleDetails.actions": "アクション", "xpack.triggersActionsUI.ruleDetails.cannotReadActions": "アクションを表示するには、コネクター機能の権限が必要です", @@ -44724,8 +44648,6 @@ "xpack.triggersActionsUI.rules.breadcrumbTitle": "ルール", "xpack.triggersActionsUI.rules.create.breadcrumbTitle": "作成", "xpack.triggersActionsUI.rules.createRule.breadcrumbTitle": "ルールを作成", - "xpack.triggersActionsUI.rules.deleteConfirmationModal.errorNotification.descriptionText": "{uuidsCount, plural, other {アラート}}を追跡解除できませんでした", - "xpack.triggersActionsUI.rules.deleteConfirmationModal.successNotification.descriptionText": "{uuidsCount, plural, other {アラート}}を追跡解除しました", "xpack.triggersActionsUI.rules.edit.breadcrumbTitle": "編集", "xpack.triggersActionsUI.rules.editRule.breadcrumbTitle": "ルールを編集", "xpack.triggersActionsUI.ruleSnoozeScheduler.addSchedule": "スケジュールを追加", @@ -44869,23 +44791,6 @@ "xpack.triggersActionsUI.sections.alertsSummaryWidget.errorBody": "アラート概要の読み込みエラーが発生しました。ヘルプについては、管理者にお問い合わせください。", "xpack.triggersActionsUI.sections.alertsSummaryWidget.errorTitle": "アラート概要を読み込めません", "xpack.triggersActionsUI.sections.alertsSummaryWidget.title": "アラートアクティビティ", - "xpack.triggersActionsUI.sections.alertsTable.alertMuted": "アラートがミュートされました", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.overview": "概要", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel": "アラートナビゲーション", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.table": "表", - "xpack.triggersActionsUI.sections.alertsTable.apm": "APM", - "xpack.triggersActionsUI.sections.alertsTable.column.actions": "アクション", - "xpack.triggersActionsUI.sections.alertsTable.infrastructure": "インフラストラクチャー", - "xpack.triggersActionsUI.sections.alertsTable.logs": "ログ", - "xpack.triggersActionsUI.sections.alertsTable.ml": "機械学習", - "xpack.triggersActionsUI.sections.alertsTable.observability": "Observability", - "xpack.triggersActionsUI.sections.alertsTable.security": "セキュリティ", - "xpack.triggersActionsUI.sections.alertsTable.slos": "SLO", - "xpack.triggersActionsUI.sections.alertsTable.stack": "スタック", - "xpack.triggersActionsUI.sections.alertsTable.stackMonitoring": "スタック監視", - "xpack.triggersActionsUI.sections.alertsTable.title": "アラートテーブル", - "xpack.triggersActionsUI.sections.alertsTable.uptime": "アップタイム", - "xpack.triggersActionsUI.sections.alertTable.viewError": "エラーが発生しました", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.cancelButtonLabel": "キャンセル", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.confirmConnectorCloseMessage": "保存されていない変更は回復できません。", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.discardButtonLabel": "変更を破棄", @@ -44965,7 +44870,6 @@ "xpack.triggersActionsUI.sections.executionDurationChart.numberOfExecutionsOption": "{value}実行", "xpack.triggersActionsUI.sections.executionDurationChart.recentDurationsTitle": "最近の実行期間", "xpack.triggersActionsUI.sections.executionDurationChart.selectNumberOfExecutionDurationsLabel": "実行回数を選択", - "xpack.triggersActionsUI.sections.globalAlerts.quickFilters.feature": "機能", "xpack.triggersActionsUI.sections.globalAlerts.quickFilters.status": "ステータス", "xpack.triggersActionsUI.sections.isDeprecatedDescription": "このコネクターは廃止予定です。更新するか新しく作成してください。", "xpack.triggersActionsUI.sections.manageLicense.manageLicenseCancelButtonText": "キャンセル", @@ -45346,10 +45250,6 @@ "xpack.triggersActionsUI.timeUnits.hourLabel": "{timeValue, plural, other {時間}}", "xpack.triggersActionsUI.timeUnits.minuteLabel": "{timeValue, plural, other {分}}", "xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, other {秒}}", - "xpack.triggersActionsUI.toolbar.bulkActions.clearSelectionTitle": "選択した項目をクリア", - "xpack.triggersActionsUI.toolbar.bulkActions.selectAllAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を選択", - "xpack.triggersActionsUI.toolbar.bulkActions.selectedAlertsTitle": "Selected {selectedAlertsFormatted} {selectedAlerts, plural, other {件のアラート}}", - "xpack.triggersActionsUI.unmuteAlert.error": "アラートのミュート解除エラー", "xpack.triggersActionsUI.updateApiKeyConfirmModal.cancelButton": "キャンセル", "xpack.triggersActionsUI.updateApiKeyConfirmModal.confirmButton": "更新", "xpack.triggersActionsUI.updateApiKeyConfirmModal.description": "古いAPI {idsToUpdate, plural, other {キー}}は回復できません", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index cbf6e71374494..ff085eb67664b 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -43866,49 +43866,8 @@ "xpack.transform.wizard.nextStepButton": "下一步", "xpack.transform.wizard.previousStepButton": "上一步", "xpack.triggersActionsUI.alerts.breadcrumbTitle": "告警", - "xpack.triggersActionsUI.alerts.table.actions.addToCase": "添加到现有案例", - "xpack.triggersActionsUI.alerts.table.actions.addToNewCase": "添加到新案例", - "xpack.triggersActionsUI.alerts.table.actions.alertsAlreadyAttachedToCase": "所有选定告警已附加到该案例", - "xpack.triggersActionsUI.alerts.table.actions.markAsUntracked": "标记为已取消跟踪", - "xpack.triggersActionsUI.alerts.table.actions.mute": "静音", - "xpack.triggersActionsUI.alerts.table.actions.noAlertsAddedToCaseTitle": "未向该案例添加任何告警", - "xpack.triggersActionsUI.alerts.table.actions.unmute": "取消静音", "xpack.triggersActionsUI.alertsSearchBar.placeholder": "搜索告警(例如 kibana.alert.evaluation.threshold > 75)", - "xpack.triggersActionsUI.alertsTable.actions.untrack": "标记为已取消跟踪", - "xpack.triggersActionsUI.alertsTable.alertMuted": "告警已静音", - "xpack.triggersActionsUI.alertsTable.alertsCountUnit": "{totalCount, plural, other {告警}}", - "xpack.triggersActionsUI.alertsTable.alertUnmuted": "告警已取消静音", - "xpack.triggersActionsUI.alertsTable.api.bulkGetMaintenanceWindow.errorTitle": "提取维护窗口数据时出错", - "xpack.triggersActionsUI.alertsTable.configuration.errorBody": "加载告警表时出现错误。此表缺少所需配置。请联系您的管理员寻求帮助", - "xpack.triggersActionsUI.alertsTable.configuration.errorTitle": "无法加载告警表", - "xpack.triggersActionsUI.alertsTable.evaluationThresholdColumnDescription": "评估阈值", - "xpack.triggersActionsUI.alertsTable.evaluationValuesColumnDescription": "评估值", - "xpack.triggersActionsUI.alertsTable.filters.errorTitle": "告警筛选集不受支持", - "xpack.triggersActionsUI.alertsTable.lastUpdated.updated": "已更新", - "xpack.triggersActionsUI.alertsTable.lastUpdated.updating": "正在更新......", - "xpack.triggersActionsUI.alertsTable.lastUpdatedColumnDescription": "上次更新时间", - "xpack.triggersActionsUI.alertsTable.loadingMutedState": "正在加载已静音状态", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowsColumnDescription": "维护窗口", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.endTime": "结束", - "xpack.triggersActionsUI.alertsTable.maintenanceWindowTooltip.startTime": "启动", - "xpack.triggersActionsUI.alertsTable.moreActionsTextLabel": "更多操作", - "xpack.triggersActionsUI.alertsTable.reasonColumnDescription": "原因", - "xpack.triggersActionsUI.alertsTable.ruleCategoryColumnDescription": "规则类别", - "xpack.triggersActionsUI.alertsTable.ruleColumnDescription": "规则", - "xpack.triggersActionsUI.alertsTable.ruleTagsColumnDescription": "规则标签", - "xpack.triggersActionsUI.alertsTable.startedColumnDescription": "已启动", - "xpack.triggersActionsUI.alertsTable.statusColumnDescription": "告警状态", - "xpack.triggersActionsUI.alertsTable.unknownErrorBody": "呈现告警表时出错", - "xpack.triggersActionsUI.alertsTable.unknownErrorCopyToClipboardLabel": "将错误复制到剪贴板", - "xpack.triggersActionsUI.alertsTable.unknownErrorTitle": "无法显示告警", - "xpack.triggersActionsUI.alertsTable.untrackByQuery.failedMessage": "无法按查询取消跟踪告警", - "xpack.triggersActionsUI.alertsTable.untrackByQuery.successMessage": "已取消跟踪告警", - "xpack.triggersActionsUI.alertsTable.viewAlertDetails": "查看告警详情", - "xpack.triggersActionsUI.alertsTable.viewRuleDetails": "查看规则详情", "xpack.triggersActionsUI.appName": "规则", - "xpack.triggersActionsUI.bulkActions.columnHeader.AriaLabel": "选择所有行", - "xpack.triggersActionsUI.bulkActions.selectRowCheckbox.AriaLabel": "选择行 {displayedRowIndex}", - "xpack.triggersActionsUI.cases.api.bulkGet": "提取案例数据时出错", "xpack.triggersActionsUI.cases.label": "案例", "xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "此规则类型需要{minimumLicenseRequired}许可证。", "xpack.triggersActionsUI.common.constants.comparators.groupByTypes.allDocumentsLabel": "所有文档", @@ -43932,8 +43891,6 @@ "xpack.triggersActionsUI.common.expressionItems.threshold.andLabel": "且", "xpack.triggersActionsUI.common.expressionItems.threshold.descriptionLabel": "当", "xpack.triggersActionsUI.common.expressionItems.threshold.popoverTitle": "当", - "xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText": "搜索告警时发生错误", - "xpack.triggersActionsUI.components.alertTable.useFetchBrowserFieldsCapabilities.errorMessageText": "加载浏览器字段时出错", "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "选择……", "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel": "基于您的数据视图", "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "未导入敏感信息。请为以下字段{encryptedFieldsLength, plural, other {}} {secretFieldsLabel}输入值{encryptedFieldsLength, plural, other {}}。", @@ -43989,27 +43946,6 @@ "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "取消", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "删除{numIdsToDelete, plural, one {{singleTitle}} other { # 个{multipleTitle}}}", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.descriptionText": "无法恢复{numIdsToDelete, plural, one {删除的{singleTitle}} other {删除的{multipleTitle}}}。", - "xpack.triggersActionsUI.empty.description": "尝试搜索更长的时间段或修改您的搜索", - "xpack.triggersActionsUI.empty.title": "没有任何结果匹配您的搜索条件", - "xpack.triggersActionsUI.fieldBrowser.categoriesCountTitle": "{totalCount} {totalCount, plural, other {个类别}}", - "xpack.triggersActionsUI.fieldBrowser.categoriesTitle": "类别", - "xpack.triggersActionsUI.fieldBrowser.categoryLabel": "类别", - "xpack.triggersActionsUI.fieldBrowser.closeButton": "关闭", - "xpack.triggersActionsUI.fieldBrowser.descriptionForScreenReaderOnly": "{field} 字段的描述:", - "xpack.triggersActionsUI.fieldBrowser.descriptionLabel": "描述", - "xpack.triggersActionsUI.fieldBrowser.fieldBrowserTitle": "字段", - "xpack.triggersActionsUI.fieldBrowser.fieldLabel": "字段", - "xpack.triggersActionsUI.fieldBrowser.fieldName": "名称", - "xpack.triggersActionsUI.fieldBrowser.fieldsCountShowing": "正在显示", - "xpack.triggersActionsUI.fieldBrowser.fieldsCountTitle": "{totalCount, plural, other {字段}}", - "xpack.triggersActionsUI.fieldBrowser.fieldsTitle": "字段", - "xpack.triggersActionsUI.fieldBrowser.filterPlaceholder": "字段名称", - "xpack.triggersActionsUI.fieldBrowser.noFieldsMatchLabel": "没有字段匹配", - "xpack.triggersActionsUI.fieldBrowser.resetFieldsLink": "重置字段", - "xpack.triggersActionsUI.fieldBrowser.viewAll": "全部", - "xpack.triggersActionsUI.fieldBrowser.viewColumnCheckboxAriaLabel": "查看 {field} 列", - "xpack.triggersActionsUI.fieldBrowser.viewLabel": "查看", - "xpack.triggersActionsUI.fieldBrowser.viewSelected": "已选定", "xpack.triggersActionsUI.globalAlerts.alertStats.disabled": "已禁用", "xpack.triggersActionsUI.globalAlerts.alertStats.errors": "错误", "xpack.triggersActionsUI.globalAlerts.alertStats.loadError": "无法加载规则统计信息", @@ -44022,15 +43958,6 @@ "xpack.triggersActionsUI.home.logsTabTitle": "日志", "xpack.triggersActionsUI.home.rulesTabTitle": "规则", "xpack.triggersActionsUI.home.sectionDescription": "使用规则来检测条件。", - "xpack.triggersActionsUI.inspect.modal.closeTitle": "关闭", - "xpack.triggersActionsUI.inspect.modal.indexPatternDescription": "连接到 Elasticsearch 索引的索引模式。可以在'Kibana'>'高级设置'中配置这些索引。", - "xpack.triggersActionsUI.inspect.modal.indexPatternLabel": "索引模式", - "xpack.triggersActionsUI.inspect.modal.queryTimeDescription": "处理查询所花费的时间。不包括发送请求或在浏览器中解析它的时间。", - "xpack.triggersActionsUI.inspect.modal.queryTimeLabel": "查询时间", - "xpack.triggersActionsUI.inspect.modal.reqTimestampDescription": "记录请求启动的时间", - "xpack.triggersActionsUI.inspect.modal.reqTimestampLabel": "请求时间戳", - "xpack.triggersActionsUI.inspect.modal.somethingWentWrongDescription": "抱歉,出现问题。", - "xpack.triggersActionsUI.inspectDescription": "检查", "xpack.triggersActionsUI.jsonFieldWrapper.defaultLabel": "JSON 编辑器", "xpack.triggersActionsUI.logs.breadcrumbTitle": "日志", "xpack.triggersActionsUI.maintenanceWindows.label": "维护窗口", @@ -44040,8 +43967,6 @@ "xpack.triggersActionsUI.managementSection.connectors.displayName": "连接器", "xpack.triggersActionsUI.managementSection.displayDescription": "使用规则来检测条件。", "xpack.triggersActionsUI.managementSection.displayName": "规则", - "xpack.triggersActionsUI.muteAlert.error": "使告警静音时出错", - "xpack.triggersActionsUI.mutedAlerts.api.get": "提取已静音告警数据时出错", "xpack.triggersActionsUI.parseInterval.errorMessage": "{value} 不是时间间隔字符串", "xpack.triggersActionsUI.ruleDetails.actions": "操作", "xpack.triggersActionsUI.ruleDetails.cannotReadActions": "需要连接器功能权限才能查看操作", @@ -44058,8 +43983,6 @@ "xpack.triggersActionsUI.rules.breadcrumbTitle": "规则", "xpack.triggersActionsUI.rules.create.breadcrumbTitle": "创建", "xpack.triggersActionsUI.rules.createRule.breadcrumbTitle": "创建规则", - "xpack.triggersActionsUI.rules.deleteConfirmationModal.errorNotification.descriptionText": "无法取消跟踪{uuidsCount, plural, other {告警}}", - "xpack.triggersActionsUI.rules.deleteConfirmationModal.successNotification.descriptionText": "已取消跟踪{uuidsCount, plural, other {告警}}", "xpack.triggersActionsUI.rules.edit.breadcrumbTitle": "编辑", "xpack.triggersActionsUI.rules.editRule.breadcrumbTitle": "编辑规则", "xpack.triggersActionsUI.ruleSnoozeScheduler.addSchedule": "添加计划", @@ -44202,23 +44125,6 @@ "xpack.triggersActionsUI.sections.alertsSummaryWidget.errorBody": "加载告警摘要时出现错误。请联系您的管理员寻求帮助。", "xpack.triggersActionsUI.sections.alertsSummaryWidget.errorTitle": "无法加载告警摘要", "xpack.triggersActionsUI.sections.alertsSummaryWidget.title": "告警活动", - "xpack.triggersActionsUI.sections.alertsTable.alertMuted": "告警已静音", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.overview": "概览", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel": "告警导航", - "xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.table": "表", - "xpack.triggersActionsUI.sections.alertsTable.apm": "APM", - "xpack.triggersActionsUI.sections.alertsTable.column.actions": "操作", - "xpack.triggersActionsUI.sections.alertsTable.infrastructure": "基础设施", - "xpack.triggersActionsUI.sections.alertsTable.logs": "日志", - "xpack.triggersActionsUI.sections.alertsTable.ml": "Machine Learning", - "xpack.triggersActionsUI.sections.alertsTable.observability": "Observability", - "xpack.triggersActionsUI.sections.alertsTable.security": "安全", - "xpack.triggersActionsUI.sections.alertsTable.slos": "SLO", - "xpack.triggersActionsUI.sections.alertsTable.stack": "堆叠", - "xpack.triggersActionsUI.sections.alertsTable.stackMonitoring": "堆栈监测", - "xpack.triggersActionsUI.sections.alertsTable.title": "告警表", - "xpack.triggersActionsUI.sections.alertsTable.uptime": "运行时间", - "xpack.triggersActionsUI.sections.alertTable.viewError": "发生错误", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.cancelButtonLabel": "取消", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.confirmConnectorCloseMessage": "您无法恢复未保存更改。", "xpack.triggersActionsUI.sections.confirmConnectorEditClose.discardButtonLabel": "放弃更改", @@ -44297,7 +44203,6 @@ "xpack.triggersActionsUI.sections.executionDurationChart.numberOfExecutionsOption": "{value} 次运行", "xpack.triggersActionsUI.sections.executionDurationChart.recentDurationsTitle": "最新运行持续时间", "xpack.triggersActionsUI.sections.executionDurationChart.selectNumberOfExecutionDurationsLabel": "选择运行次数", - "xpack.triggersActionsUI.sections.globalAlerts.quickFilters.feature": "功能", "xpack.triggersActionsUI.sections.globalAlerts.quickFilters.status": "状态", "xpack.triggersActionsUI.sections.isDeprecatedDescription": "此连接器已过时。请进行更新,或创建新连接器。", "xpack.triggersActionsUI.sections.manageLicense.manageLicenseCancelButtonText": "取消", @@ -44673,10 +44578,6 @@ "xpack.triggersActionsUI.timeUnits.hourLabel": "{timeValue, plural, other {小时}}", "xpack.triggersActionsUI.timeUnits.minuteLabel": "{timeValue, plural, other {分钟}}", "xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, other {秒}}", - "xpack.triggersActionsUI.toolbar.bulkActions.clearSelectionTitle": "清除所选内容", - "xpack.triggersActionsUI.toolbar.bulkActions.selectAllAlertsTitle": "选择全部 {totalAlertsFormatted} 个{totalAlerts, plural, other {告警}}", - "xpack.triggersActionsUI.toolbar.bulkActions.selectedAlertsTitle": "已选择 {selectedAlertsFormatted} 个{selectedAlerts, plural, other {告警}}", - "xpack.triggersActionsUI.unmuteAlert.error": "使告警取消静音时出错", "xpack.triggersActionsUI.updateApiKeyConfirmModal.cancelButton": "取消", "xpack.triggersActionsUI.updateApiKeyConfirmModal.confirmButton": "更新", "xpack.triggersActionsUI.updateApiKeyConfirmModal.description": "您将无法恢复旧的 API {idsToUpdate, plural, other {密钥}}", diff --git a/x-pack/platform/plugins/shared/cases/public/common/mock/test_providers.tsx b/x-pack/platform/plugins/shared/cases/public/common/mock/test_providers.tsx index 2e96f1e3633cb..0a0a83aace697 100644 --- a/x-pack/platform/plugins/shared/cases/public/common/mock/test_providers.tsx +++ b/x-pack/platform/plugins/shared/cases/public/common/mock/test_providers.tsx @@ -125,23 +125,6 @@ export interface AppMockRenderer { clearQueryCache: () => Promise; } -export const testQueryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - }, - }, - /** - * React query prints the errors in the console even though - * all tests are passings. We turn them off for testing. - */ - logger: { - log: console.log, - warn: console.warn, - error: () => {}, - }, -}); - export const createAppMockRenderer = ({ features, owner = mockedTestProvidersOwner, diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx index 2777ea9c3b7a8..14b1bca99efab 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.test.tsx @@ -16,22 +16,27 @@ import { CaseViewAlerts } from './case_view_alerts'; import * as api from '../../../containers/api'; import type { FeatureIdsResponse } from '../../../containers/types'; import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; jest.mock('../../../containers/api'); +// Not using `jest.mocked` here because the `AlertsTable` component is manually typed to ensure +// correct type inference, but it's actually a `memo(forwardRef())` component, which is hard to mock +jest.mock('@kbn/response-ops-alerts-table', () => ({ + AlertsTable: jest.fn().mockReturnValue(
), +})); +const mockAlertsTable = jest.mocked(AlertsTable); + const caseData: CaseUI = { ...basicCase, comments: [...basicCase.comments, alertCommentWithIndices], }; describe('CaseUI View Page activity tab', () => { - const getAlertsStateTableMock = jest.fn(); let appMockRender: AppMockRenderer; beforeEach(() => { appMockRender = createAppMockRenderer(); - appMockRender.coreStart.triggersActionsUi.getAlertsStateTable = - getAlertsStateTableMock.mockReturnValue(
); }); afterEach(() => { @@ -47,16 +52,19 @@ describe('CaseUI View Page activity tab', () => { it('should call the alerts table with correct props for security solution', async () => { appMockRender.render(); await waitFor(async () => { - expect(getAlertsStateTableMock).toHaveBeenCalledWith({ - ruleTypeIds: SECURITY_SOLUTION_RULE_TYPE_IDS, - id: 'case-details-alerts-securitySolution', - query: { - ids: { - values: ['alert-id-1'], + expect(mockAlertsTable).toHaveBeenCalledWith( + expect.objectContaining({ + ruleTypeIds: SECURITY_SOLUTION_RULE_TYPE_IDS, + id: 'case-details-alerts-securitySolution', + query: { + ids: { + values: ['alert-id-1'], + }, }, - }, - showAlertStatusWithFlapping: false, - }); + showAlertStatusWithFlapping: false, + }), + expect.anything() + ); }); }); @@ -79,17 +87,20 @@ describe('CaseUI View Page activity tab', () => { ); await waitFor(async () => { - expect(getAlertsStateTableMock).toHaveBeenCalledWith({ - ruleTypeIds: ['log-threshold'], - consumers: ['observability'], - id: 'case-details-alerts-observability', - query: { - ids: { - values: ['alert-id-1'], + expect(mockAlertsTable).toHaveBeenCalledWith( + expect.objectContaining({ + ruleTypeIds: ['log-threshold'], + consumers: ['observability'], + id: 'case-details-alerts-observability', + query: { + ids: { + values: ['alert-id-1'], + }, }, - }, - showAlertStatusWithFlapping: true, - }); + showAlertStatusWithFlapping: true, + }), + expect.anything() + ); }); }); diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx index 360a764d778c3..e2b9f3933b8b0 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx @@ -9,14 +9,16 @@ import React, { useMemo } from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiProgress } from '@elastic/eui'; import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import type { SetRequired } from 'type-fest'; import { SECURITY_SOLUTION_OWNER } from '../../../../common/constants'; import type { CaseUI } from '../../../../common'; -import { useKibana } from '../../../common/lib/kibana'; import { getManualAlertIds } from './helpers'; import { useGetFeatureIds } from '../../../containers/use_get_feature_ids'; import { CaseViewAlertsEmpty } from './case_view_alerts_empty'; import { CaseViewTabs } from '../case_view_tabs'; import { CASE_VIEW_PAGE_TABS } from '../../../../common/types'; +import { useKibana } from '../../../common/lib/kibana'; interface CaseViewAlertsProps { caseData: CaseUI; @@ -24,10 +26,9 @@ interface CaseViewAlertsProps { } export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlertsProps) => { - const { - triggersActionsUi: { getAlertsStateTable: AlertsTable }, - } = useKibana().services; - + const { services } = useKibana(); + const { data, http, notifications, fieldFormats, application, licensing, settings } = + services as SetRequired; const alertIds = getManualAlertIds(caseData.comments); const alertIdsQuery = useMemo( () => ({ @@ -74,6 +75,17 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts query={alertIdsQuery} showAlertStatusWithFlapping={caseData.owner !== SECURITY_SOLUTION_OWNER} onLoaded={onAlertsTableLoaded} + services={{ + data, + http, + notifications, + fieldFormats, + application, + settings, + // In the Cases UI the licensing service is defined + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + licensing: licensing!, + }} /> ); diff --git a/x-pack/platform/plugins/shared/cases/public/plugin.test.ts b/x-pack/platform/plugins/shared/cases/public/plugin.test.ts index 0d519f038e3b8..d4f68f0d8e1ce 100644 --- a/x-pack/platform/plugins/shared/cases/public/plugin.test.ts +++ b/x-pack/platform/plugins/shared/cases/public/plugin.test.ts @@ -21,6 +21,7 @@ import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mo import type { CasesPublicStartDependencies, CasesPublicSetupDependencies } from './types'; import { CasesUiPlugin } from './plugin'; import { ALLOWED_MIME_TYPES } from '../common/constants/mime_types'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; function getConfig(overrides = {}) { return { @@ -82,6 +83,7 @@ describe('Cases Ui Plugin', () => { remove: jest.fn(), }, triggersActionsUi: triggersActionsUiMock.createStart(), + fieldFormats: fieldFormatsMock, }; }); diff --git a/x-pack/platform/plugins/shared/cases/public/types.ts b/x-pack/platform/plugins/shared/cases/public/types.ts index b619919525765..08e5787681a9e 100644 --- a/x-pack/platform/plugins/shared/cases/public/types.ts +++ b/x-pack/platform/plugins/shared/cases/public/types.ts @@ -31,6 +31,7 @@ import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; import type { CloudStart } from '@kbn/cloud-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { UseCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; import type { UseCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; import type { UseIsAddToCaseOpen } from './components/cases_context/state/use_is_add_to_case_open'; @@ -92,6 +93,7 @@ export interface CasesPublicStartDependencies { storage: Storage; triggersActionsUi: TriggersActionsStart; uiActions: UiActionsStart; + fieldFormats: FieldFormatsStart; } /** diff --git a/x-pack/platform/plugins/shared/cases/tsconfig.json b/x-pack/platform/plugins/shared/cases/tsconfig.json index 00d250c5229c9..f64f2eaa5d815 100644 --- a/x-pack/platform/plugins/shared/cases/tsconfig.json +++ b/x-pack/platform/plugins/shared/cases/tsconfig.json @@ -76,6 +76,8 @@ "@kbn/securitysolution-rules", "@kbn/alerts-ui-shared", "@kbn/cloud-plugin", + "@kbn/response-ops-alerts-table", + "@kbn/field-formats-plugin", "@kbn/core-http-server-mocks", "@kbn/core-http-server-utils", "@kbn/code-editor-mock", diff --git a/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts b/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts deleted file mode 100644 index 42cd75f66e3c9..0000000000000 --- a/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -/// -declare const _default: Cypress.ConfigOptions; -export default _default; diff --git a/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js b/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js deleted file mode 100644 index d680a87809d2e..0000000000000 --- a/x-pack/platform/plugins/shared/fleet/cypress.config.space_awareness.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; -/* - * 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. - */ -Object.defineProperty(exports, "__esModule", { value: true }); -const cypress_config_1 = require("@kbn/cypress-config"); -// eslint-disable-next-line import/no-default-export -exports.default = (0, cypress_config_1.defineCypressConfig)({ - defaultCommandTimeout: 60000, - requestTimeout: 60000, - responseTimeout: 60000, - execTimeout: 120000, - pageLoadTimeout: 120000, - retries: { - runMode: 2, - }, - env: { - grepFilterSpecs: false, - }, - screenshotsFolder: '../../../../../target/kibana-fleet/cypress/screenshots', - trashAssetsBeforeRuns: false, - video: false, - videosFolder: '../../../../../target/kibana-fleet/cypress/videos', - viewportHeight: 900, - viewportWidth: 1440, - screenshotOnRunFailure: true, - e2e: { - baseUrl: 'http://localhost:5601', - experimentalRunAllSpecs: true, - experimentalMemoryManagement: true, - numTestsKeptInMemory: 3, - specPattern: './cypress/e2e/space_awareness/**/*.cy.ts', - supportFile: './cypress/support/e2e.ts', - setupNodeEvents(on, config) { - // eslint-disable-next-line @typescript-eslint/no-var-requires, @kbn/imports/no_boundary_crossing - return require('./cypress/plugins')(on, config); - }, - }, -}); diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx index 9449f44145709..463d45ac91326 100644 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx +++ b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/alert_actions.tsx @@ -12,26 +12,26 @@ import { EuiPopover, EuiToolTip, } from '@elastic/eui'; - import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import { AttachmentType, APP_ID as CASE_APP_ID } from '@kbn/cases-plugin/common'; import { ALERT_RULE_NAME, ALERT_RULE_UUID, ALERT_UUID } from '@kbn/rule-data-utils'; -import type { GetAlertsTableProp } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { GetAlertsTableProp } from '@kbn/response-ops-alerts-table/types'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { DefaultAlertActions } from '@kbn/response-ops-alerts-table/components/default_alert_actions'; import { PLUGIN_ID } from '../../../common/constants/app'; import { useMlKibana } from '../../application/contexts/kibana'; export const AlertActions: GetAlertsTableProp<'renderActionsCell'> = (props) => { const { alert, refresh } = props; - const { cases, triggersActionsUi } = useMlKibana().services; + const { cases } = useMlKibana().services; const casesPrivileges = cases?.helpers.canUseCases([CASE_APP_ID]); const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const ruleId = alert[ALERT_RULE_UUID]?.[0] ?? null; - const alertId = alert[ALERT_UUID]?.[0] ?? ''; + const ruleId = (alert[ALERT_RULE_UUID]?.[0] as string) ?? null; + const alertId = (alert[ALERT_UUID]?.[0] as string) ?? ''; const ecsData = useMemo( () => ({ @@ -50,7 +50,7 @@ export const AlertActions: GetAlertsTableProp<'renderActionsCell'> = (props) => type: AttachmentType.alert, rule: { id: ruleId, - name: alert[ALERT_RULE_NAME]![0], + name: alert[ALERT_RULE_NAME]![0] as string, }, owner: PLUGIN_ID, }, @@ -83,19 +83,21 @@ export const AlertActions: GetAlertsTableProp<'renderActionsCell'> = (props) => closeActionsPopover(); }; - const DefaultAlertActions = useMemo( - () => - triggersActionsUi?.getAlertsTableDefaultAlertActions({ - key: 'defaultRowActions', - onActionExecuted: closeActionsPopover, - isAlertDetailsEnabled: false, - resolveRulePagePath: (alertRuleId) => + const defaultAlertActions = useMemo( + () => ( + alertRuleId ? `/app/management/insightsAndAlerting/triggersActions/rule/${alertRuleId}` - : null, - ...props, - }), - [props, triggersActionsUi] + : null + } + {...props} + /> + ), + [props] ); const actionsMenuItems = [ @@ -125,8 +127,8 @@ export const AlertActions: GetAlertsTableProp<'renderActionsCell'> = (props) => : []), ]; - if (DefaultAlertActions) { - actionsMenuItems.push(DefaultAlertActions); + if (defaultAlertActions) { + actionsMenuItems.push(defaultAlertActions); } const actionsToolTip = diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx index 8cbcb64004660..6ed82d4fa2d3e 100644 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx +++ b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/flyout_body.tsx @@ -9,14 +9,17 @@ import { get } from 'lodash'; import React from 'react'; import { EuiDescriptionList, EuiPanel } from '@elastic/eui'; import { isDefined } from '@kbn/ml-is-defined'; -import type { GetAlertsTableProp } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { GetAlertsTableProp } from '@kbn/response-ops-alerts-table/types'; +import { useAlertsTableContext } from '@kbn/response-ops-alerts-table/contexts/alerts_table_context'; import { getAlertFormatters } from './render_cell_value'; export const AlertsTableFlyoutBody: GetAlertsTableProp<'renderFlyoutBody'> = ({ alert, columns, - fieldFormats, }) => { + const { + services: { fieldFormats }, + } = useAlertsTableContext(); const formatter = getAlertFormatters(fieldFormats); return ( diff --git a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx index 2efeac34feac1..48777bb11c415 100644 --- a/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx +++ b/x-pack/platform/plugins/shared/ml/public/alerting/anomaly_detection_alerts_table/render_cell_value.tsx @@ -13,8 +13,10 @@ import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { getFormattedSeverityScore, getSeverityColor } from '@kbn/ml-anomaly-utils'; import { EuiHealth } from '@elastic/eui'; -import type { Alert, GetAlertsTableProp } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { GetAlertsTableProp } from '@kbn/response-ops-alerts-table/types'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { Alert } from '@kbn/alerting-types'; +import { useAlertsTableContext } from '@kbn/response-ops-alerts-table/contexts/alerts_table_context'; import { alertFieldNameMap, ALERT_ANOMALY_SCORE, @@ -44,8 +46,10 @@ const getAlertFieldValue = (alert: Alert, fieldName: string) => { export const AlertsTableCellValue: GetAlertsTableProp<'renderCellValue'> = ({ columnId, alert, - fieldFormats, }) => { + const { + services: { fieldFormats }, + } = useAlertsTableContext(); const alertValueFormatter = getAlertFormatters(fieldFormats); const value = getAlertFieldValue(alert, columnId); diff --git a/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx b/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx index 4f9b51e5488e5..bfa7cb5520dee 100644 --- a/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx +++ b/x-pack/platform/plugins/shared/ml/public/application/explorer/alerts/alerts_panel.tsx @@ -25,16 +25,15 @@ import { ALERT_START, ALERT_STATUS, ALERT_STATUS_ACTIVE, - type AlertStatus, } from '@kbn/rule-data-utils'; import useObservable from 'react-use/lib/useObservable'; -import { ML_VALID_CONSUMERS } from '../../../../common/constants/alerts'; -import { ML_RULE_TYPE_IDS } from '../../../../common'; import { MANAGEMENT_APP_ID } from '@kbn/deeplinks-management/constants'; import { APP_ID as CASE_APP_ID, FEATURE_ID as CASE_GENERAL_ID } from '@kbn/cases-plugin/common'; -import type { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; -import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { SortCombinations, SortOrder } from '@elastic/elasticsearch/lib/api/types'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import { ML_RULE_TYPE_IDS } from '../../../../common'; +import { ML_VALID_CONSUMERS } from '../../../../common/constants/alerts'; import { AlertActions } from '../../../alerting/anomaly_detection_alerts_table/alert_actions'; import { AlertsTableFlyoutBody } from '../../../alerting/anomaly_detection_alerts_table/flyout_body'; import { CollapsiblePanel } from '../../components/collapsible_panel'; @@ -130,10 +129,8 @@ const sort: SortCombinations[] = [ ]; export const AlertsPanel: FC = () => { - const { - services: { triggersActionsUi }, - } = useMlKibana(); - const { getAlertsStateTable: AlertsTable } = triggersActionsUi!; + const { data, http, notifications, fieldFormats, application, licensing, settings } = + useMlKibana().services; const [isOpen, setIsOpen] = useState(true); const [toggleSelected, setToggleSelected] = useState(`alertsSummary`); @@ -223,6 +220,15 @@ export const AlertsPanel: FC = () => { syncAlerts: false, }} showAlertStatusWithFlapping + services={{ + data, + http, + notifications, + fieldFormats, + application, + licensing, + settings, + }} /> ) : ( diff --git a/x-pack/platform/plugins/shared/ml/tsconfig.json b/x-pack/platform/plugins/shared/ml/tsconfig.json index da5bcd2c0f51b..e2355f3667587 100644 --- a/x-pack/platform/plugins/shared/ml/tsconfig.json +++ b/x-pack/platform/plugins/shared/ml/tsconfig.json @@ -131,6 +131,8 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/response-ops-alerts-table", + "@kbn/alerting-types", "@kbn/core-http-server-utils", "@kbn/core-saved-objects-api-server", "@kbn/core-ui-settings-server", diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts index 0a518a750948b..60997974da2fd 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/common/utils.ts @@ -5,12 +5,4 @@ * 2.0. */ -import React, { forwardRef, ReactElement } from 'react'; - export const nonNullable = (v: T): v is NonNullable => v != null; - -export function typedForwardRef( - render: (props: P, ref: React.Ref) => ReactElement -): (props: P & React.RefAttributes) => ReactElement { - return forwardRef(render) as any; -} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts deleted file mode 100644 index 9031179f58d61..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/hooks/constants.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const triggersActionsUiQueriesKeys = { - all: ['triggersActionsUi'] as const, - alertsTable: () => [...triggersActionsUiQueriesKeys.all, 'alertsTable'] as const, - cases: () => [...triggersActionsUiQueriesKeys.alertsTable(), 'cases'] as const, - mutedAlerts: (ruleIds: string[]) => - [...triggersActionsUiQueriesKeys.alertsTable(), 'mutedAlerts', ruleIds] as const, - casesBulkGet: (caseIds: string[]) => - [...triggersActionsUiQueriesKeys.cases(), 'bulkGet', caseIds] as const, - maintenanceWindows: () => - [...triggersActionsUiQueriesKeys.alertsTable(), 'maintenanceWindows'] as const, - maintenanceWindowsBulkGet: (maintenanceWindowIds: string[]) => [ - ...triggersActionsUiQueriesKeys.maintenanceWindows(), - 'bulkGet', - maintenanceWindowIds, - ], -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.ts deleted file mode 100644 index d2932440090ae..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/mute_alert.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { HttpSetup } from '@kbn/core/public'; -import { BASE_ALERTING_API_PATH } from '../../constants'; - -export async function muteAlertInstance({ - id, - instanceId, - http, -}: { - id: string; - instanceId: string; - http: HttpSetup; -}): Promise { - await http.post( - `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent( - instanceId - )}/_mute` - ); -} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.ts deleted file mode 100644 index fa3dd97750f2a..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/lib/rule_api/unmute_alert.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { HttpSetup } from '@kbn/core/public'; -import { BASE_ALERTING_API_PATH } from '../../constants'; - -export async function unmuteAlertInstance({ - id, - instanceId, - http, -}: { - id: string; - instanceId: string; - http: HttpSetup; -}): Promise { - await http.post( - `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent( - instanceId - )}/_unmute` - ); -} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx index 721862f1ebae5..7d00b74cae11c 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.test.tsx @@ -5,9 +5,10 @@ * 2.0. */ -import * as React from 'react'; +import React from 'react'; import { screen } from '@testing-library/react'; - +import { fetchAlertsFields } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'; +import { alertsTableQueryClient } from '@kbn/response-ops-alerts-table/query_client'; import { StackAlertsPage } from './stack_alerts_page'; import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; import { createAppMockRenderer } from '../../test_utils'; @@ -19,14 +20,19 @@ const mockLoadRuleTypes = jest .mocked(loadRuleTypes) .mockResolvedValue(Array.from(ruleTypesIndex.values())); +jest.mock('@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'); +jest.mocked(fetchAlertsFields).mockResolvedValue({ browserFields: {}, fields: [] }); + jest.mock('../../alerts_search_bar/url_synced_alerts_search_bar', () => ({ UrlSyncedAlertsSearchBar: () => (
{'UrlSyncedAlertsSearchBar'}
), })); -jest.mock('../../alerts_table/alerts_data_grid', () => ({ - AlertsDataGrid: jest.fn(() =>
{'Alerts table'}
), +// Not using `jest.mocked` here because the `AlertsTable` component is manually typed to ensure +// correct type inference, but it's actually a `memo(forwardRef())` component, which is hard to mock +jest.mock('@kbn/response-ops-alerts-table/components/alerts_table', () => ({ + AlertsTable: () =>
{'Alerts table'}
, })); jest.mock('../../../../common/get_experimental_features'); @@ -37,6 +43,11 @@ describe('StackAlertsPage', () => { additionalServices: {}, }); + afterEach(() => { + appMockRender.queryClient.clear(); + alertsTableQueryClient.clear(); + }); + it('renders the stack alerts page with the correct permissions', async () => { appMockRender.render(); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx index 624f18c809d81..ff8b0ed6f9203 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx @@ -5,16 +5,9 @@ * 2.0. */ -import React, { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiBetaBadge, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, - EuiPageTemplate, - EuiSpacer, -} from '@elastic/eui'; +import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPageTemplate, EuiSpacer } from '@elastic/eui'; import { ALERT_STATUS, ALERT_STATUS_ACTIVE, @@ -26,15 +19,19 @@ import { import { QueryClientProvider } from '@tanstack/react-query'; import { BoolQuery, Filter } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import { alertProducersData } from '@kbn/response-ops-alerts-table/constants'; +import { alertsTableQueryClient } from '@kbn/response-ops-alerts-table/query_client'; +import { defaultAlertsTableSort } from '@kbn/response-ops-alerts-table/configuration'; +import { AlertsTableSupportedConsumers } from '@kbn/response-ops-alerts-table/types'; +import { AlertActionsCell } from '@kbn/response-ops-alerts-table/components/alert_actions_cell'; import { ALERTS_PAGE_ID } from '../../../../common/constants'; import { QuickFiltersMenuItem } from '../../alerts_search_bar/quick_filters'; import { NoPermissionPrompt } from '../../../components/prompts/no_permission_prompt'; import { useRuleStats } from '../hooks/use_rule_stats'; import { getAlertingSectionBreadcrumb } from '../../../lib/breadcrumb'; -import { alertProducersData } from '../../alerts_table/constants'; import { UrlSyncedAlertsSearchBar } from '../../alerts_search_bar/url_synced_alerts_search_bar'; import { useKibana } from '../../../../common/lib/kibana'; -import { alertsTableQueryClient } from '../../alerts_table/query_client'; import { alertSearchBarStateContainer, Provider, @@ -52,15 +49,6 @@ import { useRuleTypeIdsByFeatureId, } from '../hooks/use_rule_type_ids_by_feature_id'; import { TECH_PREVIEW_DESCRIPTION, TECH_PREVIEW_LABEL } from '../../translations'; -import { - defaultAlertsTableColumns, - defaultAlertsTableSort, -} from '../../alerts_table/configuration'; -import { DefaultAlertsFlyoutBody } from '../../alerts_table/alerts_flyout/default_alerts_flyout'; -import { AlertActionsCell } from '../../alerts_table/row_actions/alert_actions_cell'; -import { AlertsTable } from '../../alerts_table/alerts_table'; -import { renderCellValue } from '../../alerts_table/cells/render_cell_value'; -import { AlertsTableSupportedConsumers } from '../../alerts_table/types'; import { NON_SIEM_CONSUMERS } from '../../alerts_search_bar/constants'; /** @@ -124,6 +112,8 @@ const PageContentComponent: React.FC = ({ authorizedToReadAnyRules, ruleTypeIdsByFeatureId, }) => { + const { data, http, notifications, fieldFormats, application, licensing, settings } = + useKibana().services; const ruleTypeIdsByFeatureIdEntries = Object.entries(ruleTypeIdsByFeatureId); const [esQuery, setEsQuery] = useState({ bool: {} } as { bool: BoolQuery }); @@ -260,24 +250,29 @@ const PageContentComponent: React.FC = ({ onEsQueryChange={setEsQuery} onFilterSelected={onFilterSelected} /> - }> - - + )} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/hooks/use_rule_type_ids_by_feature_id.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/hooks/use_rule_type_ids_by_feature_id.ts index 1380f9655d479..0f2db3b8fec42 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/hooks/use_rule_type_ids_by_feature_id.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/hooks/use_rule_type_ids_by_feature_id.ts @@ -8,7 +8,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import { useMemo } from 'react'; import { mapValues } from 'lodash'; -import { observabilityFeatureIds, stackFeatureIds } from '../../alerts_table/constants'; +import { observabilityFeatureIds, stackFeatureIds } from '@kbn/response-ops-alerts-table/constants'; import { MULTI_CONSUMER_RULE_TYPE_IDS } from '../../../constants'; import { RuleTypeIndex } from '../../../../types'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/index.ts deleted file mode 100644 index f1f90cc602c58..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { AlertsFlyout } from './alerts_flyout'; -// eslint-disable-next-line import/no-default-export -export default AlertsFlyout; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/index.ts deleted file mode 100644 index f3dd59a90f468..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/components/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { BulkActionsHeader } from './column_header'; -export { BulkActionsCell } from './row_cell'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/translations.ts deleted file mode 100644 index fc37af46f33f8..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/translations.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const SELECTED_ALERTS = (selectedAlertsFormatted: string, selectedAlerts: number) => - i18n.translate('xpack.triggersActionsUI.toolbar.bulkActions.selectedAlertsTitle', { - values: { selectedAlertsFormatted, selectedAlerts }, - defaultMessage: - 'Selected {selectedAlertsFormatted} {selectedAlerts, plural, =1 {alert} other {alerts}}', - }); - -export const SELECT_ALL_ALERTS = (totalAlertsFormatted: string, totalAlerts: number) => - i18n.translate('xpack.triggersActionsUI.toolbar.bulkActions.selectAllAlertsTitle', { - values: { totalAlertsFormatted, totalAlerts }, - defaultMessage: - 'Select all {totalAlertsFormatted} {totalAlerts, plural, =1 {alert} other {alerts}}', - }); - -export const CLEAR_SELECTION = i18n.translate( - 'xpack.triggersActionsUI.toolbar.bulkActions.clearSelectionTitle', - { - defaultMessage: 'Clear selection', - } -); - -export const COLUMN_HEADER_ARIA_LABEL = i18n.translate( - 'xpack.triggersActionsUI.bulkActions.columnHeader.AriaLabel', - { - defaultMessage: 'Select all rows', - } -); - -export const SELECT_ROW_ARIA_LABEL = (displayedRowIndex: number) => - i18n.translate('xpack.triggersActionsUI.bulkActions.selectRowCheckbox.AriaLabel', { - values: { displayedRowIndex }, - defaultMessage: 'Select row {displayedRowIndex}', - }); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.test.tsx deleted file mode 100644 index 2492c02777197..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/cell.test.tsx +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { CasesCell } from './cell'; -import { CellComponentProps } from '../types'; -import { Alert } from '../../../../types'; -import { getCasesMapMock } from './index.mock'; -import { getMaintenanceWindowsMapMock } from '../maintenance_windows/index.mock'; -import userEvent from '@testing-library/user-event'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { useCaseViewNavigation } from './use_case_view_navigation'; -import { createStartServicesMock } from '../../../../common/lib/kibana/kibana_react.mock'; - -const mockUseKibanaReturnValue = createStartServicesMock(); -jest.mock('../../../../common/lib/kibana', () => ({ - __esModule: true, - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); -jest.mock('./use_case_view_navigation'); - -const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; - -describe('CasesCell', () => { - const casesMap = getCasesMapMock(); - const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); - const alert = { - _id: 'alert-id', - _index: 'alert-index', - 'kibana.alert.case_ids': ['test-id'], - } as Alert; - - const props = { - isLoading: false, - alert, - cases: casesMap, - maintenanceWindows: maintenanceWindowsMap, - columnId: 'kibana.alert.case_ids', - showAlertStatusWithFlapping: false, - // Assertion used to avoid defining all the (numerous) context properties - } as CellComponentProps; - - let appMockRender: AppMockRenderer; - - const navigateToCaseView = jest.fn(); - useCaseViewNavigationMock.mockReturnValue({ navigateToCaseView }); - - beforeEach(() => { - appMockRender = createAppMockRenderer(); - }); - - it('renders the cases cell', async () => { - appMockRender.render(); - expect(screen.getByText('Test case')).toBeInTheDocument(); - }); - - it('renders the loading skeleton', async () => { - appMockRender.render(); - expect(screen.getByTestId('cases-cell-loading')).toBeInTheDocument(); - }); - - it('renders multiple cases correctly', async () => { - appMockRender.render( - - ); - - expect(screen.getByText('Test case')).toBeInTheDocument(); - expect(screen.getByText('Test case 2')).toBeInTheDocument(); - }); - - it('does not render a case that it is in the map but not in the alerts data', async () => { - appMockRender.render(); - - expect(screen.getByText('Test case')).toBeInTheDocument(); - expect(screen.queryByText('Test case 2')).not.toBeInTheDocument(); - }); - - it('does not show any cases when the alert does not have any case ids', async () => { - appMockRender.render( - - ); - - expect(screen.queryByText('Test case')).not.toBeInTheDocument(); - expect(screen.queryByText('Test case 2')).not.toBeInTheDocument(); - }); - - it('does show the default value when the alert does not have any case ids', async () => { - appMockRender.render( - - ); - - expect(screen.getByText('--')).toBeInTheDocument(); - }); - - it('does not show any cases when the alert has invalid case ids', async () => { - appMockRender.render( - - ); - - expect(screen.queryByTestId('cases-cell-link')).not.toBeInTheDocument(); - }); - - it('does show the default value when the alert has invalid case ids', async () => { - appMockRender.render( - - ); - - expect(screen.getByText('--')).toBeInTheDocument(); - }); - - it('shows the cases tooltip', async () => { - appMockRender.render(); - expect(screen.getByText('Test case')).toBeInTheDocument(); - - await userEvent.hover(screen.getByText('Test case')); - - expect(await screen.findByTestId('cases-components-tooltip')).toBeInTheDocument(); - }); - - it('navigates to the case correctly', async () => { - appMockRender.render(); - expect(screen.getByText('Test case')).toBeInTheDocument(); - - await userEvent.click(screen.getByText('Test case')); - expect(navigateToCaseView).toBeCalledWith({ caseId: 'test-id' }); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/index.mock.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/index.mock.ts deleted file mode 100644 index d3cdd892629bd..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/index.mock.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CaseStatuses } from '@kbn/cases-components'; -import { Case } from '../hooks/apis/bulk_get_cases'; - -export const theCase: Case = { - id: 'test-id', - created_at: '2023-02-16T18:13:37.058Z', - created_by: { full_name: 'Elastic', username: 'elastic', email: 'elastic@elastic.co' }, - description: 'Test description', - status: CaseStatuses.open, - title: 'Test case', - totalComment: 1, - version: 'WzQ3LDFd', - owner: 'cases', -}; - -export const getCasesMock = () => { - return [theCase, { ...theCase, id: 'test-id-2', title: 'Test case 2' }]; -}; - -export const getCasesMapMock = () => - getCasesMock().reduce((acc, val) => acc.set(val.id, val), new Map()); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.test.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.test.ts deleted file mode 100644 index 6b1a14c0f7d2d..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cases/use_case_view_navigation.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { BehaviorSubject } from 'rxjs'; -import { act, waitFor, renderHook } from '@testing-library/react'; -import { useKibana } from '../../../../common/lib/kibana'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { useCaseViewNavigation } from './use_case_view_navigation'; - -jest.mock('../../../../common/lib/kibana'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useCaseViewNavigation', () => { - let appMockRender: AppMockRenderer; - const navigateToApp = jest.fn(); - - beforeEach(() => { - appMockRender = createAppMockRenderer(); - useKibanaMock().services.application.currentAppId$ = new BehaviorSubject('testAppId'); - useKibanaMock().services.application.navigateToApp = navigateToApp; - }); - - it('calls navigateToApp with correct arguments', async () => { - const { result } = renderHook(() => useCaseViewNavigation(), { - wrapper: appMockRender.AppWrapper, - }); - - act(() => { - result.current.navigateToCaseView({ caseId: 'test-id' }); - }); - - await waitFor(() => { - expect(navigateToApp).toHaveBeenCalledWith('testAppId', { - deepLinkId: 'cases', - path: '/test-id', - }); - }); - }); - - it('calls navigateToApp with correct arguments and bypass current app id', async () => { - const { result } = renderHook(() => useCaseViewNavigation('superAppId'), { - wrapper: appMockRender.AppWrapper, - }); - - act(() => { - result.current.navigateToCaseView({ caseId: 'test-id' }); - }); - - await waitFor(() => { - expect(navigateToApp).toHaveBeenCalledWith('superAppId', { - deepLinkId: 'cases', - path: '/test-id', - }); - }); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.test.tsx deleted file mode 100644 index 1a3baeec26109..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/default_cell.test.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { DefaultCell } from './default_cell'; -import { CellComponentProps } from '../types'; -import { Alert } from '../../../../types'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { getCasesMapMock } from '../cases/index.mock'; -import { getMaintenanceWindowsMapMock } from '../maintenance_windows/index.mock'; - -jest.mock('../../../../common/lib/kibana'); - -describe('DefaultCell', () => { - const casesMap = getCasesMapMock(); - const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); - const alert = { - _id: 'alert-id', - _index: 'alert-index', - 'kibana.alert.status': ['active'], - } as Alert; - - const props = { - isLoading: false, - alert, - cases: casesMap, - maintenanceWindows: maintenanceWindowsMap, - columnId: 'kibana.alert.status', - showAlertStatusWithFlapping: false, - } as CellComponentProps; - - let appMockRender: AppMockRenderer; - - beforeEach(() => { - appMockRender = createAppMockRenderer(); - }); - - it('shows the value', async () => { - appMockRender.render(); - expect(screen.getByText('active')).toBeInTheDocument(); - }); - - it('shows empty tag if the value is empty', async () => { - appMockRender.render( - - ); - expect(screen.getByText('--')).toBeInTheDocument(); - }); - - it('shows multiple values', async () => { - appMockRender.render( - - ); - expect(screen.getByText('active, recovered')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.test.tsx deleted file mode 100644 index 2de3b01bc4a5a..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/index.test.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { SystemCellFactory } from '.'; -import { CellComponentProps } from '../types'; -import { Alert } from '../../../../types'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { getCasesMapMock } from '../cases/index.mock'; -import { getMaintenanceWindowsMapMock } from '../maintenance_windows/index.mock'; - -jest.mock('../../../../common/lib/kibana'); - -describe('SystemCellFactory', () => { - const casesMap = getCasesMapMock(); - const maintenanceWindowsMap = getMaintenanceWindowsMapMock(); - - const alert = { - _id: 'alert-id', - _index: 'alert-index', - 'kibana.alert.status': ['active'], - 'kibana.alert.case_ids': ['test-id'], - 'kibana.alert.maintenance_window_ids': ['test-mw-id-1'], - } as Alert; - - const props = { - isLoading: false, - alert, - cases: casesMap, - maintenanceWindows: maintenanceWindowsMap, - columnId: 'kibana.alert.status', - showAlertStatusWithFlapping: true, - } as CellComponentProps; - - let appMockRender: AppMockRenderer; - - beforeEach(() => { - appMockRender = createAppMockRenderer(); - }); - - it('shows the status cell', async () => { - appMockRender.render(); - expect(screen.getByText('Active')).toBeInTheDocument(); - }); - - it('shows the cases cell', async () => { - appMockRender.render(); - expect(screen.getByText('Test case')).toBeInTheDocument(); - }); - - it('shows the maintenance windows cell', async () => { - appMockRender.render( - - ); - expect(screen.getByText('test-title')).toBeInTheDocument(); - }); - - it('shows the cell if the columnId is not registered to the map', async () => { - appMockRender.render(); - expect(screen.getByText('--')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/render_cell_value.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/render_cell_value.tsx deleted file mode 100644 index b7aea4aac2d69..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/cells/render_cell_value.tsx +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { isEmpty } from 'lodash'; -import React from 'react'; -import { - ALERT_DURATION, - ALERT_RULE_NAME, - ALERT_RULE_UUID, - ALERT_START, - TIMESTAMP, - ALERT_RULE_CONSUMER, - ALERT_RULE_PRODUCER, -} from '@kbn/rule-data-utils'; -import { FIELD_FORMAT_IDS, FieldFormatParams } from '@kbn/field-formats-plugin/common'; -import { EuiBadge, EuiLink } from '@elastic/eui'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { AlertsTableProps } from '../../../../types'; -import { alertProducersData, observabilityFeatureIds } from '../constants'; -import { useKibana } from '../../../../common/lib/kibana'; -import { AlertsTableSupportedConsumers } from '../types'; - -export const getMappedNonEcsValue = ({ - data, - fieldName, -}: { - data: any[]; - fieldName: string; -}): string[] | undefined => { - const item = data.find((d) => d.field === fieldName); - if (item != null && item.value != null) { - return item.value; - } - return undefined; -}; - -const getRenderValue = (mappedNonEcsValue: any) => { - const value = Array.isArray(mappedNonEcsValue) ? mappedNonEcsValue.join() : mappedNonEcsValue; - - if (!isEmpty(value)) { - if (typeof value === 'object') { - return JSON.stringify(value); - } - return value; - } - - return '—'; -}; - -export const renderCellValue: AlertsTableProps['renderCellValue'] = (props) => { - const { columnId, data, fieldFormats } = props; - const alertValueFormatter = getAlertFormatters(fieldFormats); - const rawValue = props.alert[columnId]?.[0]; - const value = getRenderValue(rawValue); - return alertValueFormatter(columnId, value, data); -}; - -const defaultParam: Record = { - [FIELD_FORMAT_IDS.DURATION]: { - inputFormat: 'milliseconds', - outputFormat: 'humanizePrecise', - }, - [FIELD_FORMAT_IDS.NUMBER]: { - pattern: '00.00', - }, -}; - -export const getFieldFormatterProvider = - (fieldFormats: FieldFormatsStart) => - (fieldType: FIELD_FORMAT_IDS, params?: FieldFormatParams) => { - const fieldFormatter = fieldFormats.deserialize({ - id: fieldType, - params: params ?? defaultParam[fieldType], - }); - return fieldFormatter.convert.bind(fieldFormatter); - }; - -export function useFieldFormatter(fieldType: FIELD_FORMAT_IDS) { - const { fieldFormats } = useKibana().services; - return getFieldFormatterProvider(fieldFormats)(fieldType); -} - -const AlertRuleLink = ({ alertFields }: { alertFields: Array<{ field: string; value: any }> }) => { - const { http } = useKibana().services; - const ruleName = alertFields.find((f) => f.field === ALERT_RULE_NAME)?.value?.[0]; - const ruleUuid = alertFields.find((f) => f.field === ALERT_RULE_UUID)?.value?.[0]; - - if (!ruleName || !ruleUuid) { - return null; - } - - return ( - - {ruleName} - - ); -}; - -export function getAlertFormatters(fieldFormats: FieldFormatsStart) { - const getFormatter = getFieldFormatterProvider(fieldFormats); - - return ( - columnId: string, - value: any, - rowData?: Array<{ field: string; value: any }> - ): React.ReactElement => { - switch (columnId) { - case TIMESTAMP: - case ALERT_START: - return <>{getFormatter(FIELD_FORMAT_IDS.DATE)(value)}; - case ALERT_RULE_NAME: - return rowData ? : <>{value}; - case ALERT_DURATION: - return ( - <> - {getFormatter(FIELD_FORMAT_IDS.DURATION, { - inputFormat: 'microseconds', - outputFormat: 'humanizePrecise', - })(value) || '--'} - - ); - case ALERT_RULE_CONSUMER: - const producer = rowData?.find(({ field }) => field === ALERT_RULE_PRODUCER)?.value?.[0]; - const consumer: AlertsTableSupportedConsumers = observabilityFeatureIds.includes(producer) - ? 'observability' - : producer && (value === 'alerts' || value === 'stackAlerts' || value === 'discover') - ? producer - : value; - const consumerData = alertProducersData[consumer]; - if (!consumerData) { - return <>{value}; - } - return {consumerData.displayName}; - default: - return <>{value}; - } - }; -} - -export type RegisterFormatter = ReturnType; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.stories.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.stories.tsx deleted file mode 100644 index 87227cccc242d..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/empty_state.stories.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EmptyState as Component } from './empty_state'; - -export default { - component: Component, - title: 'app/AlertTable', - argTypes: { - height: { type: 'select', options: ['short', 'tall'] }, - }, -}; - -export const EmptyState = { - args: { - height: 'tall', - }, -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_alert_muted_state.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_alert_muted_state.ts deleted file mode 100644 index 4b15772c218b7..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_alert_muted_state.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import { ALERT_INSTANCE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import { Alert } from '../../../../../types'; -import { useAlertsTableContext } from '../../contexts/alerts_table_context'; - -export const useAlertMutedState = (alert?: Alert) => { - const { mutedAlerts } = useAlertsTableContext(); - const alertInstanceId = alert && alert[ALERT_INSTANCE_ID]?.[0]; - const ruleId = alert && alert[ALERT_RULE_UUID]?.[0]; - return useMemo(() => { - const rule = ruleId ? mutedAlerts?.[ruleId] ?? [] : []; - return { - isMuted: alertInstanceId ? rule?.includes(alertInstanceId) : null, - ruleId, - rule, - alertInstanceId, - }; - }, [alertInstanceId, mutedAlerts, ruleId]); -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.test.tsx deleted file mode 100644 index 945c30a27b473..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_get_muted_alerts.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { waitFor, renderHook } from '@testing-library/react'; -import * as api from '../apis/get_rules_with_muted_alerts'; -import { useKibana } from '../../../../../common/lib/kibana'; -import { AppMockRenderer, createAppMockRenderer } from '../../../test_utils'; -import { useGetMutedAlertsQuery } from './use_get_muted_alerts'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; - -jest.mock('../apis/get_rules_with_muted_alerts'); -jest.mock('../../../../../common/lib/kibana'); - -const ruleIds = ['a', 'b']; - -describe('useGetMutedAlerts', () => { - const addErrorMock = jest.mocked(useKibana().services.notifications.toasts.addError); - - const appMockRender: AppMockRenderer = createAppMockRenderer({ - queryClientContext: AlertsQueryContext, - }); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('calls the api when invoked with the correct parameters', async () => { - const muteAlertInstanceSpy = jest.spyOn(api, 'getRulesWithMutedAlerts'); - - renderHook(() => useGetMutedAlertsQuery({ ruleIds }), { - wrapper: appMockRender.AppWrapper, - }); - - await waitFor(() => - expect(muteAlertInstanceSpy).toHaveBeenCalledWith(expect.objectContaining({ ruleIds })) - ); - }); - - it('does not call the api if the enabled option is false', async () => { - const spy = jest.spyOn(api, 'getRulesWithMutedAlerts'); - - renderHook(() => useGetMutedAlertsQuery({ ruleIds }, { enabled: false }), { - wrapper: appMockRender.AppWrapper, - }); - - await waitFor(() => expect(spy).not.toHaveBeenCalled()); - }); - - it('shows a toast error when the api returns an error', async () => { - const spy = jest.spyOn(api, 'getRulesWithMutedAlerts').mockRejectedValue(new Error('An error')); - - renderHook(() => useGetMutedAlertsQuery({ ruleIds }), { - wrapper: appMockRender.AppWrapper, - }); - - await waitFor(() => expect(spy).toHaveBeenCalled()); - expect(addErrorMock).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx deleted file mode 100644 index f315c83850583..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as api from '../../../../lib/rule_api/mute_alert'; -import { waitFor, renderHook } from '@testing-library/react'; -import { useKibana } from '../../../../../common/lib/kibana'; -import { AppMockRenderer, createAppMockRenderer } from '../../../test_utils'; -import { useMuteAlert } from './use_mute_alert'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; - -jest.mock('../../../../lib/rule_api/mute_alert'); -jest.mock('../../../../../common/lib/kibana'); - -const params = { ruleId: '', alertInstanceId: '' }; - -describe('useMuteAlert', () => { - const addErrorMock = useKibana().services.notifications.toasts.addError as jest.Mock; - - let appMockRender: AppMockRenderer; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRender = createAppMockRenderer({ queryClientContext: AlertsQueryContext }); - }); - - it('calls the api when invoked with the correct parameters', async () => { - const muteAlertInstanceSpy = jest.spyOn(api, 'muteAlertInstance'); - - const { result } = renderHook(() => useMuteAlert(), { - wrapper: appMockRender.AppWrapper, - }); - - result.current.mutate(params); - - await waitFor(() => { - expect(muteAlertInstanceSpy).toHaveBeenCalledWith({ - id: params.ruleId, - instanceId: params.alertInstanceId, - http: expect.anything(), - }); - }); - }); - - it('shows a toast error when the api returns an error', async () => { - const spy = jest.spyOn(api, 'muteAlertInstance').mockRejectedValue(new Error('An error')); - - const { result } = renderHook(() => useMuteAlert(), { - wrapper: appMockRender.AppWrapper, - }); - - result.current.mutate(params); - - await waitFor(() => { - expect(spy).toHaveBeenCalled(); - expect(addErrorMock).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.ts deleted file mode 100644 index 2603426e492d6..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_mute_alert.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMutation } from '@tanstack/react-query'; -import { i18n } from '@kbn/i18n'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import { muteAlertInstance } from '../../../../lib/rule_api/mute_alert'; -import { useKibana } from '../../../../..'; -import { ServerError, ToggleAlertParams } from '../../types'; - -const ERROR_TITLE = i18n.translate('xpack.triggersActionsUI.muteAlert.error', { - defaultMessage: 'Error muting alert', -}); - -export const useMuteAlert = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; - return useMutation( - ({ ruleId, alertInstanceId }: ToggleAlertParams) => - muteAlertInstance({ http, id: ruleId, instanceId: alertInstanceId }), - { - context: AlertsQueryContext, - onSuccess() { - toasts.addSuccess( - i18n.translate('xpack.triggersActionsUI.alertsTable.alertMuted', { - defaultMessage: 'Alert muted', - }) - ); - }, - onError: (error: ServerError) => { - if (error.name !== 'AbortError') { - toasts.addError( - error.body && error.body.message ? new Error(error.body.message) : error, - { - title: ERROR_TITLE, - } - ); - } - }, - } - ); -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx deleted file mode 100644 index f1bf72b604b5c..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as api from '../../../../lib/rule_api/unmute_alert'; -import { waitFor, renderHook } from '@testing-library/react'; -import { useKibana } from '../../../../../common/lib/kibana'; -import { AppMockRenderer, createAppMockRenderer } from '../../../test_utils'; -import { useUnmuteAlert } from './use_unmute_alert'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; - -jest.mock('../../../../lib/rule_api/mute_alert'); -jest.mock('../../../../../common/lib/kibana'); - -const params = { ruleId: '', alertInstanceId: '' }; - -describe('useUnmuteAlert', () => { - const addErrorMock = useKibana().services.notifications.toasts.addError as jest.Mock; - - let appMockRender: AppMockRenderer; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRender = createAppMockRenderer({ queryClientContext: AlertsQueryContext }); - }); - - it('calls the api when invoked with the correct parameters', async () => { - const muteAlertInstanceSpy = jest.spyOn(api, 'unmuteAlertInstance'); - - const { result } = renderHook(() => useUnmuteAlert(), { - wrapper: appMockRender.AppWrapper, - }); - - result.current.mutate(params); - - await waitFor(() => { - expect(muteAlertInstanceSpy).toHaveBeenCalledWith({ - id: params.ruleId, - instanceId: params.alertInstanceId, - http: expect.anything(), - }); - }); - }); - - it('shows a toast error when the api returns an error', async () => { - const spy = jest.spyOn(api, 'unmuteAlertInstance').mockRejectedValue(new Error('An error')); - - const { result } = renderHook(() => useUnmuteAlert(), { - wrapper: appMockRender.AppWrapper, - }); - - result.current.mutate(params); - - await waitFor(() => { - expect(spy).toHaveBeenCalled(); - expect(addErrorMock).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.ts deleted file mode 100644 index f74fdfdc44bf1..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/alert_mute/use_unmute_alert.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMutation } from '@tanstack/react-query'; -import { i18n } from '@kbn/i18n'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import { ServerError, ToggleAlertParams } from '../../types'; -import { unmuteAlertInstance } from '../../../../lib/rule_api/unmute_alert'; -import { useKibana } from '../../../../..'; - -const ERROR_TITLE = i18n.translate('xpack.triggersActionsUI.unmuteAlert.error', { - defaultMessage: 'Error unmuting alert', -}); - -export const useUnmuteAlert = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; - return useMutation( - ({ ruleId, alertInstanceId }: ToggleAlertParams) => - unmuteAlertInstance({ http, id: ruleId, instanceId: alertInstanceId }), - { - context: AlertsQueryContext, - onSuccess() { - toasts.addSuccess( - i18n.translate('xpack.triggersActionsUI.alertsTable.alertUnmuted', { - defaultMessage: 'Alert unmuted', - }) - ); - }, - onError: (error: ServerError) => { - if (error.name !== 'AbortError') { - toasts.addError( - error.body && error.body.message ? new Error(error.body.message) : error, - { - title: ERROR_TITLE, - } - ); - } - }, - } - ); -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/constants.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/constants.ts deleted file mode 100644 index aee55831e833a..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SortCombinations } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - -export const DefaultSort: SortCombinations[] = [ - { - '@timestamp': { - order: 'desc', - }, - }, -]; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts deleted file mode 100644 index 6c27075b53d02..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -export type { UsePagination } from './use_pagination'; -export { usePagination } from './use_pagination'; -export type { UseSorting } from './use_sorting'; -export { useSorting } from './use_sorting'; -export { DefaultSort } from './constants'; -export { useBulkActions } from './use_bulk_actions'; -export { useActionsColumn } from './use_actions_column'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/translations.ts deleted file mode 100644 index dc88b7968a3b3..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/translations.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const ERROR_FETCH_ALERTS = i18n.translate( - 'xpack.triggersActionsUI.components.alertTable.useFetchAlerts.errorMessageText', - { - defaultMessage: `An error has occurred on alerts search`, - } -); - -export const ERROR_FETCH_BROWSER_FIELDS = i18n.translate( - 'xpack.triggersActionsUI.components.alertTable.useFetchBrowserFieldsCapabilities.errorMessageText', - { - defaultMessage: 'An error has occurred loading browser fields', - } -); - -export const ADD_TO_EXISTING_CASE = i18n.translate( - 'xpack.triggersActionsUI.alerts.table.actions.addToCase', - { - defaultMessage: 'Add to existing case', - } -); - -export const ADD_TO_NEW_CASE = i18n.translate( - 'xpack.triggersActionsUI.alerts.table.actions.addToNewCase', - { - defaultMessage: 'Add to new case', - } -); - -export const NO_ALERTS_ADDED_TO_CASE = i18n.translate( - 'xpack.triggersActionsUI.alerts.table.actions.noAlertsAddedToCaseTitle', - { - defaultMessage: 'No alerts added to the case', - } -); - -export const ALERTS_ALREADY_ATTACHED_TO_CASE = i18n.translate( - 'xpack.triggersActionsUI.alerts.table.actions.alertsAlreadyAttachedToCase', - { - defaultMessage: 'All selected alerts are already attached to the case', - } -); - -export const MARK_AS_UNTRACKED = i18n.translate( - 'xpack.triggersActionsUI.alerts.table.actions.markAsUntracked', - { - defaultMessage: 'Mark as untracked', - } -); - -export const MUTE = i18n.translate('xpack.triggersActionsUI.alerts.table.actions.mute', { - defaultMessage: 'Mute', -}); - -export const UNMUTE = i18n.translate('xpack.triggersActionsUI.alerts.table.actions.unmute', { - defaultMessage: 'Unmute', -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_actions_column.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_actions_column.ts deleted file mode 100644 index ac70aa2170d26..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_actions_column.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useCallback, useMemo } from 'react'; -import { useAlertsTableContext } from '../contexts/alerts_table_context'; -import { UseActionsColumnRegistry, BulkActionsVerbs } from '../../../../types'; - -const DEFAULT_ACTIONS_COLUMNS_WIDTH = 75; - -interface UseActionsColumnProps { - options?: UseActionsColumnRegistry; -} - -export const useActionsColumn = ({ options }: UseActionsColumnProps) => { - const { - bulkActionsStore: [, updateBulkActionsState], - } = useAlertsTableContext(); - - const defaultActionsColum = useCallback( - () => ({ - renderCustomActionsRow: undefined, - width: undefined, - }), - [] - ); - - const useUserActionsColumn = options ? options : defaultActionsColum; - - const { renderCustomActionsRow, width: actionsColumnWidth = DEFAULT_ACTIONS_COLUMNS_WIDTH } = - useUserActionsColumn(); - - // we save the rowIndex when creating the function to be used by the clients - // so they don't have to manage it - const getSetIsActionLoadingCallback = useCallback( - (rowIndex: number) => - (isLoading: boolean = true) => { - updateBulkActionsState({ - action: BulkActionsVerbs.updateRowLoadingState, - rowIndex, - isLoading, - }); - }, - [updateBulkActionsState] - ); - - return useMemo(() => { - return { - renderCustomActionsRow, - actionsColumnWidth, - getSetIsActionLoadingCallback, - }; - }, [renderCustomActionsRow, actionsColumnWidth, getSetIsActionLoadingCallback]); -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.test.tsx deleted file mode 100644 index b437dcf0d02e8..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_cases.test.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as api from './apis/bulk_get_cases'; -import { waitFor, renderHook } from '@testing-library/react'; -import { useKibana } from '../../../../common/lib/kibana'; -import { useBulkGetCasesQuery } from './use_bulk_get_cases'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; - -jest.mock('./apis/bulk_get_cases'); -jest.mock('../../../../common/lib/kibana'); - -const response = { - cases: [], - errors: [], -}; - -describe('useBulkGetCases', () => { - const addErrorMock = useKibana().services.notifications.toasts.addError as jest.Mock; - - let appMockRender: AppMockRenderer; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRender = createAppMockRenderer({ queryClientContext: AlertsQueryContext }); - }); - - it('calls the api when invoked with the correct parameters', async () => { - const spy = jest.spyOn(api, 'bulkGetCases'); - spy.mockResolvedValue(response); - - renderHook(() => useBulkGetCasesQuery({ caseIds: ['case-1'] }), { - wrapper: appMockRender.AppWrapper, - }); - - await waitFor(() => - expect(spy).toHaveBeenCalledWith( - expect.anything(), - { - ids: ['case-1'], - }, - expect.any(AbortSignal) - ) - ); - }); - - it('does not call the api if the fetchCases is false', async () => { - const spy = jest.spyOn(api, 'bulkGetCases'); - spy.mockResolvedValue(response); - - renderHook(() => useBulkGetCasesQuery({ caseIds: ['case-1'] }, { enabled: false }), { - wrapper: appMockRender.AppWrapper, - }); - - await waitFor(() => expect(spy).not.toHaveBeenCalled()); - }); - - it('shows a toast error when the api return an error', async () => { - const spy = jest.spyOn(api, 'bulkGetCases').mockRejectedValue(new Error('An error')); - - renderHook(() => useBulkGetCasesQuery({ caseIds: ['case-1'] }), { - wrapper: appMockRender.AppWrapper, - }); - - await waitFor(() => { - expect(spy).toHaveBeenCalledWith( - expect.anything(), - { - ids: ['case-1'], - }, - expect.any(AbortSignal) - ); - expect(addErrorMock).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.test.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.test.ts deleted file mode 100644 index 5339de9f410c3..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_untrack_alerts_by_query.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { renderHook, act, waitFor } from '@testing-library/react'; -import { AppMockRenderer, createAppMockRenderer } from '../../test_utils'; -import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import { useBulkUntrackAlertsByQuery } from './use_bulk_untrack_alerts_by_query'; -import { createStartServicesMock } from '../../../../common/lib/kibana/kibana_react.mock'; -import { TriggersAndActionsUiServices } from '../../../..'; - -const mockUseKibanaReturnValue: TriggersAndActionsUiServices = createStartServicesMock(); - -jest.mock('../../../../common/lib/kibana', () => ({ - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); - -const response = {}; - -describe('useBulkUntrackAlertsByQuery', () => { - const httpMock = mockUseKibanaReturnValue.http.post as jest.Mock; - - let appMockRender: AppMockRenderer; - - beforeEach(() => { - jest.clearAllMocks(); - appMockRender = createAppMockRenderer({ queryClientContext: AlertsQueryContext }); - }); - - it('calls the api when invoked with the correct parameters', async () => { - httpMock.mockResolvedValue(response); - - const { result } = renderHook(() => useBulkUntrackAlertsByQuery(), { - wrapper: appMockRender.AppWrapper, - }); - - await act(async () => { - // @ts-expect-error: no need to specify a query - await result.current.mutateAsync({ ruleTypeIds: ['foo'], query: [] }); - }); - - await waitFor(() => { - expect(httpMock).toHaveBeenCalledWith('/internal/alerting/alerts/_bulk_untrack_by_query', { - body: '{"query":[],"rule_type_ids":["foo"]}', - }); - }); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/index.ts deleted file mode 100644 index a8fb587516fe2..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './use_columns'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.mock.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.mock.ts deleted file mode 100644 index a073124f5442c..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.mock.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CasesService } from './types'; - -export const openAddToExistingCaseModalMock = jest.fn(); -export const openAddToNewCaseFlyoutMock = jest.fn(); - -const uiMock: jest.MockedObject = { - getCasesContext: jest.fn().mockImplementation(() => null), -}; - -const hooksMock: jest.MockedObject = { - useCasesAddToNewCaseFlyout: jest.fn().mockImplementation(() => ({ - open: openAddToNewCaseFlyoutMock, - })), - useCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({ - open: openAddToExistingCaseModalMock, - })), -}; - -const helpersMock: jest.MockedObject = { - canUseCases: jest.fn(), - groupAlertsByRule: jest.fn(), -}; - -export const createCasesServiceMock = (): jest.MaybeMockedDeep => ({ - ui: uiMock, - hooks: hooksMock, - helpers: helpersMock, -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/query_client.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/query_client.ts deleted file mode 100644 index 133a6d5fbdd46..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/query_client.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { QueryClient } from '@tanstack/react-query'; - -export const alertsTableQueryClient = new QueryClient(); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.test.tsx deleted file mode 100644 index cedc0eccfd68e..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.test.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import DefaultAlertActions from './default_alert_actions'; -import { render, screen } from '@testing-library/react'; -import type { AlertActionsProps } from '../../../../types'; - -jest.mock('../../../hooks/use_load_rule_types_query', () => ({ - useLoadRuleTypesQuery: jest.fn(), -})); -jest.mock('./view_rule_details_alert_action', () => { - return { - ViewRuleDetailsAlertAction: () => ( -
{'ViewRuleDetailsAlertAction'}
- ), - }; -}); -jest.mock('./view_alert_details_alert_action', () => { - return { - ViewAlertDetailsAlertAction: () => ( -
{'ViewAlertDetailsAlertAction'}
- ), - }; -}); -jest.mock('./mute_alert_action', () => { - return { MuteAlertAction: () =>
{'MuteAlertAction'}
}; -}); -jest.mock('./mark_as_untracked_alert_action', () => { - return { - MarkAsUntrackedAlertAction: () => ( -
{'MarkAsUntrackedAlertAction'}
- ), - }; -}); - -const { useLoadRuleTypesQuery } = jest.requireMock('../../../hooks/use_load_rule_types_query'); -const props = { alert: {}, refresh: jest.fn() } as unknown as AlertActionsProps; - -describe('DefaultAlertActions component', () => { - it('should show "Mute" and "Marked as untracted" option', async () => { - useLoadRuleTypesQuery.mockReturnValue({ authorizedToCreateAnyRules: true }); - - render(); - - expect(await screen.findByText('MuteAlertAction')).toBeInTheDocument(); - expect(await screen.findByText('MarkAsUntrackedAlertAction')).toBeInTheDocument(); - }); - - it('should hide "Mute" and "Marked as untracted" option', async () => { - useLoadRuleTypesQuery.mockReturnValue({ authorizedToCreateAnyRules: false }); - - render(); - - expect(screen.queryByText('MuteAlertAction')).not.toBeInTheDocument(); - expect(screen.queryByText('MarkAsUntrackedAlertAction')).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.tsx deleted file mode 100644 index 791eca7b2489a..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/default_alert_actions.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { ViewRuleDetailsAlertAction } from './view_rule_details_alert_action'; -import type { AlertActionsProps } from '../../../../types'; -import { ViewAlertDetailsAlertAction } from './view_alert_details_alert_action'; -import { MuteAlertAction } from './mute_alert_action'; -import { MarkAsUntrackedAlertAction } from './mark_as_untracked_alert_action'; -import { useLoadRuleTypesQuery } from '../../../hooks/use_load_rule_types_query'; - -/** - * Common alerts table row actions - */ -export const DefaultAlertActions = (props: AlertActionsProps) => { - const { authorizedToCreateAnyRules } = useLoadRuleTypesQuery({ - filteredRuleTypes: [], - }); - - return ( - <> - - - {authorizedToCreateAnyRules && } - {authorizedToCreateAnyRules && } - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { DefaultAlertActions as default }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/index.ts deleted file mode 100644 index b48e6d0f8f727..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { lazy } from 'react'; -import { suspendedComponentWithProps } from '../../../lib/suspended_component_with_props'; - -export const DefaultAlertActions = suspendedComponentWithProps( - lazy(() => import('./default_alert_actions')) -); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mute_alert_action.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mute_alert_action.tsx deleted file mode 100644 index 009742d2ca2fe..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/mute_alert_action.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiContextMenuItem } from '@elastic/eui'; -import React, { memo, useCallback, useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { ALERT_STATUS, ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; -import { useMuteAlert } from '../hooks/alert_mute/use_mute_alert'; -import type { AlertActionsProps } from '../../../../types'; -import { MUTE, UNMUTE } from '../hooks/translations'; -import { useUnmuteAlert } from '../hooks/alert_mute/use_unmute_alert'; -import { useAlertMutedState } from '../hooks/alert_mute/use_alert_muted_state'; - -/** - * Alerts table row action to mute/unmute the selected alert - */ -export const MuteAlertAction = memo(({ alert, refresh, onActionExecuted }: AlertActionsProps) => { - const { isMuted, ruleId, rule, alertInstanceId } = useAlertMutedState(alert); - const { mutateAsync: muteAlert } = useMuteAlert(); - const { mutateAsync: unmuteAlert } = useUnmuteAlert(); - const isAlertActive = useMemo(() => alert[ALERT_STATUS]?.[0] === ALERT_STATUS_ACTIVE, [alert]); - - const toggleAlert = useCallback(async () => { - if (ruleId == null || alertInstanceId == null) { - return; - } - if (isMuted) { - await unmuteAlert({ ruleId, alertInstanceId }); - } else { - await muteAlert({ ruleId, alertInstanceId }); - } - onActionExecuted?.(); - refresh(); - }, [alertInstanceId, isMuted, muteAlert, onActionExecuted, refresh, ruleId, unmuteAlert]); - - if ((!isAlertActive && !isMuted) || ruleId == null || alertInstanceId == null) { - return null; - } - - return ( - - {!rule - ? i18n.translate('xpack.triggersActionsUI.alertsTable.loadingMutedState', { - defaultMessage: 'Loading muted state', - }) - : isMuted - ? UNMUTE - : MUTE} - - ); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_rule_details_alert_action.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_rule_details_alert_action.tsx deleted file mode 100644 index a92e710b626bc..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/row_actions/view_rule_details_alert_action.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiContextMenuItem } from '@elastic/eui'; -import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import styled from '@emotion/styled'; -import { useKibana } from '../../../../common/lib/kibana'; -import type { AlertActionsProps } from '../../../../types'; - -const MenuItem = styled(EuiContextMenuItem)` - &:hover { - text-decoration: underline; - } -`; - -/** - * Alerts table row action to open the rule to which the selected alert is associated - */ -export const ViewRuleDetailsAlertAction = memo( - ({ alert, resolveRulePagePath, tableId }: AlertActionsProps) => { - const { - http: { - basePath: { prepend }, - }, - } = useKibana().services; - - const ruleId = alert[ALERT_RULE_UUID]?.[0] ?? null; - const pagePath = ruleId && tableId && resolveRulePagePath?.(ruleId, tableId); - const linkToRule = pagePath ? prepend(pagePath) : null; - - if (!linkToRule) { - return null; - } - - return ( - - {i18n.translate('xpack.triggersActionsUI.alertsTable.viewRuleDetails', { - defaultMessage: 'View rule details', - })} - - ); - } -); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.test.tsx deleted file mode 100644 index 2a48a94216d77..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/index.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render, screen, cleanup, fireEvent } from '@testing-library/react'; -import React from 'react'; - -import { InspectButton } from '.'; - -jest.mock('./modal', () => ({ - ModalInspectQuery: jest.fn(() =>
), -})); - -describe('Inspect Button', () => { - const alertsQuerySnapshot = { - request: [''], - response: [''], - }; - - afterEach(() => { - cleanup(); - }); - - test('open Inspect Modal', async () => { - render( - - ); - fireEvent.click(await screen.findByTestId('inspect-icon-button')); - - expect(await screen.findByTestId('mocker-modal')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/translations.ts deleted file mode 100644 index 6dbedeada1ffa..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/translations.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const INSPECT = i18n.translate('xpack.triggersActionsUI.inspectDescription', { - defaultMessage: 'Inspect', -}); - -export const CLOSE = i18n.translate('xpack.triggersActionsUI.inspect.modal.closeTitle', { - defaultMessage: 'Close', -}); - -export const SOMETHING_WENT_WRONG = i18n.translate( - 'xpack.triggersActionsUI.inspect.modal.somethingWentWrongDescription', - { - defaultMessage: 'Sorry about that, something went wrong.', - } -); -export const INDEX_PATTERN = i18n.translate( - 'xpack.triggersActionsUI.inspect.modal.indexPatternLabel', - { - defaultMessage: 'Index pattern', - } -); - -export const INDEX_PATTERN_DESC = i18n.translate( - 'xpack.triggersActionsUI.inspect.modal.indexPatternDescription', - { - defaultMessage: - 'The index pattern that connected to the Elasticsearch indices. These indices can be configured in Kibana > Advanced Settings.', - } -); - -export const QUERY_TIME = i18n.translate('xpack.triggersActionsUI.inspect.modal.queryTimeLabel', { - defaultMessage: 'Query time', -}); - -export const QUERY_TIME_DESC = i18n.translate( - 'xpack.triggersActionsUI.inspect.modal.queryTimeDescription', - { - defaultMessage: - 'The time it took to process the query. Does not include the time to send the request or parse it in the browser.', - } -); - -export const REQUEST_TIMESTAMP = i18n.translate( - 'xpack.triggersActionsUI.inspect.modal.reqTimestampLabel', - { - defaultMessage: 'Request timestamp', - } -); - -export const REQUEST_TIMESTAMP_DESC = i18n.translate( - 'xpack.triggersActionsUI.inspect.modal.reqTimestampDescription', - { - defaultMessage: 'Time when the start of the request has been logged', - } -); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts deleted file mode 100644 index ec68d475823b0..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/translations.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const UPDATING = i18n.translate('xpack.triggersActionsUI.alertsTable.lastUpdated.updating', { - defaultMessage: 'Updating...', -}); - -export const UPDATED = i18n.translate('xpack.triggersActionsUI.alertsTable.lastUpdated.updated', { - defaultMessage: 'Updated', -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/index.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/index.tsx deleted file mode 100644 index 41190da81cca9..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/toolbar/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './toolbar_visibility'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/translations.ts deleted file mode 100644 index 86441c02a7ac5..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/translations.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -export { CASES, MAINTENANCE_WINDOWS } from '../translations'; - -export const ALERTS_TABLE_CONF_ERROR_TITLE = i18n.translate( - 'xpack.triggersActionsUI.alertsTable.configuration.errorTitle', - { - defaultMessage: 'Unable to load alerts table', - } -); - -export const ALERTS_TABLE_CONF_ERROR_MESSAGE = i18n.translate( - 'xpack.triggersActionsUI.alertsTable.configuration.errorBody', - { - defaultMessage: - 'There was an error loading the alerts table. This table is missing the necessary configuration. Please contact your administrator for help', - } -); - -export const ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.column.actions', - { - defaultMessage: 'Actions', - } -); - -export const ALERTS_TABLE_TITLE = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.title', - { - defaultMessage: 'Alerts table', - } -); - -export const ALERTS_TABLE_FILTERS_ERROR_TITLE = i18n.translate( - 'xpack.triggersActionsUI.alertsTable.filters.errorTitle', - { - defaultMessage: 'Unsupported alerts filters set', - } -); - -export const ALERTS_TABLE_UNKNOWN_ERROR_TITLE = i18n.translate( - 'xpack.triggersActionsUI.alertsTable.unknownErrorTitle', - { - defaultMessage: 'Cannot display alerts', - } -); - -export const ALERTS_TABLE_UNKNOWN_ERROR_MESSAGE = i18n.translate( - 'xpack.triggersActionsUI.alertsTable.unknownErrorBody', - { - defaultMessage: 'An error occurred while rendering the alerts table', - } -); - -export const ALERTS_TABLE_UNKNOWN_ERROR_COPY_TO_CLIPBOARD_LABEL = i18n.translate( - 'xpack.triggersActionsUI.alertsTable.unknownErrorCopyToClipboardLabel', - { - defaultMessage: 'Copy error to clipboard', - } -); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/types.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/types.ts deleted file mode 100644 index 0891a6195873b..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/types.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { - AlertConsumers, - ALERT_CASE_IDS, - ALERT_STATUS, - ALERT_MAINTENANCE_WINDOW_IDS, -} from '@kbn/rule-data-utils'; -import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; -import { ComponentProps, Dispatch, ReducerAction, ReducerState } from 'react'; -import { AlertsTableProps } from '../../../types'; -import type { bulkActionsReducer } from './bulk_actions/reducer'; - -export interface Consumer { - id: AlertConsumers; - name: string; -} - -export type AlertsTableSupportedConsumers = Exclude; - -export type ServerError = IHttpFetchError; - -export type CellComponent = NonNullable; - -export type CellComponentProps = ComponentProps; - -export interface SystemCellComponentMap { - [ALERT_STATUS]: CellComponent; - [ALERT_CASE_IDS]: CellComponent; - [ALERT_MAINTENANCE_WINDOW_IDS]: CellComponent; -} - -export type SystemCellId = keyof SystemCellComponentMap; - -type UseCasesAddToNewCaseFlyout = (props?: Record & { onSuccess: () => void }) => { - open: ({ attachments }: { attachments: any[] }) => void; - close: () => void; -}; - -type UseCasesAddToExistingCaseModal = ( - props?: Record & { onSuccess: () => void } -) => { - open: ({ - getAttachments, - }: { - getAttachments: ({ theCase }: { theCase?: { id: string } }) => any[]; - }) => void; - close: () => void; -}; - -export interface CasesService { - ui: { - getCasesContext: () => React.FC; - }; - hooks: { - useCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout; - useCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal; - }; - helpers: { - groupAlertsByRule: (items?: any[]) => any[]; - canUseCases: (owners: string[]) => Record; - }; -} - -/** - * Map from rule ids to muted alert instance ids - */ -export type MutedAlerts = Record; - -export interface ToggleAlertParams { - ruleId: string; - alertInstanceId: string; -} - -export interface AlertsTableContextType { - mutedAlerts: MutedAlerts; - bulkActions: [ - ReducerState, - Dispatch> - ]; - resolveRulePagePath?: (ruleId: string) => string; - resolveAlertPagePath?: (alertId: string) => string; -} diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/utils.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/utils.ts deleted file mode 100644 index 02078e1bb0f93..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { memo } from 'react'; - -export const typedMemo: (c: T) => T = memo; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx index a2c21c1222815..a23cde45380e4 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/common/components/with_bulk_rule_api_operations.tsx @@ -15,6 +15,8 @@ import { import { AlertingFrameworkHealth } from '@kbn/alerting-types'; import { fetchAlertingFrameworkHealth as alertingFrameworkHealth } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerting_framework_health'; import { resolveRule } from '@kbn/response-ops-rule-form'; +import { muteAlertInstance } from '@kbn/response-ops-alerts-apis/apis/mute_alert_instance'; +import { unmuteAlertInstance } from '@kbn/response-ops-alerts-apis/apis/unmute_alert_instance'; import { Rule, RuleType, @@ -39,7 +41,6 @@ import type { import { cloneRule } from '../../../lib/rule_api/clone'; import { loadRule } from '../../../lib/rule_api/get_rule'; import { loadRuleSummary } from '../../../lib/rule_api/rule_summary'; -import { muteAlertInstance } from '../../../lib/rule_api/mute_alert'; import { loadRuleTypes } from '../../../lib/rule_api/rule_types'; import { loadExecutionLogAggregations, @@ -51,7 +52,6 @@ import { loadRuleState } from '../../../lib/rule_api/state'; import { loadExecutionKPIAggregations } from '../../../lib/rule_api/load_execution_kpi_aggregations'; import { loadGlobalExecutionKPIAggregations } from '../../../lib/rule_api/load_global_execution_kpi_aggregations'; import { loadActionErrorLog } from '../../../lib/rule_api/load_action_error_log'; -import { unmuteAlertInstance } from '../../../lib/rule_api/unmute_alert'; import { snoozeRule, bulkSnoozeRules } from '../../../lib/rule_api/snooze'; import { unsnoozeRule, bulkUnsnoozeRules } from '../../../lib/rule_api/unsnooze'; import { bulkDeleteRules } from '../../../lib/rule_api/bulk_delete'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.styles.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.styles.ts deleted file mode 100644 index db349e19ef374..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/categories_badges.styles.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { css } from '@emotion/react'; -import { UseEuiTheme } from '@elastic/eui'; - -export const styles = { - badgesGroup: ({ euiTheme }: { euiTheme: UseEuiTheme['euiTheme'] }) => css` - margin-top: ${euiTheme.size.xs}; - min-height: 24px; - `, -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/index.ts deleted file mode 100644 index 26e882bb0e1d9..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_badges/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { CategoriesBadges } from './categories_badges'; -export type { CategoriesBadgesProps } from './categories_badges'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.styles.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.styles.ts deleted file mode 100644 index d552a242fe244..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/categories_selector.styles.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { css } from '@emotion/react'; - -export const styles = { - countBadge: css` - margin-left: 5px; - `, - categoryName: ({ bold }: { bold: boolean }) => css` - font-weight: ${bold ? 'bold' : 'normal'}; - `, - selectableContainer: css` - width: 300px; - `, -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/index.ts deleted file mode 100644 index 6c49384ce6913..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/categories_selector/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { CategoriesSelector } from './categories_selector'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/index.ts deleted file mode 100644 index d783e8cd7f095..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_items/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { getFieldItemsData, getFieldColumns } from './field_items'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/index.ts deleted file mode 100644 index 7891ca3b0555d..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_name/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { FieldName } from './field_name'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.styles.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.styles.ts deleted file mode 100644 index a29b0050babcf..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/field_table_header.styles.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { css } from '@emotion/react'; - -export const styles = { - count: css` - font-weight: bold; - `, -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/index.ts deleted file mode 100644 index 02dee020e0abc..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/field_table/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { FieldTable } from './field_table'; -export type { FieldTableProps } from './field_table'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/index.ts deleted file mode 100644 index 3b965d88b9bc3..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/components/search/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { Search } from './search'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.styles.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.styles.ts deleted file mode 100644 index 6db8a68f734d6..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/field_browser.styles.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { css } from '@emotion/react'; - -export const styles = { - buttonContainer: css` - display: inline-block; - position: relative; - `, -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/index.ts deleted file mode 100644 index a5f6df3b44e02..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { FieldBrowser } from './field_browser'; -export type { FieldBrowserProps } from './types'; - -export { FieldBrowser }; -// eslint-disable-next-line import/no-default-export -export { FieldBrowser as default }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/translations.ts deleted file mode 100644 index 0183e2fb79a74..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/field_browser/translations.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export { CASES, MAINTENANCE_WINDOWS } from '../translations'; - -export const CATEGORY = i18n.translate('xpack.triggersActionsUI.fieldBrowser.categoryLabel', { - defaultMessage: 'Category', -}); - -export const CATEGORIES = i18n.translate('xpack.triggersActionsUI.fieldBrowser.categoriesTitle', { - defaultMessage: 'Categories', -}); - -export const CATEGORIES_COUNT = (totalCount: number) => - i18n.translate('xpack.triggersActionsUI.fieldBrowser.categoriesCountTitle', { - values: { totalCount }, - defaultMessage: '{totalCount} {totalCount, plural, =1 {category} other {categories}}', - }); - -export const CLOSE = i18n.translate('xpack.triggersActionsUI.fieldBrowser.closeButton', { - defaultMessage: 'Close', -}); - -export const FIELDS_BROWSER = i18n.translate( - 'xpack.triggersActionsUI.fieldBrowser.fieldBrowserTitle', - { - defaultMessage: 'Fields', - } -); - -export const DESCRIPTION = i18n.translate('xpack.triggersActionsUI.fieldBrowser.descriptionLabel', { - defaultMessage: 'Description', -}); - -export const DESCRIPTION_FOR_FIELD = (field: string) => - i18n.translate('xpack.triggersActionsUI.fieldBrowser.descriptionForScreenReaderOnly', { - values: { - field, - }, - defaultMessage: 'Description for field {field}:', - }); - -export const NAME = i18n.translate('xpack.triggersActionsUI.fieldBrowser.fieldName', { - defaultMessage: 'Name', -}); - -export const FIELD = i18n.translate('xpack.triggersActionsUI.fieldBrowser.fieldLabel', { - defaultMessage: 'Field', -}); - -export const FIELDS = i18n.translate('xpack.triggersActionsUI.fieldBrowser.fieldsTitle', { - defaultMessage: 'Fields', -}); - -export const FIELDS_SHOWING = i18n.translate( - 'xpack.triggersActionsUI.fieldBrowser.fieldsCountShowing', - { - defaultMessage: 'Showing', - } -); - -export const FIELDS_COUNT = (totalCount: number) => - i18n.translate('xpack.triggersActionsUI.fieldBrowser.fieldsCountTitle', { - values: { totalCount }, - defaultMessage: '{totalCount, plural, =1 {field} other {fields}}', - }); - -export const FILTER_PLACEHOLDER = i18n.translate( - 'xpack.triggersActionsUI.fieldBrowser.filterPlaceholder', - { - defaultMessage: 'Field name', - } -); - -export const NO_FIELDS_MATCH = i18n.translate( - 'xpack.triggersActionsUI.fieldBrowser.noFieldsMatchLabel', - { - defaultMessage: 'No fields match', - } -); - -export const NO_FIELDS_MATCH_INPUT = (searchInput: string) => - i18n.translate('xpack.triggersActionsUI.fieldBrowser.noFieldsMatchInputLabel', { - defaultMessage: 'No fields match {searchInput}', - values: { - searchInput, - }, - }); - -export const RESET_FIELDS = i18n.translate('xpack.triggersActionsUI.fieldBrowser.resetFieldsLink', { - defaultMessage: 'Reset Fields', -}); - -export const VIEW_COLUMN = (field: string) => - i18n.translate('xpack.triggersActionsUI.fieldBrowser.viewColumnCheckboxAriaLabel', { - values: { field }, - defaultMessage: 'View {field} column', - }); - -export const VIEW_LABEL = i18n.translate('xpack.triggersActionsUI.fieldBrowser.viewLabel', { - defaultMessage: 'View', -}); - -export const VIEW_VALUE_SELECTED = i18n.translate( - 'xpack.triggersActionsUI.fieldBrowser.viewSelected', - { - defaultMessage: 'selected', - } -); - -export const VIEW_VALUE_ALL = i18n.translate('xpack.triggersActionsUI.fieldBrowser.viewAll', { - defaultMessage: 'all', -}); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx index 34884010b6be2..0075fe6682786 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx @@ -21,8 +21,8 @@ import { RuleSummary, AlertStatus, RuleType, RuleTypeModel } from '../../../../t import { mockRule, mockLogResponse } from './test_helpers'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; import { useKibana } from '../../../../common/lib/kibana'; -import { useBulkGetMaintenanceWindowsQuery } from '../../alerts_table/hooks/use_bulk_get_maintenance_windows'; -import { getMaintenanceWindowsMock } from '../../alerts_table/maintenance_windows/index.mock'; +import { useBulkGetMaintenanceWindowsQuery } from '@kbn/response-ops-alerts-table/hooks/use_bulk_get_maintenance_windows'; +import { getMaintenanceWindowsMock } from '@kbn/response-ops-alerts-table/mocks/maintenance_windows.mock'; import { loadRuleTypes } from '../../../lib/rule_api/rule_types'; jest.mock('../../../lib/rule_api/rule_types'); @@ -38,7 +38,7 @@ jest.mock('../../../../common/lib/kibana', () => ({ jest.mock('../../../../common/get_experimental_features', () => ({ getIsExperimentalFeatureEnabled: jest.fn(), })); -jest.mock('../../alerts_table/hooks/use_bulk_get_maintenance_windows'); +jest.mock('@kbn/response-ops-alerts-table/hooks/use_bulk_get_maintenance_windows'); jest.mock('../../../lib/rule_api/load_execution_log_aggregations', () => ({ loadExecutionLogAggregations: jest.fn(), })); @@ -46,9 +46,9 @@ jest.mock('../../../lib/rule_api/load_execution_log_aggregations', () => ({ const mockAlertsTable = jest.fn(() => { return
; }); -jest.mock('../../alerts_table/alerts_table_state', () => ({ +jest.mock('@kbn/response-ops-alerts-table/components/alerts_table', () => ({ __esModule: true, - AlertsTableState: mockAlertsTable, + AlertsTable: mockAlertsTable, default: mockAlertsTable, })); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx index 3715cb7544d7c..9711448bc4916 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx @@ -10,6 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiTabbedContent, useEuiTheme } from '@elastic/eui'; import { AlertStatusValues } from '@kbn/alerting-plugin/common'; import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; +import { defaultAlertsTableColumns } from '@kbn/response-ops-alerts-table/configuration'; +import type { AlertsTable as AlertsTableType } from '@kbn/response-ops-alerts-table'; import { useKibana } from '../../../../common/lib/kibana'; import { Rule, RuleSummary, AlertStatus, RuleType } from '../../../../types'; import { @@ -31,12 +33,11 @@ import { rulesLastRunOutcomeTranslationMapping, rulesStatusesTranslationsMapping, } from '../../rules_list/translations'; -import { defaultAlertsTableColumns } from '../../alerts_table/configuration'; const RuleEventLogList = lazy(() => import('./rule_event_log_list')); const RuleAlertList = lazy(() => import('./rule_alert_list')); const RuleDefinition = lazy(() => import('./rule_definition')); -const AlertsTable = lazy(() => import('../../alerts_table/alerts_table')); +const AlertsTable = lazy(() => import('@kbn/response-ops-alerts-table')) as AlertsTableType; export type RuleComponentProps = { rule: Rule; @@ -71,7 +72,17 @@ export function RuleComponent({ durationEpoch = Date.now(), isLoadingChart, }: RuleComponentProps) { - const { ruleTypeRegistry, actionTypeRegistry } = useKibana().services; + const { + ruleTypeRegistry, + actionTypeRegistry, + data, + http, + notifications, + fieldFormats, + application, + licensing, + settings, + } = useKibana().services; // The lastReloadRequestTime should be updated when the refreshToken changes // eslint-disable-next-line react-hooks/exhaustive-deps const lastReloadRequestTime = useMemo(() => new Date().getTime(), [refreshToken]); @@ -110,6 +121,15 @@ export function RuleComponent({ showAlertStatusWithFlapping columns={alertsTableColumns} lastReloadRequestTime={lastReloadRequestTime} + services={{ + data, + http, + notifications, + fieldFormats, + application, + licensing, + settings, + }} /> ); } @@ -123,13 +143,20 @@ export function RuleComponent({ }); }, [ alerts, - onMuteAction, + application, + data, + fieldFormats, + http, lastReloadRequestTime, + licensing, + notifications, + onMuteAction, readOnly, rule.id, ruleType.hasAlertsMappings, ruleType.hasFieldsForAAD, ruleType.id, + settings, ]); const tabs = [ diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx index c075335d99601..330a2b8fd8b57 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx @@ -17,13 +17,14 @@ import { ALERT_STATUS_UNTRACKED, } from '@kbn/rule-data-utils'; import { AlertStatusValues, MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import { useBulkGetMaintenanceWindowsQuery } from '@kbn/response-ops-alerts-table/hooks/use_bulk_get_maintenance_windows'; +import { MaintenanceWindowBaseCell } from '@kbn/response-ops-alerts-table/components/maintenance_windows_cell'; import { DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants'; import { Pagination } from '../../../../types'; import { AlertListItem } from './types'; import { AlertMutedSwitch } from './alert_muted_switch'; import { AlertLifecycleStatusBadge } from '../../../components/alert_lifecycle_status_badge'; -import { useBulkGetMaintenanceWindowsQuery } from '../../alerts_table/hooks/use_bulk_get_maintenance_windows'; -import { MaintenanceWindowBaseCell } from '../../alerts_table/maintenance_windows/cell'; +import { useKibana } from '../../../../common'; export const getConvertedAlertStatus = ( status: AlertStatusValues, @@ -98,6 +99,7 @@ const RuleAlertListMaintenanceWindowCell = (props: RuleAlertListMaintenanceWindo }; export const RuleAlertList = (props: RuleAlertListProps) => { + const { http, application, notifications, licensing } = useKibana().services; const { items, readOnly, onMuteAction } = props; const [pagination, setPagination] = useState({ @@ -127,6 +129,10 @@ export const RuleAlertList = (props: RuleAlertListProps) => { const { data: maintenanceWindows, isFetching: isLoadingMaintenanceWindows } = useBulkGetMaintenanceWindowsQuery({ ids: Array.from(maintenanceWindowIds.values()), + http, + application, + notifications, + licensing, }); const alertsTableColumns = useMemo( diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/translations.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/translations.ts index a4776b58f442f..4ecf4e8907dc3 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/translations.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/translations.ts @@ -18,77 +18,6 @@ export const MAINTENANCE_WINDOWS = i18n.translate( } ); -export const OBSERVABILITY_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.observability', - { - defaultMessage: 'Observability', - } -); - -export const SECURITY_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.security', - { - defaultMessage: 'Security', - } -); - -export const STACK_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.stack', - { - defaultMessage: 'Stack', - } -); - -export const STACK_MONITORING_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.stackMonitoring', - { - defaultMessage: 'Stack Monitoring', - } -); - -export const UPTIME_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.uptime', - { - defaultMessage: 'Uptime', - } -); - -export const APM_DISPLAY_NAME = i18n.translate('xpack.triggersActionsUI.sections.alertsTable.apm', { - defaultMessage: 'APM', -}); - -export const INFRASTRUCTURE_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.infrastructure', - { - defaultMessage: 'Infrastructure', - } -); - -export const SLO_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.slos', - { - defaultMessage: 'SLOs', - } -); - -export const LOGS_DISPLAY_NAME = i18n.translate( - 'xpack.triggersActionsUI.sections.alertsTable.logs', - { - defaultMessage: 'Logs', - } -); - -export const ML_DISPLAY_NAME = i18n.translate('xpack.triggersActionsUI.sections.alertsTable.ml', { - defaultMessage: 'Machine Learning', -}); - -export const FEATURE_LABEL = i18n.translate( - 'xpack.triggersActionsUI.sections.globalAlerts.quickFilters.feature', - { - defaultMessage: 'Feature', - } -); - export const TECH_PREVIEW_LABEL = i18n.translate( 'xpack.triggersActionsUI.technicalPreviewBadgeLabel', { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table.tsx deleted file mode 100644 index 690af3857f301..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table.tsx +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import type { AlertsDataGridProps } from '../types'; -import { AlertsDataGrid } from '../application/sections/alerts_table/alerts_data_grid'; - -export const getAlertsTableLazy = (props: AlertsDataGridProps) => { - return ; -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_default_row_actions.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_default_row_actions.tsx deleted file mode 100644 index d902842561053..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_default_row_actions.tsx +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { DefaultAlertActions } from '../application/sections/alerts_table/row_actions'; -import type { AlertActionsProps } from '../types'; - -export const getAlertsTableDefaultAlertActionsLazy = (props: AlertActionsProps) => { - return ; -}; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_state.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_state.tsx deleted file mode 100644 index 924a495796277..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_alerts_table_state.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { lazy, Ref, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; -import { typedForwardRef } from '../../common/utils'; -import { - AdditionalContext, - AlertsTableImperativeApi, - AlertsTableProps, - LazyLoadProps, -} from '../types'; -import { type AlertsTable } from '../application/sections/alerts_table/alerts_table'; - -const AlertsTableStateLazy = lazy( - () => import('../application/sections/alerts_table/alerts_table') -) as typeof AlertsTable; - -export const getAlertsTableStateLazy = typedForwardRef( - ( - { hideLazyLoader, ...props }: AlertsTableProps & LazyLoadProps, - ref: Ref - ) => ( - }> - - - ) -); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_field_browser.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_field_browser.tsx deleted file mode 100644 index fe7940dd7b14e..0000000000000 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_field_browser.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; - -import type { FieldBrowserProps } from '../application/sections/field_browser'; - -const FieldBrowserLazy: React.FC = lazy( - () => import('../application/sections/field_browser') -); - -export const getFieldBrowserLazy = (props: FieldBrowserProps) => ( - }> - - -); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/index.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/index.ts index 6ec8884131e53..31021c87df894 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/index.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/index.ts @@ -30,14 +30,9 @@ export type { RuleFlyoutCloseReason, RuleTypeParams, AsApiContract, - AlertsDataGridProps, RuleSummary, AlertStatus, - AlertsTableFlyoutBaseProps, RuleEventLogListProps, - AlertTableFlyoutComponent, - FieldBrowserOptions, - FieldBrowserProps, RuleDefinitionProps, RulesListVisibleColumns, AlertSummaryTimeRange, @@ -70,8 +65,6 @@ export { export type { ConnectorFormSchema } from './application/sections/action_connector_form'; -export { getCategory } from './application/sections/field_browser/helpers'; - export type { ConfigFieldSchema, SecretsFieldSchema } from './application/components'; export { @@ -152,5 +145,3 @@ export const getNotifyWhenOptions = async () => { export { transformRule } from './application/lib/rule_api/common_transformations'; export { validateActionFilterQuery } from './application/lib/value_validators'; - -export { useBulkUntrackAlerts } from './application/sections/alerts_table/hooks/use_bulk_untrack_alerts'; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts index a4ce0de51a099..f07d4ca2f3be9 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/mocks.ts @@ -9,7 +9,6 @@ import { RuleAction } from '@kbn/alerting-plugin/common'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { TypeRegistry } from '@kbn/alerts-ui-shared/src/common/type_registry'; import { uiSettingsServiceMock } from '@kbn/core/public/mocks'; -import { getAlertsTableDefaultAlertActionsLazy } from './common/get_alerts_table_default_row_actions'; import type { TriggersAndActionsUIPublicPluginStart } from './plugin'; import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; @@ -19,16 +18,12 @@ import { getEditRuleFlyoutLazy } from './common/get_edit_rule_flyout'; import { ActionTypeModel, RuleTypeModel, - AlertsDataGridProps, - FieldBrowserProps, RuleTagBadgeOptions, RuleTagBadgeProps, RuleEventLogListOptions, RuleEventLogListProps, RuleUiAction, - AdditionalContext, } from './types'; -import { getAlertsTableLazy } from './common/get_alerts_table'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; import { getRuleTagFilterLazy } from './common/get_rule_tag_filter'; import { getRuleStatusFilterLazy } from './common/get_rule_status_filter'; @@ -36,7 +31,6 @@ import { getRuleTagBadgeLazy } from './common/get_rule_tag_badge'; import { getRuleEventLogListLazy } from './common/get_rule_event_log_list'; import { getGlobalRuleEventLogListLazy } from './common/get_global_rule_event_log_list'; import { getRulesListLazy } from './common/get_rules_list'; -import { getAlertsTableStateLazy } from './common/get_alerts_table_state'; import { getAlertsSearchBarLazy } from './common/get_alerts_search_bar'; import { getRulesListNotifyBadgeLazy } from './common/get_rules_list_notify_badge'; import { AlertsSearchBarProps } from './application/sections/alerts_search_bar'; @@ -44,13 +38,11 @@ import { CreateConnectorFlyoutProps } from './application/sections/action_connec import { EditConnectorFlyoutProps } from './application/sections/action_connector_form/edit_connector_flyout'; import { getActionFormLazy } from './common/get_action_form'; import { ActionAccordionFormProps } from './application/sections/action_connector_form/action_form'; -import { getFieldBrowserLazy } from './common/get_field_browser'; import { getAlertSummaryWidgetLazy } from './common/get_rule_alerts_summary'; import { getRuleDefinitionLazy } from './common/get_rule_definition'; import { getRuleStatusPanelLazy } from './common/get_rule_status_panel'; import { getRuleSnoozeModalLazy } from './common/get_rule_snooze_modal'; import { getRulesSettingsLinkLazy } from './common/get_rules_settings_link'; -import { AlertActionsProps } from './types'; import { AlertSummaryWidgetDependencies } from './application/sections/alert_summary_widget/types'; function createStartMock(): TriggersAndActionsUIPublicPluginStart { @@ -99,19 +91,9 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart { connectorServices, }); }, - getAlertsStateTable: getAlertsTableStateLazy, getAlertsSearchBar: (props: AlertsSearchBarProps) => { return getAlertsSearchBarLazy(props); }, - getAlertsTable: (props: AlertsDataGridProps) => { - return getAlertsTableLazy(props); - }, - getAlertsTableDefaultAlertActions: (props: AlertActionsProps) => { - return getAlertsTableDefaultAlertActionsLazy(props); - }, - getFieldBrowser: (props: FieldBrowserProps) => { - return getFieldBrowserLazy(props); - }, getRuleStatusDropdown: (props) => { return getRuleStatusDropdownLazy(props); }, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts index 63adcb7deb7d2..b9cef8c580d70 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts @@ -8,7 +8,7 @@ import { CoreSetup, CoreStart, Plugin as CorePlugin } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; -import { ReactElement, RefAttributes } from 'react'; +import { ReactElement } from 'react'; import { PluginInitializerContext } from '@kbn/core/public'; import { FeaturesPluginStart } from '@kbn/features-plugin/public'; import { KibanaFeature } from '@kbn/features-plugin/common'; @@ -33,22 +33,13 @@ import { LensPublicStart } from '@kbn/lens-plugin/public'; import { RuleAction } from '@kbn/alerting-plugin/common'; import { TypeRegistry } from '@kbn/alerts-ui-shared/src/common/type_registry'; import { CloudSetup } from '@kbn/cloud-plugin/public'; -import { getAlertsTableDefaultAlertActionsLazy } from './common/get_alerts_table_default_row_actions'; -import type { - AdditionalContext, - AlertActionsProps, - AlertsTableImperativeApi, - AlertsTableProps, - RuleUiAction, -} from './types'; +import type { RuleUiAction } from './types'; import type { AlertsSearchBarProps } from './application/sections/alerts_search_bar'; import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; import { getAddRuleFlyoutLazy } from './common/get_add_rule_flyout'; import { getEditRuleFlyoutLazy } from './common/get_edit_rule_flyout'; -import { getAlertsTableLazy } from './common/get_alerts_table'; -import { getFieldBrowserLazy } from './common/get_field_browser'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; import { getRuleTagFilterLazy } from './common/get_rule_tag_filter'; import { getRuleStatusFilterLazy } from './common/get_rule_status_filter'; @@ -63,7 +54,6 @@ import { ExperimentalFeatures, parseExperimentalConfigValue, } from '../common/experimental_features'; -import { LazyLoadProps } from './types'; import type { ActionTypeModel, @@ -72,7 +62,6 @@ import type { RuleTypeModel, RuleTypeParams, RuleTypeMetaData, - AlertsDataGridProps, RuleStatusDropdownProps, RuleTagFilterProps, RuleStatusFilterProps, @@ -90,10 +79,8 @@ import type { } from './types'; import { TriggersActionsUiConfigType } from '../common/types'; import { PLUGIN_ID, CONNECTORS_PLUGIN_ID, ALERTS_PAGE_ID } from './common/constants'; -import { getAlertsTableStateLazy } from './common/get_alerts_table_state'; import { getAlertsSearchBarLazy } from './common/get_alerts_search_bar'; import { ActionAccordionFormProps } from './application/sections/action_connector_form/action_form'; -import type { FieldBrowserProps } from './application/sections/field_browser/types'; import { getRuleDefinitionLazy } from './common/get_rule_definition'; import { RuleStatusPanelProps } from './application/sections/rule_details/components/rule_status_panel'; import { AlertSummaryWidgetProps } from './application/sections/alert_summary_widget'; @@ -135,17 +122,7 @@ export interface TriggersAndActionsUIPublicPluginStart { >( props: Omit, 'actionTypeRegistry' | 'ruleTypeRegistry'> ) => ReactElement>; - getAlertsTable: ( - props: AlertsDataGridProps - ) => ReactElement>; - getAlertsTableDefaultAlertActions:

( - props: P - ) => ReactElement; - getAlertsStateTable: ( - props: AlertsTableProps & LazyLoadProps & RefAttributes - ) => ReactElement>; getAlertsSearchBar: (props: AlertsSearchBarProps) => ReactElement; - getFieldBrowser: (props: FieldBrowserProps) => ReactElement; getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement; getRuleTagFilter: (props: RuleTagFilterProps) => ReactElement; getRuleStatusFilter: (props: RuleStatusFilterProps) => ReactElement; @@ -495,19 +472,9 @@ export class Plugin connectorServices: this.connectorServices!, }); }, - getAlertsStateTable: getAlertsTableStateLazy, getAlertsSearchBar: (props: AlertsSearchBarProps) => { return getAlertsSearchBarLazy(props); }, - getAlertsTable: (props: AlertsDataGridProps) => { - return getAlertsTableLazy(props); - }, - getAlertsTableDefaultAlertActions: (props: AlertActionsProps) => { - return getAlertsTableDefaultAlertActionsLazy(props); - }, - getFieldBrowser: (props: FieldBrowserProps) => { - return getFieldBrowserLazy(props); - }, getRuleStatusDropdown: (props: RuleStatusDropdownProps) => { return getRuleStatusDropdownLazy(props); }, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/types.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/types.ts index 736cb8c53e069..31a93856cad9e 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/types.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/types.ts @@ -5,27 +5,18 @@ * 2.0. */ -import React, { - ComponentClass, - ComponentType, - Dispatch, - Key, - MutableRefObject, - ReactNode, - RefAttributes, - SetStateAction, -} from 'react'; +import React, { ComponentType } from 'react'; import type { Moment } from 'moment'; -import type { - EuiDataGridCellValueElementProps, - EuiDataGridColumnCellAction, - EuiDataGridProps, - EuiDataGridToolBarVisibilityOptions, - EuiSuperSelectOption, - EuiDataGridOnColumnResizeHandler, - EuiDataGridRefProps, -} from '@elastic/eui'; -import { EuiDataGridColumn, EuiDataGridControlColumn, EuiDataGridSorting } from '@elastic/eui'; +import type { EuiSuperSelectOption } from '@elastic/eui'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import type { DocLinksStart } from '@kbn/core/public'; +import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { RuleCreationValidConsumer } from '@kbn/rule-data-utils'; +import { HttpSetup } from '@kbn/core/public'; +import { KueryNode } from '@kbn/es-query'; import { ALERT_HISTORY_PREFIX, ActionType, @@ -40,7 +31,6 @@ import { AlertStatus, SanitizedRule as AlertingSanitizedRule, ExecutionDuration, - MaintenanceWindow, RawAlertInstance, ResolvedSanitizedRule, SanitizedRuleAction as RuleAction, @@ -53,65 +43,32 @@ import { RuleTypeParams, } from '@kbn/alerting-plugin/common'; import type { BulkOperationError } from '@kbn/alerting-plugin/server'; -import type { - RuleRegistrySearchRequestPagination, - EcsFieldsResponse, - BrowserFields, -} from '@kbn/rule-registry-plugin/common'; -import { TypeRegistry } from '@kbn/alerts-ui-shared/src/common/type_registry'; -import { - type MappingRuntimeFields, - QueryDslQueryContainer, - SortCombinations, -} from '@elastic/elasticsearch/lib/api/types'; import type { RuleType, RuleTypeIndex } from '@kbn/triggers-actions-ui-types'; import { ValidationResult, UserConfiguredActionConnector, ActionConnector, ActionTypeRegistryContract, - EsQuerySnapshot, - type LegacyField, } from '@kbn/alerts-ui-shared/src/common/types'; -import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import type { DocLinksStart } from '@kbn/core/public'; -import { HttpSetup } from '@kbn/core/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { KueryNode } from '@kbn/es-query'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { SetRequired } from 'type-fest'; -import type { RuleCreationValidConsumer } from '@kbn/rule-data-utils'; -import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { CreateConnectorFlyoutProps } from './application/sections/action_connector_form/create_connector_flyout'; -import type { EditConnectorFlyoutProps } from './application/sections/action_connector_form/edit_connector_flyout'; -import type { AlertSummaryTimeRange } from './application/sections/alert_summary_widget/types'; -import { TimelineItem } from './application/sections/alerts_table/bulk_actions/components/toolbar'; -import type { - BrowserFieldItem, - CreateFieldComponent, - FieldBrowserOptions, - FieldBrowserProps, - GetFieldTableColumns, -} from './application/sections/field_browser/types'; -import type { GlobalRuleEventLogListProps } from './application/sections/rule_details/components/global_rule_event_log_list'; -import type { - RuleEventLogListOptions, - RuleEventLogListProps, -} from './application/sections/rule_details/components/rule_event_log_list'; -import type { RulesListNotifyBadgePropsWithApi } from './application/sections/rules_list/components/notify_badge'; -import { Case } from './application/sections/alerts_table/hooks/apis/bulk_get_cases'; -import { MutedAlerts } from './application/sections/alerts_table/types'; +import { TypeRegistry } from '@kbn/alerts-ui-shared/src/common/type_registry'; import type { ComponentOpts as RuleStatusDropdownProps } from './application/sections/rules_list/components/rule_status_dropdown'; +import type { RuleTagFilterProps } from './application/sections/rules_list/components/rule_tag_filter'; import type { RuleStatusFilterProps } from './application/sections/rules_list/components/rule_status_filter'; +import type { RulesListProps } from './application/sections/rules_list/components/rules_list'; import type { - RuleTagBadgeOptions, RuleTagBadgeProps, + RuleTagBadgeOptions, } from './application/sections/rules_list/components/rule_tag_badge'; -import type { RuleTagFilterProps } from './application/sections/rules_list/components/rule_tag_filter'; -import type { RulesListProps } from './application/sections/rules_list/components/rules_list'; +import type { + RuleEventLogListProps, + RuleEventLogListOptions, +} from './application/sections/rule_details/components/rule_event_log_list'; +import type { GlobalRuleEventLogListProps } from './application/sections/rule_details/components/global_rule_event_log_list'; +import type { AlertSummaryTimeRange } from './application/sections/alert_summary_widget/types'; +import type { CreateConnectorFlyoutProps } from './application/sections/action_connector_form/create_connector_flyout'; +import type { EditConnectorFlyoutProps } from './application/sections/action_connector_form/edit_connector_flyout'; import { RulesListVisibleColumns } from './application/sections/rules_list/components/rules_list_column_selector'; +import type { RulesListNotifyBadgePropsWithApi } from './application/sections/rules_list/components/notify_badge'; export type { ActionConnectorFieldsProps, @@ -170,14 +127,7 @@ export type { AlertStatus, AlertSummaryTimeRange, AsApiContract, - BrowserFieldItem, - CreateConnectorFlyoutProps, - CreateFieldComponent, - EditConnectorFlyoutProps, ExecutionDuration, - FieldBrowserOptions, - FieldBrowserProps, - GetFieldTableColumns, GlobalRuleEventLogListProps, RawAlertInstance, ResolvedRule, @@ -202,6 +152,8 @@ export type { RuleUiAction, RulesListNotifyBadgePropsWithApi, RulesListProps, + CreateConnectorFlyoutProps, + EditConnectorFlyoutProps, RulesListVisibleColumns, SanitizedRule, UserConfiguredActionConnector, @@ -418,399 +370,6 @@ export interface TriggersActionsUiConfig { }; } -export enum AlertsField { - name = 'kibana.alert.rule.name', - reason = 'kibana.alert.reason', - uuid = 'kibana.alert.rule.uuid', - case_ids = 'kibana.alert.case_ids', -} - -export interface InspectQuery { - request: string[]; - response: string[]; -} - -export type GetInspectQuery = () => InspectQuery; - -export type Alert = EcsFieldsResponse; -export type Alerts = Alert[]; - -export interface FetchAlertData { - activePage: number; - alerts: Alerts; - alertsCount: number; - isInitializing: boolean; - isLoading: boolean; - getInspectQuery: GetInspectQuery; - onPageChange: (pagination: RuleRegistrySearchRequestPagination) => void; - onSortChange: (sort: EuiDataGridSorting['columns']) => void; - refresh: () => void; - sort: SortCombinations[]; - /** - * We need to have it because of lot code is expecting this format - * @deprecated - */ - oldAlertsData: Array>; - /** - * We need to have it because of lot code is expecting this format - * @deprecated - */ - ecsAlertsData: unknown[]; -} - -type MergeProps = T extends (args: infer Props) => unknown - ? (args: Props & AP) => ReactNode - : T extends ComponentClass - ? ComponentClass - : never; - -export interface SelectedAlertWithLegacyFormats { - alert: Alert; - /** - * @deprecated - */ - legacyAlert: LegacyField[]; - /** - * @deprecated - */ - ecsAlert: any; -} - -export interface AlertsTableProps - extends PublicAlertsDataGridProps { - id: string; - columns?: EuiDataGridProps['columns']; - query: Pick; - initialSort?: SortCombinations[]; - initialPageSize?: number; - browserFields?: BrowserFields; - onUpdate?: (context: RenderContext) => void; - onLoaded?: (alerts: Alerts, columns: EuiDataGridColumn[]) => void; - runtimeMappings?: MappingRuntimeFields; - showAlertStatusWithFlapping?: boolean; - toolbarVisibility?: EuiDataGridToolBarVisibilityOptions; - /** - * Allows to consumers of the table to decide to highlight a row based on the current alert. - */ - shouldHighlightRow?: (alert: Alert) => boolean; - /** - * Enable when rows may have variable heights (disables virtualization) - */ - dynamicRowHeight?: boolean; - emptyStateHeight?: 'tall' | 'short'; - /** - * An object that will be passed along with the renderContext to all render functions. - */ - additionalContext?: AC; - /** - * Cell content render function - */ - renderCellValue?: MergeProps< - EuiDataGridProps['renderCellValue'], - RenderContext & SelectedAlertWithLegacyFormats - >; - renderCellPopover?: MergeProps< - EuiDataGridProps['renderCellPopover'], - RenderContext & { alert: Alert } - >; - renderActionsCell?: MergeProps< - EuiDataGridControlColumn['rowCellRender'], - RenderContext & - SelectedAlertWithLegacyFormats & { setIsActionLoading?: (isLoading: boolean) => void } - >; - renderAdditionalToolbarControls?: ComponentRenderer; - renderFlyoutHeader?: FlyoutSectionRenderer; - renderFlyoutBody?: FlyoutSectionRenderer; - renderFlyoutFooter?: FlyoutSectionRenderer; - - lastReloadRequestTime?: number; -} - -/** - * A utility type to extract the type of a prop from `AlertsTableProps`, excluding `undefined`. - */ -export type GetAlertsTableProp = NonNullable< - AlertsTableProps[Key] ->; - -export interface AlertsTableImperativeApi { - refresh: () => void; - toggleColumn: (columnId: string) => void; -} - -export type AlertsTablePropsWithRef = AlertsTableProps & - RefAttributes; - -export type FlyoutSectionRenderer = - ComponentRenderer< - AC & { - alert: Alert; - flyoutIndex: number; - isLoading: boolean; - onClose: () => void; - onPaginate: (pageIndex: number) => void; - } - >; - -export interface BaseRenderContext - extends SetRequired< - Pick< - AlertsTableProps, - | 'columns' - | 'renderCellValue' - | 'renderCellPopover' - | 'renderActionsCell' - | 'renderFlyoutHeader' - | 'renderFlyoutBody' - | 'renderFlyoutFooter' - >, - 'columns' - > { - tableId?: string; - dataGridRef: MutableRefObject; - - /** - * Refetches all the queries, resetting the alerts pagination if necessary - */ - refresh: () => void; - - /** - * True if any of the active queries is fetching - */ - isLoading: boolean; - - isLoadingAlerts: boolean; - alerts: Alerts; - ecsAlertsData: any[]; - oldAlertsData: any[]; - alertsCount: number; - browserFields: BrowserFields; - - isLoadingMutedAlerts: boolean; - mutedAlerts?: MutedAlerts; - - isLoadingCases: boolean; - cases?: Map; - - isLoadingMaintenanceWindows: boolean; - maintenanceWindows?: Map; - - pageIndex: number; - pageSize: number; - - fieldFormats: FieldFormatsStart; - openAlertInFlyout: (alertId: string) => void; - - showAlertStatusWithFlapping?: boolean; - - bulkActionsStore: [BulkActionsState, Dispatch]; -} - -export type AdditionalContext = object; - -export type RenderContext = BaseRenderContext & AC; - -export type ComponentRenderer = ComponentType>; - -export interface CellActionsOptions { - /** - * Resolves the cell actions for a given column - */ - getCellActionsForColumn: (columnId: string, columnIndex: number) => EuiDataGridColumnCellAction[]; - visibleCellActions?: number; - disabledCellActions?: string[]; -} - -export interface PublicAlertsDataGridProps - extends Omit< - EuiDataGridProps, - | 'renderCellPopover' - | 'renderCellValue' - | 'aria-labelledby' - | 'columnVisibility' - | 'rowCount' - | 'sorting' - | 'cellContext' - | 'pagination' - | 'columns' - > { - ruleTypeIds: string[]; - consumers?: string[]; - showInspectButton?: boolean; - casesConfiguration?: { - featureId: string; - owner: string[]; - appId?: string; - syncAlerts?: boolean; - }; - hideBulkActions?: boolean; - getBulkActions?: ( - query: Pick, - refresh: () => void - ) => BulkActionsPanelConfig[]; - actionsColumnWidth?: number; - fieldsBrowserOptions?: FieldBrowserOptions; - cellActionsOptions?: CellActionsOptions; -} - -export interface AlertsDataGridProps - extends PublicAlertsDataGridProps { - renderContext: RenderContext; - additionalToolbarControls?: ReactNode; - pageSizeOptions?: number[]; - leadingControlColumns?: EuiDataGridControlColumn[]; - trailingControlColumns?: EuiDataGridControlColumn[]; - visibleColumns: string[]; - 'data-test-subj': string; - onToggleColumn: (columnId: string) => void; - onResetColumns: () => void; - onChangeVisibleColumns: (newColumns: string[]) => void; - onColumnResize?: EuiDataGridOnColumnResizeHandler; - query: Pick; - showInspectButton?: boolean; - toolbarVisibility?: EuiDataGridToolBarVisibilityOptions; - /** - * Allows to consumers of the table to decide to highlight a row based on the current alert. - */ - shouldHighlightRow?: (alert: Alert) => boolean; - /** - * Enable when rows may have variable heights (disables virtualization) - */ - dynamicRowHeight?: boolean; - sort: SortCombinations[]; - alertsQuerySnapshot?: EsQuerySnapshot; - onSortChange: (sort: EuiDataGridSorting['columns']) => void; - flyoutAlertIndex: number; - setFlyoutAlertIndex: Dispatch>; - onPaginateFlyout: (nextPageIndex: number) => void; - onChangePageSize: (size: number) => void; - onChangePageIndex: (index: number) => void; -} - -export interface TimelineNonEcsData { - field: string; - value?: string[] | null; -} - -export type AlertTableFlyoutComponent = - | React.FunctionComponent - | React.LazyExoticComponent> - | null; - -export interface AlertsTableFlyoutBaseProps { - alert: Alert; - isLoading: boolean; - id?: string; -} - -export interface BulkActionsConfig { - label: string; - key: string; - 'data-test-subj'?: string; - disableOnQuery: boolean; - disabledLabel?: string; - onClick?: ( - selectedIds: TimelineItem[], - isAllSelected: boolean, - setIsBulkActionsLoading: (isLoading: boolean) => void, - clearSelection: () => void, - refresh: () => void - ) => void; - panel?: number; -} - -interface PanelConfig { - id: number; - title?: JSX.Element | string; - 'data-test-subj'?: string; -} - -export interface RenderContentPanelProps { - alertItems: TimelineItem[]; - setIsBulkActionsLoading: (isLoading: boolean) => void; - isAllSelected?: boolean; - clearSelection?: () => void; - refresh?: () => void; - closePopoverMenu: () => void; -} - -interface ContentPanelConfig extends PanelConfig { - renderContent: (args: RenderContentPanelProps) => JSX.Element; - items?: never; -} - -interface ItemsPanelConfig extends PanelConfig { - content?: never; - items: BulkActionsConfig[]; -} - -export type BulkActionsPanelConfig = ItemsPanelConfig | ContentPanelConfig; - -export interface RenderCustomActionsRowArgs { - ecsAlert: FetchAlertData['ecsAlertsData'][number]; - nonEcsData: FetchAlertData['oldAlertsData'][number]; - rowIndex: number; - cveProps: EuiDataGridCellValueElementProps; - alert: Alert; - setFlyoutAlert: (alertId: string) => void; - id?: string; - setIsActionLoading?: (isLoading: boolean) => void; - refresh: () => void; - clearSelection: () => void; -} - -export interface AlertActionsProps extends RenderContext { - key?: Key; - alert: Alert; - onActionExecuted?: () => void; - isAlertDetailsEnabled?: boolean; - /** - * Implement this to resolve your app's specific rule page path, return null to avoid showing the link - */ - resolveRulePagePath?: (ruleId: string, currentPageId: string) => string | null; - /** - * Implement this to resolve your app's specific alert page path, return null to avoid showing the link - */ - resolveAlertPagePath?: (alertId: string, currentPageId: string) => string | null; -} - -export type UseActionsColumnRegistry = () => { - renderCustomActionsRow: (args: RenderCustomActionsRowArgs) => JSX.Element; - width?: number; -}; - -export enum BulkActionsVerbs { - add = 'add', - delete = 'delete', - clear = 'clear', - selectCurrentPage = 'selectCurrentPage', - selectAll = 'selectAll', - rowCountUpdate = 'rowCountUpdate', - updateRowLoadingState = 'updateRowLoadingState', - updateAllLoadingState = 'updateAllLoadingState', -} - -export interface BulkActionsReducerAction { - action: BulkActionsVerbs; - rowIndex?: number; - rowCount?: number; - isLoading?: boolean; -} - -export interface BulkActionsState { - rowSelection: Map; - isAllSelected: boolean; - areAllVisibleRowsSelected: boolean; - rowCount: number; - updatedAt: number; -} - -export type RowSelection = Map; - -export interface RowSelectionState { - isLoading: boolean; -} - export type RuleStatus = 'enabled' | 'disabled' | 'snoozed'; export enum RRuleFrequency { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/tsconfig.json b/x-pack/platform/plugins/shared/triggers_actions_ui/tsconfig.json index 308ef6cd153dc..019121a56c054 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/tsconfig.json +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/tsconfig.json @@ -46,9 +46,7 @@ "@kbn/shared-ux-router", "@kbn/alerts-ui-shared", "@kbn/safer-lodash-set", - "@kbn/cases-components", "@kbn/field-types", - "@kbn/alerts-as-data-utils", "@kbn/core-ui-settings-common", "@kbn/dashboard-plugin", "@kbn/licensing-plugin", @@ -69,13 +67,12 @@ "@kbn/alerting-types", "@kbn/visualization-utils", "@kbn/core-ui-settings-browser", - "@kbn/core-http-browser-mocks", - "@kbn/core-application-browser-mocks", "@kbn/alerting-rule-utils", "@kbn/core-application-browser", "@kbn/cloud-plugin", + "@kbn/response-ops-alerts-apis", + "@kbn/response-ops-alerts-table", "@kbn/response-ops-rule-form", - "@kbn/core-user-profile-browser-mocks", "@kbn/charts-theme", "@kbn/rrule" ], diff --git a/x-pack/solutions/observability/plugins/infra/public/components/asset_details/__stories__/decorator.tsx b/x-pack/solutions/observability/plugins/infra/public/components/asset_details/__stories__/decorator.tsx index 5cde2fc3ca1ae..957b81802731f 100644 --- a/x-pack/solutions/observability/plugins/infra/public/components/asset_details/__stories__/decorator.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/components/asset_details/__stories__/decorator.tsx @@ -99,7 +99,6 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { }, triggersActionsUi: { getAlertSummaryWidget: () => <>, - getAlertsStateTable: () => <>, }, charts: { theme: { diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions.test.tsx similarity index 74% rename from x-pack/solutions/observability/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx rename to x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions.test.tsx index a88075aeefe86..7e6a317357a67 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions.test.tsx @@ -7,13 +7,7 @@ import React, { ComponentProps } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; -import { kibanaStartMock } from '../../../utils/kibana_react.mock'; import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; -import { AlertActions } from './alert_actions'; -import { inventoryThresholdAlertEs } from '../../../rules/fixtures/example_alerts'; -import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants'; -import * as pluginContext from '../../../hooks/use_plugin_context'; -import { ConfigSchema, ObservabilityPublicPluginsStart } from '../../../plugin'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { allCasesPermissions, noCasesPermissions } from '@kbn/observability-shared-plugin/public'; @@ -21,10 +15,20 @@ import { noop } from 'lodash'; import { EuiDataGridCellValueElementProps } from '@elastic/eui/src/components/datagrid/data_grid_types'; import { waitFor, act } from '@testing-library/react'; import { Router } from '@kbn/shared-ux-router'; -import { createMemoryHistory } from 'history'; -import { ObservabilityRuleTypeRegistry } from '../../../rules/create_observability_rule_type_registry'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; -import type { GetObservabilityAlertsTableProp } from '../../../components/alerts_table/types'; +import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import { kibanaStartMock } from '../../utils/kibana_react.mock'; +import { AlertActions } from './alert_actions'; +import { inventoryThresholdAlertEs } from '../../rules/fixtures/example_alerts'; +import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants'; +import * as pluginContext from '../../hooks/use_plugin_context'; +import { ConfigSchema, ObservabilityPublicPluginsStart } from '../../plugin'; +import { createMemoryHistory } from 'history'; +import { ObservabilityRuleTypeRegistry } from '../../rules/create_observability_rule_type_registry'; +import type { GetObservabilityAlertsTableProp } from '../..'; +import { AlertsTableContextProvider } from '@kbn/response-ops-alerts-table/contexts/alerts_table_context'; +import { AdditionalContext, RenderContext } from '@kbn/response-ops-alerts-table/types'; const refresh = jest.fn(); const caseHooksReturnedValue = { @@ -34,34 +38,21 @@ const caseHooksReturnedValue = { close: jest.fn(), }; -const mockUseKibanaReturnValue = kibanaStartMock.startContract(); -mockUseKibanaReturnValue.services.cases.hooks.useCasesAddToNewCaseFlyout.mockReturnValue( - caseHooksReturnedValue -); +const mockKibana = kibanaStartMock.startContract(); +mockKibana.services.cases.hooks.useCasesAddToNewCaseFlyout.mockReturnValue(caseHooksReturnedValue); -mockUseKibanaReturnValue.services.cases.hooks.useCasesAddToExistingCaseModal.mockReturnValue( +mockKibana.services.cases.hooks.useCasesAddToExistingCaseModal.mockReturnValue( caseHooksReturnedValue ); -mockUseKibanaReturnValue.services.cases.helpers.canUseCases.mockReturnValue(allCasesPermissions()); +mockKibana.services.cases.helpers.canUseCases.mockReturnValue(allCasesPermissions()); +const mockLicensing = licensingMock.createStart(); const { ObservabilityAIAssistantContextualInsight } = observabilityAIAssistantPluginMock.createStartContract(); -jest.mock('../../../utils/kibana_react', () => ({ - __esModule: true, - useKibana: jest.fn(() => mockUseKibanaReturnValue), -})); const prependMock = jest.fn().mockImplementation((args) => args); -mockUseKibanaReturnValue.services.http.basePath.prepend = prependMock; -jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react', () => ({ - useKibana: jest.fn(() => ({ - services: { - notifications: { toasts: { addDanger: jest.fn(), addSuccess: jest.fn() } }, - http: mockUseKibanaReturnValue.services.http, - }, - })), -})); +mockKibana.services.http.basePath.prepend = prependMock; const config: ConfigSchema = { unsafe: { @@ -112,7 +103,20 @@ describe('ObservabilityActions component', () => { }, }); - const props = { + const props: Pick< + ComponentProps>, + | 'tableId' + | 'config' + | 'alert' + | 'ecsAlert' + | 'nonEcsData' + | 'rowIndex' + | 'cveProps' + | 'clearSelection' + | 'observabilityRuleTypeRegistry' + | 'openAlertInFlyout' + | 'refresh' + > = { tableId: pageId, config, alert: inventoryThresholdAlertEs, @@ -124,13 +128,32 @@ describe('ObservabilityActions component', () => { observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), openAlertInFlyout: jest.fn(), refresh, - } as unknown as ComponentProps>; + }; + + const context = { + services: { + http: mockKibana.services.http, + data: mockKibana.services.data, + notifications: mockKibana.services.notifications, + application: mockKibana.services.application, + settings: mockKibana.services.settings, + cases: mockKibana.services.cases, + licensing: mockLicensing, + fieldFormats: fieldFormatsMock, + }, + } as unknown as RenderContext; const wrapper = mountWithIntl( - - - + + + + >)} + /> + + ); await act(async () => { @@ -192,7 +215,7 @@ describe('ObservabilityActions component', () => { await setup('nothing'); // @ts-expect-error: The object will always be defined - mockUseKibanaReturnValue.services.cases.hooks.useCasesAddToNewCaseFlyout.mock.calls[0][0].onSuccess(); + mockKibana.services.cases.hooks.useCasesAddToNewCaseFlyout.mock.calls[0][0].onSuccess(); expect(refresh).toHaveBeenCalled(); }); @@ -214,15 +237,13 @@ describe('ObservabilityActions component', () => { await setup('nothing'); // @ts-expect-error: The object will always be defined - mockUseKibanaReturnValue.services.cases.hooks.useCasesAddToExistingCaseModal.mock.calls[0][0].onSuccess(); + mockKibana.services.cases.hooks.useCasesAddToExistingCaseModal.mock.calls[0][0].onSuccess(); expect(refresh).toHaveBeenCalled(); }); it('should hide the case actions without permissions', async () => { - mockUseKibanaReturnValue.services.cases.helpers.canUseCases.mockReturnValue( - noCasesPermissions() - ); + mockKibana.services.cases.helpers.canUseCases.mockReturnValue(noCasesPermissions()); const wrapper = await setup('nothing'); wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alerts/components/alert_actions.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions.tsx similarity index 60% rename from x-pack/solutions/observability/plugins/observability/public/pages/alerts/components/alert_actions.tsx rename to x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions.tsx index 3181a05c5bc71..9da3b598513ff 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/alerts/components/alert_actions.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions.tsx @@ -16,41 +16,69 @@ import { import React, { useMemo, useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; +import { CaseAttachmentsWithoutOwner, CasesPublicStart } from '@kbn/cases-plugin/public'; import { AttachmentType } from '@kbn/cases-plugin/common'; import { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { useRouteMatch } from 'react-router-dom'; import { SLO_ALERTS_TABLE_ID } from '@kbn/observability-shared-plugin/common'; -import type { EventNonEcsData } from '../../../../common/typings'; -import type { GetObservabilityAlertsTableProp } from '../../../components/alerts_table/types'; -import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants'; -import { paths, SLO_DETAIL_PATH } from '../../../../common/locators/paths'; -import { useKibana } from '../../../utils/kibana_react'; -import { parseAlert } from '../helpers/parse_alert'; -import { observabilityFeatureId } from '../../..'; -import { ALERT_DETAILS_PAGE_ID } from '../../alert_details/alert_details'; +import { DefaultAlertActions } from '@kbn/response-ops-alerts-table/components/default_alert_actions'; +import { useAlertsTableContext } from '@kbn/response-ops-alerts-table/contexts/alerts_table_context'; +import type { EventNonEcsData } from '../../../common/typings'; +import { GetObservabilityAlertsTableProp } from '../alerts_table/types'; +import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants'; +import { paths, SLO_DETAIL_PATH } from '../../../common/locators/paths'; +import { parseAlert } from '../../pages/alerts/helpers/parse_alert'; +import { observabilityFeatureId } from '../..'; +import { ALERT_DETAILS_PAGE_ID } from '../../pages/alert_details/alert_details'; // eslint-disable-next-line react/function-component-definition export const AlertActions: GetObservabilityAlertsTableProp<'renderActionsCell'> = ({ config, observabilityRuleTypeRegistry, - ...customActionsProps + alert, + id, + tableId, + dataGridRef, + refresh, + isLoading, + isLoadingAlerts, + alerts, + oldAlertsData, + ecsAlertsData, + alertsCount, + browserFields, + isLoadingMutedAlerts, + mutedAlerts, + isLoadingCases, + cases, + isLoadingMaintenanceWindows, + maintenanceWindows, + pageIndex, + pageSize, + openAlertInFlyout, + showAlertStatusWithFlapping, + bulkActionsStore, + columns, + renderCellValue, + renderCellPopover, + renderActionsCell, + renderFlyoutHeader, + renderFlyoutBody, + renderFlyoutFooter, }) => { - const { alert, refresh, id } = customActionsProps; - const isSLODetailsPage = useRouteMatch(SLO_DETAIL_PATH); - - const isInApp = Boolean(id === SLO_ALERTS_TABLE_ID && isSLODetailsPage); + const { services } = useAlertsTableContext(); const { - cases: { - helpers: { getRuleIdFromEvent, canUseCases }, - hooks: { useCasesAddToNewCaseFlyout, useCasesAddToExistingCaseModal }, - }, http: { basePath: { prepend }, }, - triggersActionsUi, - } = useKibana().services; + } = services; + const { + helpers: { getRuleIdFromEvent, canUseCases }, + hooks: { useCasesAddToNewCaseFlyout, useCasesAddToExistingCaseModal }, + } = services.cases! as unknown as CasesPublicStart; // Cases is guaranteed to be defined in Observability + const isSLODetailsPage = useRouteMatch(SLO_DETAIL_PATH); + const isInApp = Boolean(id === SLO_ALERTS_TABLE_ID && isSLODetailsPage); const data = useMemo( () => Object.entries(alert ?? {}).reduce( @@ -136,21 +164,86 @@ export const AlertActions: GetObservabilityAlertsTableProp<'renderActionsCell'> closeActionsPopover(); }; - const DefaultRowActions = useMemo( - () => - triggersActionsUi.getAlertsTableDefaultAlertActions({ - key: 'defaultRowActions', - onActionExecuted: closeActionsPopover, - isAlertDetailsEnabled: true, - resolveRulePagePath: (ruleId, currentPageId) => - currentPageId !== RULE_DETAILS_PAGE_ID ? paths.observability.ruleDetails(ruleId) : null, - resolveAlertPagePath: (alertId, currentPageId) => - currentPageId !== ALERT_DETAILS_PAGE_ID - ? paths.observability.alertDetails(alertId) - : null, - ...customActionsProps, - }), - [customActionsProps, triggersActionsUi] + const defaultRowActions = useMemo( + () => ( + + currentPageId !== RULE_DETAILS_PAGE_ID ? paths.observability.ruleDetails(ruleId) : null + } + resolveAlertPagePath={(alertId, currentPageId) => + currentPageId !== ALERT_DETAILS_PAGE_ID ? paths.observability.alertDetails(alertId) : null + } + tableId={tableId} + dataGridRef={dataGridRef} + refresh={refresh} + isLoading={isLoading} + isLoadingAlerts={isLoadingAlerts} + alert={alert} + alerts={alerts} + oldAlertsData={oldAlertsData} + ecsAlertsData={ecsAlertsData} + alertsCount={alertsCount} + browserFields={browserFields} + isLoadingMutedAlerts={isLoadingMutedAlerts} + mutedAlerts={mutedAlerts} + isLoadingCases={isLoadingCases} + cases={cases} + isLoadingMaintenanceWindows={isLoadingMaintenanceWindows} + maintenanceWindows={maintenanceWindows} + pageIndex={pageIndex} + pageSize={pageSize} + openAlertInFlyout={openAlertInFlyout} + showAlertStatusWithFlapping={showAlertStatusWithFlapping} + bulkActionsStore={bulkActionsStore} + columns={columns} + renderCellValue={renderCellValue} + renderCellPopover={renderCellPopover} + renderActionsCell={renderActionsCell} + renderFlyoutHeader={renderFlyoutHeader} + renderFlyoutBody={renderFlyoutBody} + renderFlyoutFooter={renderFlyoutFooter} + services={services} + config={config} + observabilityRuleTypeRegistry={observabilityRuleTypeRegistry} + /> + ), + [ + alert, + alerts, + alertsCount, + browserFields, + bulkActionsStore, + cases, + columns, + config, + dataGridRef, + ecsAlertsData, + isLoading, + isLoadingAlerts, + isLoadingCases, + isLoadingMaintenanceWindows, + isLoadingMutedAlerts, + maintenanceWindows, + mutedAlerts, + observabilityRuleTypeRegistry, + oldAlertsData, + openAlertInFlyout, + pageIndex, + pageSize, + refresh, + renderActionsCell, + renderCellPopover, + renderCellValue, + renderFlyoutBody, + renderFlyoutFooter, + renderFlyoutHeader, + services, + showAlertStatusWithFlapping, + tableId, + ] ); const actionsMenuItems = [ @@ -178,7 +271,7 @@ export const AlertActions: GetObservabilityAlertsTableProp<'renderActionsCell'> , ] : []), - DefaultRowActions, + defaultRowActions, ]; const actionsToolTip = @@ -248,3 +341,9 @@ export const AlertActions: GetObservabilityAlertsTableProp<'renderActionsCell'> ); }; + +// Default export used for lazy loading +// eslint-disable-next-line import/no-default-export +export default AlertActions; + +export type AlertActions = typeof AlertActions; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.ts b/x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions_lazy.tsx similarity index 60% rename from x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.ts rename to x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions_lazy.tsx index 781f7083d10f5..6cfe82eb3d515 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_table/index.ts +++ b/x-pack/solutions/observability/plugins/observability/public/components/alert_actions/alert_actions_lazy.tsx @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { lazy } from 'react'; -import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; +import type { AlertActions as AlertActionsType } from './alert_actions'; -export const AlertsTable = suspendedComponentWithProps(lazy(() => import('./alerts_data_grid'))); +export const AlertActions = lazy(() => import('./alert_actions')) as AlertActionsType; diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts b/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts index e9e8714bf85b9..d5002c9d713b3 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts +++ b/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts @@ -16,6 +16,7 @@ import { ALERT_EVALUATION_THRESHOLD, ApmRuleType, SLO_BURN_RATE_RULE_TYPE_ID, + METRIC_THRESHOLD_ALERT_TYPE_ID, } from '@kbn/rule-data-utils'; import { EsQueryRuleParams } from '@kbn/stack-alerts-plugin/public/rule_types/es_query/types'; import { i18n } from '@kbn/i18n'; @@ -31,7 +32,6 @@ import { asDuration, asPercent, convertToBuiltInComparators } from '../../../../ import { createFormatter } from '../../../../common/custom_threshold_rule/formatters'; import { metricValueFormatter } from '../../../../common/custom_threshold_rule/metric_value_formatter'; import { METRIC_FORMATTERS } from '../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats'; -import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../pages/alert_details/alert_details'; import { BaseMetricExpressionParams, CustomMetricExpressionParams, diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/overview_columns.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/overview_columns.tsx index d2c3eb58854ee..c58ae36f71392 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/overview_columns.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alert_overview/overview_columns.tsx @@ -6,7 +6,7 @@ */ import { EuiBasicTableColumn, EuiCallOut, EuiLink, EuiLoadingSpinner, EuiText } from '@elastic/eui'; -import { AlertLifecycleStatusBadge } from '@kbn/alerts-ui-shared'; +import { AlertLifecycleStatusBadge } from '@kbn/alerts-ui-shared/src/alert_lifecycle_status_badge'; import { Cases } from '@kbn/cases-plugin/common'; import { i18n } from '@kbn/i18n'; import { AlertStatus } from '@kbn/rule-data-utils'; diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.mock.ts b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.mock.ts index 5c702222ee4ae..a80da214e2db4 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.mock.ts +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.mock.ts @@ -24,7 +24,7 @@ import { SPACE_IDS, TIMESTAMP, } from '@kbn/rule-data-utils'; -import { Alerts } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { Alert } from '@kbn/alerting-types'; const createDates = (start: string, duration: number, isEnd?: boolean) => { const started = new Date(start); @@ -80,7 +80,7 @@ export const apmAlertResponseExample = [ 'processor.event': ['error'], ...createDates('2021-04-12T13:09:30.441Z', 2419005000, true), }, -] as unknown as Alerts; +] as unknown as Alert[]; export const dynamicIndexPattern = { fields: [ diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.stories.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.stories.tsx index 253c871877025..7ff85d36602ee 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.stories.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.stories.tsx @@ -8,14 +8,14 @@ import React, { ComponentType } from 'react'; import { ALERT_UUID } from '@kbn/rule-data-utils'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { Alerts } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { Alert } from '@kbn/alerting-types'; import { PluginContext, PluginContextValue } from '../../context/plugin_context/plugin_context'; import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock'; import { apmAlertResponseExample } from './alerts_flyout.mock'; import { AlertsFlyout } from './alerts_flyout'; interface Args { - alerts: Alerts; + alerts: Alert[]; } export default { @@ -49,7 +49,7 @@ export default { }; export function Example({ alerts }: Args) { - const selectedAlertId = apmAlertResponseExample[0]![ALERT_UUID]![0]; + const selectedAlertId = apmAlertResponseExample[0]![ALERT_UUID]![0] as string; const observabilityRuleTypeRegistry = createObservabilityRuleTypeRegistryMock(); return ( ({ diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.tsx index b9cc97c50790e..de2ba0480e675 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutProps } from '@elastic/eui'; import { ALERT_UUID } from '@kbn/rule-data-utils'; -import type { Alert, Alerts } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { Alert } from '@kbn/alerting-types'; import { AlertsFlyoutHeader } from './alerts_flyout_header'; import { AlertsFlyoutBody } from './alerts_flyout_body'; import { AlertsFlyoutFooter } from './alerts_flyout_footer'; @@ -17,7 +17,7 @@ import type { ObservabilityRuleTypeRegistry } from '../../rules/create_observabi type AlertsFlyoutProps = { alert?: Alert; - alerts?: Alerts; + alerts?: Alert[]; selectedAlertId?: string; observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; } & EuiFlyoutProps; @@ -45,12 +45,10 @@ export function AlertsFlyout({ diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_body.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_body.tsx index d07f0072bcc3c..e7a85f2b5184b 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_body.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_body.tsx @@ -7,7 +7,10 @@ import React, { ComponentProps, useCallback, useMemo, useState } from 'react'; import { EuiPanel, EuiTabbedContentTab } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AlertFieldsTable, ScrollableFlyoutTabbedContent } from '@kbn/alerts-ui-shared'; +import { + AlertFieldsTable, + ScrollableFlyoutTabbedContent, +} from '@kbn/alerts-ui-shared/src/alert_fields_table'; import { parseAlert } from '../../pages/alerts/helpers/parse_alert'; import { GetObservabilityAlertsTableProp } from '../alerts_table/types'; import { AlertOverview } from '../alert_overview/alert_overview'; diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_header.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_header.tsx index a66fd634d0788..d0107c7f5d0cd 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_header.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_flyout/alerts_flyout_header.tsx @@ -19,7 +19,7 @@ export function AlertsFlyoutHeader({ alert }: AlertsFlyoutHeaderProps) { <> -

{alert[ALERT_RULE_NAME]}

+

{alert[ALERT_RULE_NAME]?.[0] as string}

); diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table.tsx index a1b07c6ac3880..7df6d8358169d 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table.tsx @@ -6,11 +6,18 @@ */ import React from 'react'; -import { ALERT_DURATION } from '@kbn/rule-data-utils'; -import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ALERT_START } from '@kbn/rule-data-utils'; +import { SortOrder } from '@elastic/elasticsearch/lib/api/types'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import { ObservabilityPublicStart } from '../..'; +import AlertActions from '../alert_actions/alert_actions'; import { useKibana } from '../../utils/kibana_react'; import { casesFeatureId, observabilityFeatureId } from '../../../common'; -import { ObservabilityAlertsTableContext, ObservabilityAlertsTableProps } from './types'; +import { + GetObservabilityAlertsTableProp, + ObservabilityAlertsTableContext, + ObservabilityAlertsTableProps, +} from './types'; import { AlertsTableCellValue } from './common/cell_value'; import { AlertsFlyoutBody } from '../alerts_flyout/alerts_flyout_body'; import { AlertsFlyoutHeader } from '../alerts_flyout/alerts_flyout_header'; @@ -19,19 +26,32 @@ import { usePluginContext } from '../../hooks/use_plugin_context'; import { getColumns } from './common/get_columns'; import { OBSERVABILITY_RULE_TYPE_IDS_WITH_SUPPORTED_STACK_RULE_TYPES } from '../../../common/constants'; -const columns = getColumns(); +const columns = getColumns({ showRuleName: true }); const initialSort = [ { - [ALERT_DURATION]: { + [ALERT_START]: { order: 'desc' as SortOrder, }, }, ]; -export function ObservabilityAlertsTable(props: ObservabilityAlertsTableProps) { +const caseConfiguration: GetObservabilityAlertsTableProp<'casesConfiguration'> = { + featureId: casesFeatureId, + owner: [observabilityFeatureId], +}; + +export function ObservabilityAlertsTable(props: Omit) { const { - triggersActionsUi: { getAlertsStateTable: AlertsTable }, - } = useKibana().services; + data, + http, + notifications, + fieldFormats, + application, + licensing, + cases, + settings, + observability, + } = useKibana<{ observability?: ObservabilityPublicStart }>().services; const { observabilityRuleTypeRegistry, config } = usePluginContext(); return ( @@ -39,14 +59,34 @@ export function ObservabilityAlertsTable(props: ObservabilityAlertsTableProps) { columns={columns} ruleTypeIds={OBSERVABILITY_RULE_TYPE_IDS_WITH_SUPPORTED_STACK_RULE_TYPES} initialSort={initialSort} - casesConfiguration={{ featureId: casesFeatureId, owner: [observabilityFeatureId] }} - additionalContext={{ observabilityRuleTypeRegistry, config }} + casesConfiguration={caseConfiguration} + additionalContext={{ + observabilityRuleTypeRegistry: + observabilityRuleTypeRegistry ?? observability?.observabilityRuleTypeRegistry, + config, + }} renderCellValue={AlertsTableCellValue} + renderActionsCell={AlertActions} renderFlyoutHeader={AlertsFlyoutHeader} renderFlyoutBody={AlertsFlyoutBody} renderFlyoutFooter={AlertsFlyoutFooter} showAlertStatusWithFlapping + services={{ + data, + http, + notifications, + fieldFormats, + application, + licensing, + cases, + settings, + }} {...props} /> ); } + +// Lazy loading helpers +// eslint-disable-next-line import/no-default-export +export default ObservabilityAlertsTable; +export type ObservabilityAlertsTable = typeof ObservabilityAlertsTable; diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table_lazy.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table_lazy.tsx new file mode 100644 index 0000000000000..3a94111bba765 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/alerts_table_lazy.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { ComponentProps, lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import type { ObservabilityAlertsTable as ObservabilityAlertsTableType } from './alerts_table'; + +export const AlertsTable = lazy(() => import('./alerts_table')) as ObservabilityAlertsTableType; + +export function ObservabilityAlertsTable( + props: ComponentProps & { hideLazyLoader?: boolean } +) { + const { hideLazyLoader, ...tableProps } = props; + return ( + }> + + + ); +} diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.test.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.test.tsx index 2078d85963688..a16796bdb0eef 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.test.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.test.tsx @@ -9,13 +9,13 @@ import React, { ComponentProps } from 'react'; import { ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils'; import { render } from '../../../utils/test_helper'; import { AlertsTableCellValue } from './cell_value'; -import { Alert } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { Alert } from '@kbn/alerting-types'; interface AlertsTableRow { alertStatus: typeof ALERT_STATUS_ACTIVE | typeof ALERT_STATUS_RECOVERED; } -describe('getRenderCellValue', () => { +describe('AlertsTableCellValue', () => { describe('when column is alert status', () => { it('should return an active indicator when alert status is active', async () => { const cell = render( diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.tsx b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.tsx index 659bfab81e811..309350e5d5560 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/common/cell_value.tsx @@ -23,14 +23,14 @@ import { ALERT_RULE_EXECUTION_TIMESTAMP, } from '@kbn/rule-data-utils'; import { isEmpty } from 'lodash'; -import { Alert } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { Alert } from '@kbn/alerting-types'; import { asDuration } from '../../../../common/utils/formatters'; import { AlertSeverityBadge } from '../../alert_severity_badge'; import { AlertStatusIndicator } from '../../alert_status_indicator'; import { parseAlert } from '../../../pages/alerts/helpers/parse_alert'; import { CellTooltip } from './cell_tooltip'; import { TimestampTooltip } from './timestamp_tooltip'; -import { GetObservabilityAlertsTableProp } from '../types'; +import type { GetObservabilityAlertsTableProp } from '../types'; const getAlertFieldValue = (alert: Alert, fieldName: string) => { // can be updated when working on https://github.com/elastic/kibana/issues/140819 diff --git a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/types.ts b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/types.ts index 61d0ae3fd1a00..382dacb163af5 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/types.ts +++ b/x-pack/solutions/observability/plugins/observability/public/components/alerts_table/types.ts @@ -5,11 +5,8 @@ * 2.0. */ -import { - AlertsTablePropsWithRef, - LazyLoadProps, -} from '@kbn/triggers-actions-ui-plugin/public/types'; import { SetOptional } from 'type-fest'; +import type { AlertsTablePropsWithRef } from '@kbn/response-ops-alerts-table/types'; import type { ConfigSchema, ObservabilityRuleTypeRegistry } from '../..'; export interface ObservabilityAlertsTableContext { @@ -20,8 +17,7 @@ export interface ObservabilityAlertsTableContext { export type ObservabilityAlertsTableProps = SetOptional< AlertsTablePropsWithRef, 'ruleTypeIds' -> & - LazyLoadProps; +>; export type GetObservabilityAlertsTableProp = NonNullable; diff --git a/x-pack/solutions/observability/plugins/observability/public/index.ts b/x-pack/solutions/observability/plugins/observability/public/index.ts index fe9bcdae83606..c5e6af014c036 100644 --- a/x-pack/solutions/observability/plugins/observability/public/index.ts +++ b/x-pack/solutions/observability/plugins/observability/public/index.ts @@ -130,10 +130,10 @@ export { getGroupFilters } from '../common/custom_threshold_rule/helpers/get_gro export type { GenericAggType } from './components/rule_condition_chart/rule_condition_chart'; export { Threshold } from './components/custom_threshold/components/threshold'; -export { ObservabilityAlertsTable } from './components/alerts_table/alerts_table'; +export { ObservabilityAlertsTable } from './components/alerts_table/alerts_table_lazy'; +export { AlertActions } from './components/alert_actions/alert_actions_lazy'; export type { GetObservabilityAlertsTableProp, ObservabilityAlertsTableContext, ObservabilityAlertsTableProps, } from './components/alerts_table/types'; -export { AlertActions } from './pages/alerts/components/alert_actions'; diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx index 6b9a5cb3480a9..3fc7997ce3e0a 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx @@ -31,7 +31,7 @@ import { import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public'; import dedent from 'dedent'; -import { AlertFieldsTable } from '@kbn/alerts-ui-shared'; +import { AlertFieldsTable } from '@kbn/alerts-ui-shared/src/alert_fields_table'; import { css } from '@emotion/react'; import { omit } from 'lodash'; import { BetaBadge } from '../../components/experimental_badge'; diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/related_alerts.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/related_alerts.tsx index e5da25f2484f1..50883729be74d 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/related_alerts.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/related_alerts.tsx @@ -29,7 +29,6 @@ import { BoolQuery, Filter, type Query } from '@kbn/es-query'; import { AlertsGrouping } from '@kbn/alerts-grouping'; import { ObservabilityFields } from '../../../../common/utils/alerting/types'; -import { AlertActions } from '../../alerts/components/alert_actions'; import { OBSERVABILITY_RULE_TYPE_IDS_WITH_SUPPORTED_STACK_RULE_TYPES, observabilityAlertFeatureIds, @@ -151,7 +150,6 @@ export function InternalRelatedAlerts({ alert }: Props) { consumers={observabilityAlertFeatureIds} query={mergeBoolQueries(esQuery, groupQuery)} initialPageSize={ALERTS_PER_PAGE} - renderActionsCell={AlertActions} showInspectButton /> ); diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/status_bar.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/status_bar.tsx index eee2db8e98d52..32e1efd5094c4 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/status_bar.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/status_bar.tsx @@ -8,7 +8,7 @@ import React from 'react'; import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, useEuiTheme, EuiToolTip } from '@elastic/eui'; -import { AlertLifecycleStatusBadge } from '@kbn/alerts-ui-shared'; +import { AlertLifecycleStatusBadge } from '@kbn/alerts-ui-shared/src/alert_lifecycle_status_badge'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alerts/alerts.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alerts/alerts.tsx index cd36836fd0ef5..d2eae85660a32 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/alerts/alerts.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/alerts/alerts.tsx @@ -13,7 +13,7 @@ import { usePerformanceContext } from '@kbn/ebt-tools'; import { i18n } from '@kbn/i18n'; import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public'; import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public'; -import { MaintenanceWindowCallout } from '@kbn/alerts-ui-shared'; +import { MaintenanceWindowCallout } from '@kbn/alerts-ui-shared/src/maintenance_window_callout'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common'; import { AlertsGrouping } from '@kbn/alerts-grouping'; @@ -50,7 +50,6 @@ import { ALERTS_PAGE_ALERTS_TABLE_CONFIG_ID } from '../../constants'; import { useGetAvailableRulesWithDescriptions } from '../../hooks/use_get_available_rules_with_descriptions'; import { ObservabilityAlertsTable } from '../../components/alerts_table/alerts_table'; import { getColumns } from '../../components/alerts_table/common/get_columns'; -import { AlertActions } from './components/alert_actions'; import { HeaderMenu } from '../overview/components/header_menu/header_menu'; import { buildEsQuery } from '../../utils/build_es_query'; import { renderRuleStats, RuleStatsState } from './components/rule_stats'; @@ -320,7 +319,6 @@ function InternalAlertsPage() { initialPageSize={ALERTS_PER_PAGE} onUpdate={onUpdate} columns={tableColumns} - renderActionsCell={AlertActions} showInspectButton /> ); diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/overview/overview.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/overview/overview.tsx index d22bbb0128a71..e5f161c5820f9 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/overview/overview.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/overview/overview.tsx @@ -42,7 +42,6 @@ import { HasDataMap, appLabels, } from '../../context/has_data_context/has_data_context'; -import { AlertActions } from '../alerts/components/alert_actions'; const ALERTS_PER_PAGE = 10; const ALERTS_TABLE_ID = 'xpack.observability.overview.alert.table'; @@ -248,11 +247,9 @@ export function OverviewPage() { id={ALERTS_TABLE_ID} ruleTypeIds={OBSERVABILITY_RULE_TYPE_IDS_WITH_SUPPORTED_STACK_RULE_TYPES} consumers={observabilityAlertFeatureIds} - hideLazyLoader query={esQuery} initialPageSize={ALERTS_PER_PAGE} columns={tableColumns} - renderActionsCell={AlertActions} showInspectButton /> diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/rule_details_tabs.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/rule_details_tabs.tsx index 10cffafa4cfd2..b904a2c24d32d 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/rule_details_tabs.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/rule_details/components/rule_details_tabs.tsx @@ -30,7 +30,6 @@ import { } from '../constants'; import type { TabId } from '../rule_details'; import { getColumns } from '../../../components/alerts_table/common/get_columns'; -import { AlertActions } from '../../alerts/components/alert_actions'; interface Props { activeTabId: TabId; @@ -95,7 +94,6 @@ export function RuleDetailsTabs({ consumers={observabilityAlertFeatureIds} query={esQuery} columns={tableColumns} - renderActionsCell={AlertActions} /> )} diff --git a/x-pack/solutions/observability/plugins/observability/public/plugin.mock.tsx b/x-pack/solutions/observability/plugins/observability/public/plugin.mock.tsx index 378bdf888df84..41b3dcad1dafb 100644 --- a/x-pack/solutions/observability/plugins/observability/public/plugin.mock.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/plugin.mock.tsx @@ -13,23 +13,7 @@ import { contentManagementMock } from '@kbn/content-management-plugin/public/moc import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; -import type { AlertActionsProps } from '@kbn/triggers-actions-ui-plugin/public/types'; -import { getAlertsTableDefaultAlertActionsLazy } from '@kbn/triggers-actions-ui-plugin/public/common/get_alerts_table_default_row_actions'; import { lensPluginMock } from '@kbn/lens-plugin/public/mocks'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - }, - }, - logger: { - log: () => {}, - warn: () => {}, - error: () => {}, - }, -}); const triggersActionsUiStartMock = { createStart() { @@ -43,13 +27,6 @@ const triggersActionsUiStartMock = { getAlertsStateTable: jest.fn(() => (
mocked component
)), - getAlertsTableDefaultAlertActions: (props: AlertActionsProps) => { - return ( - - {getAlertsTableDefaultAlertActionsLazy(props)} - - ); - }, getAddRuleFlyout: jest.fn(() =>
mocked component
), getEditRuleFlyout: jest.fn(() => (
mocked component
diff --git a/x-pack/solutions/observability/plugins/observability/tsconfig.json b/x-pack/solutions/observability/plugins/observability/tsconfig.json index cf77383bc426f..fa7b218b451da 100644 --- a/x-pack/solutions/observability/plugins/observability/tsconfig.json +++ b/x-pack/solutions/observability/plugins/observability/tsconfig.json @@ -110,8 +110,10 @@ "@kbn/core-ui-settings-server-mocks", "@kbn/es-types", "@kbn/logging-mocks", - "@kbn/response-ops-rule-form", + "@kbn/alerting-types", + "@kbn/response-ops-alerts-table", "@kbn/streams-plugin", + "@kbn/response-ops-rule-form", "@kbn/data-service", "@kbn/ebt-tools", "@kbn/response-ops-rule-params" diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx index a04ad68978ad9..113c0182aa8d7 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx @@ -18,7 +18,7 @@ import { ALL_VALUE } from '@kbn/slo-schema'; import type { AlertsTableProps, AlertsTableImperativeApi, -} from '@kbn/triggers-actions-ui-plugin/public/types'; +} from '@kbn/response-ops-alerts-table/types'; import { ObservabilityAlertsTable } from '@kbn/observability-plugin/public'; import { EuiDataGridColumn } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/solutions/observability/plugins/slo/tsconfig.json b/x-pack/solutions/observability/plugins/slo/tsconfig.json index 8f6066dcad9c2..380bf22c874a4 100644 --- a/x-pack/solutions/observability/plugins/slo/tsconfig.json +++ b/x-pack/solutions/observability/plugins/slo/tsconfig.json @@ -100,6 +100,7 @@ "@kbn/alerting-rule-utils", "@kbn/discover-shared-plugin", "@kbn/server-route-repository-client", + "@kbn/response-ops-alerts-table", "@kbn/security-plugin-types-public", "@kbn/response-ops-rule-params", "@kbn/core-test-helpers-kbn-server", diff --git a/x-pack/solutions/security/packages/data_table/common/types/header_actions/index.ts b/x-pack/solutions/security/packages/data_table/common/types/header_actions/index.ts index b7d819c8c8ddc..73f9de5eec15b 100644 --- a/x-pack/solutions/security/packages/data_table/common/types/header_actions/index.ts +++ b/x-pack/solutions/security/packages/data_table/common/types/header_actions/index.ts @@ -7,11 +7,11 @@ import type { EuiDataGridColumn, EuiDataGridProps } from '@elastic/eui'; import type { IFieldSubType } from '@kbn/es-query'; -import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public'; import type { ComponentType } from 'react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { TimelineNonEcsData, BrowserFields } from '@kbn/timelines-plugin/common'; import type { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; +import type { FieldBrowserOptions } from '@kbn/response-ops-alerts-fields-browser'; import { OnRowSelected } from '../../../components/data_table/types'; import type { SortColumnTable } from '../data_table'; import { SetEventsDeleted, SetEventsLoading } from '..'; diff --git a/x-pack/solutions/security/packages/data_table/components/data_table/data_table.stories.tsx b/x-pack/solutions/security/packages/data_table/components/data_table/data_table.stories.tsx index 0fe5da56ed3c2..cbc2e79789473 100644 --- a/x-pack/solutions/security/packages/data_table/components/data_table/data_table.stories.tsx +++ b/x-pack/solutions/security/packages/data_table/components/data_table/data_table.stories.tsx @@ -99,7 +99,7 @@ export const DataTable = () => { loadPage={() => {}} rowRenderers={[]} totalItems={mockTimelineData.length} - getFieldBrowser={() => } + fieldsBrowserComponent={() => } /> ); diff --git a/x-pack/solutions/security/packages/data_table/components/data_table/index.test.tsx b/x-pack/solutions/security/packages/data_table/components/data_table/index.test.tsx index 7a0a3e8f76caa..0cdfcd891a71a 100644 --- a/x-pack/solutions/security/packages/data_table/components/data_table/index.test.tsx +++ b/x-pack/solutions/security/packages/data_table/components/data_table/index.test.tsx @@ -78,7 +78,7 @@ describe('DataTable', () => { onChangeItemsPerPage: jest.fn(), onChangePage: jest.fn(), }, - getFieldBrowser: jest.fn(), + fieldsBrowserComponent: jest.fn().mockReturnValue(
), }; beforeEach(() => { diff --git a/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx b/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx index 4c3bdd0dd5334..f3db21829ce35 100644 --- a/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx +++ b/x-pack/solutions/security/packages/data_table/components/data_table/index.tsx @@ -12,21 +12,18 @@ import type { EuiDataGridPaginationProps, EuiDataGridProps, EuiDataGridRefProps, + EuiDataGridRowHeightsOptions, EuiDataGridStyle, EuiDataGridToolBarVisibilityOptions, } from '@elastic/eui'; import { EuiDataGrid, EuiProgress } from '@elastic/eui'; import { getOr } from 'lodash/fp'; import memoizeOne from 'memoize-one'; -import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useRef, ComponentType } from 'react'; import { useDispatch } from 'react-redux'; import styled, { ThemeContext } from 'styled-components'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; -import type { - FieldBrowserOptions, - FieldBrowserProps, -} from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { BrowserFields, @@ -40,6 +37,7 @@ import { UseDataGridColumnsCellActionsProps, } from '@kbn/cell-actions'; import { FieldSpec } from '@kbn/data-views-plugin/common'; +import { FieldBrowser, type FieldBrowserOptions } from '@kbn/response-ops-alerts-fields-browser'; import { DataTableModel, DataTableState } from '../../store/data_table/types'; import { getColumnHeader, getColumnHeaders } from './column_headers/helpers'; @@ -57,8 +55,6 @@ const DATA_TABLE_ARIA_LABEL = i18n.translate('securitySolutionPackages.dataTable defaultMessage: 'Alerts', }); -type GetFieldBrowser = (props: FieldBrowserProps) => void; - type NonCustomizableGridProps = | 'id' | 'data-test-subj' @@ -90,7 +86,9 @@ interface BaseDataTableProps { unitCountText: string; pagination: EuiDataGridPaginationProps & { pageSize: number }; totalItems: number; - getFieldBrowser: GetFieldBrowser; + rowHeightsOptions?: EuiDataGridRowHeightsOptions; + isEventRenderedView?: boolean; + fieldsBrowserComponent?: ComponentType; getFieldSpec: (fieldName: string) => FieldSpec | undefined; cellActionsTriggerId?: string; } @@ -154,7 +152,9 @@ export const DataTableComponent = React.memo( pagination, unitCountText, totalItems, - getFieldBrowser, + rowHeightsOptions, + isEventRenderedView = false, + fieldsBrowserComponent = FieldBrowser, getFieldSpec, cellActionsTriggerId, ...otherProps @@ -172,6 +172,7 @@ export const DataTableComponent = React.memo( defaultColumns, dataViewId, } = dataTable; + const FieldsBrowserComponent = fieldsBrowserComponent; const columnHeaders = memoizedGetColumnHeaders(columns, browserFields); @@ -228,13 +229,13 @@ export const DataTableComponent = React.memo( {isLoading && } {unitCountText} {additionalControls ?? null} - {getFieldBrowser({ - browserFields, - options: fieldBrowserOptions, - columnIds: columnHeaders.map(({ id: columnId }) => columnId), - onResetColumns, - onToggleColumn, - })} + columnId)} + onResetColumns={onResetColumns} + onToggleColumn={onToggleColumn} + /> ), }, @@ -256,7 +257,7 @@ export const DataTableComponent = React.memo( isLoading, unitCountText, additionalControls, - getFieldBrowser, + FieldsBrowserComponent, browserFields, fieldBrowserOptions, columnHeaders, diff --git a/x-pack/solutions/security/packages/data_table/kibana.jsonc b/x-pack/solutions/security/packages/data_table/kibana.jsonc index 027d2a426bf23..7be69d514b163 100644 --- a/x-pack/solutions/security/packages/data_table/kibana.jsonc +++ b/x-pack/solutions/security/packages/data_table/kibana.jsonc @@ -1,9 +1,9 @@ { - "type": "shared-common", + "type": "shared-browser", "id": "@kbn/securitysolution-data-table", "owner": [ "@elastic/security-threat-hunting-investigations" ], "group": "security", "visibility": "private" -} \ No newline at end of file +} diff --git a/x-pack/solutions/security/packages/data_table/tsconfig.json b/x-pack/solutions/security/packages/data_table/tsconfig.json index d44a419c3ba4e..094a4f17716e3 100644 --- a/x-pack/solutions/security/packages/data_table/tsconfig.json +++ b/x-pack/solutions/security/packages/data_table/tsconfig.json @@ -10,7 +10,6 @@ "@kbn/cell-actions", "@kbn/timelines-plugin", "@kbn/es-query", - "@kbn/triggers-actions-ui-plugin", "@kbn/securitysolution-ecs", "@kbn/safer-lodash-set", "@kbn/i18n", @@ -21,6 +20,7 @@ "@kbn/ui-actions-plugin", "@kbn/data-views-plugin", "@kbn/field-formats-plugin", - "@kbn/data-plugin" + "@kbn/data-plugin", + "@kbn/response-ops-alerts-fields-browser" ] } diff --git a/x-pack/solutions/security/plugins/security_solution/common/types/header_actions/index.ts b/x-pack/solutions/security/plugins/security_solution/common/types/header_actions/index.ts index 14852edd8c864..7935111f6233b 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/types/header_actions/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/types/header_actions/index.ts @@ -11,7 +11,7 @@ import type { EuiDataGridControlColumn, } from '@elastic/eui'; import type { IFieldSubType } from '@kbn/es-query'; -import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public'; +import type { FieldBrowserOptions } from '@kbn/response-ops-alerts-fields-browser'; import type { ComponentType, JSXElementConstructor } from 'react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import type { SortColumnTable } from '@kbn/securitysolution-data-table'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx index 29494407e0c04..b3ed53b023098 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx @@ -5,12 +5,12 @@ * 2.0. */ +import React, { useMemo } from 'react'; import type { AttackDiscovery, Replacements } from '@kbn/elastic-assistant-common'; import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; -import React, { useMemo } from 'react'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { TableId } from '@kbn/securitysolution-data-table'; +import { AlertConsumers } from '@kbn/rule-data-utils'; import { AlertsTableComponent } from '../../../../../../detections/components/alerts_table'; interface Props { diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/control_columns/transform_control_columns.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/control_columns/transform_control_columns.tsx index a2e057f2136d1..33ffd5f178f29 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/control_columns/transform_control_columns.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/control_columns/transform_control_columns.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public'; +import type { FieldBrowserOptions } from '@kbn/response-ops-alerts-fields-browser'; import type { EuiDataGridCellValueElementProps, EuiDataGridControlColumn } from '@elastic/eui'; import type { ComponentType } from 'react'; import React from 'react'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx index ac400984db6b9..ba782a222bf65 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -138,11 +138,7 @@ const StatefulEventsViewerComponent: React.FC eventsViewerSelector(state, tableId)); const inspectModalTitle = useMemo(() => {title}, [title]); - const { - uiSettings, - data, - triggersActionsUi: { getFieldBrowser }, - } = useKibana().services; + const { uiSettings, data } = useKibana().services; const { browserFields, @@ -555,7 +551,6 @@ const StatefulEventsViewerComponent: React.FC diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.test.tsx index ca4e3175a08dc..fa8e8a39f6625 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.test.tsx @@ -6,8 +6,7 @@ */ import { ALERT_WORKFLOW_ASSIGNEE_IDS } from '@kbn/rule-data-utils'; -import type { BulkActionsConfig } from '@kbn/triggers-actions-ui-plugin/public/types'; -import type { TimelineItem } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/bulk_actions/components/toolbar'; +import type { BulkActionsConfig } from '@kbn/response-ops-alerts-table/types'; import { act, fireEvent, render, renderHook } from '@testing-library/react'; import { TestProviders } from '../../../mock'; @@ -23,6 +22,7 @@ import { useSuggestUsers } from '../../user_profiles/use_suggest_users'; import { ASSIGNEES_APPLY_BUTTON_TEST_ID } from '../../assignees/test_ids'; import { useAlertsPrivileges } from '../../../../detections/containers/detection_engine/alerts/use_alerts_privileges'; import { useLicense } from '../../../hooks/use_license'; +import type { TimelineItem } from '@kbn/timelines-plugin/common'; jest.mock('./use_set_alert_assignees'); jest.mock('../../user_profiles/use_get_current_user_profile'); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.tsx index a91fc5ad8aede..0bef8950aeb16 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_assignees_items.tsx @@ -5,15 +5,15 @@ * 2.0. */ -import { union } from 'lodash'; import React, { useCallback, useMemo } from 'react'; +import { union } from 'lodash'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { ALERT_WORKFLOW_ASSIGNEE_IDS } from '@kbn/rule-data-utils'; import type { BulkActionsConfig, RenderContentPanelProps, -} from '@kbn/triggers-actions-ui-plugin/public/types'; +} from '@kbn/response-ops-alerts-table/types'; import { isEmpty } from 'lodash/fp'; import { useLicense } from '../../../hooks/use_license'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_tags_items.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_tags_items.tsx index d83d2c7dad0dd..714d049524f51 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_tags_items.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_alert_tags_items.tsx @@ -6,8 +6,8 @@ */ import { EuiFlexGroup, EuiIconTip, EuiFlexItem } from '@elastic/eui'; -import type { RenderContentPanelProps } from '@kbn/triggers-actions-ui-plugin/public/types'; import React, { useCallback, useMemo } from 'react'; +import type { RenderContentPanelProps } from '@kbn/response-ops-alerts-table/types'; import { useAlertsPrivileges } from '../../../../detections/containers/detection_engine/alerts/use_alerts_privileges'; import type { BulkAlertTagsPanelComponentProps } from './alert_bulk_tags'; import { BulkAlertTagsPanel } from './alert_bulk_tags'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/containers/source/use_data_view.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/containers/source/use_data_view.tsx index f363a4c079238..1dd1bf2877438 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/containers/source/use_data_view.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/containers/source/use_data_view.tsx @@ -10,10 +10,10 @@ import type { Subscription } from 'rxjs'; import { useDispatch } from 'react-redux'; import memoizeOne from 'memoize-one'; import type { BrowserFields } from '@kbn/timelines-plugin/common'; -import { getCategory } from '@kbn/triggers-actions-ui-plugin/public'; import type { DataViewSpec } from '@kbn/data-views-plugin/public'; import type { FieldCategory } from '@kbn/timelines-plugin/common/search_strategy'; +import { getCategory } from '@kbn/response-ops-alerts-fields-browser/helpers'; import { useKibana } from '../../lib/kibana'; import { sourcererActions } from '../../../sourcerer/store'; import { SourcererScopeName } from '../../../sourcerer/store/model'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_triggers_actions_ui_plugin.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_triggers_actions_ui_plugin.tsx deleted file mode 100644 index db13e93115c88..0000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_triggers_actions_ui_plugin.tsx +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; - -export const mockTriggersActionsUi = { - getFieldBrowser: jest.fn().mockReturnValue(
), -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx index 6d2337a78296f..83b7c13e9de2a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx @@ -14,13 +14,13 @@ import { EuiLoadingSpinner, useEuiTheme, } from '@elastic/eui'; +import { TOTAL_COUNT_OF_ALERTS } from '../../alerts_table/translations'; import type { SeverityBuckets as SeverityData } from '../../../../overview/components/detection_response/alerts_by_status/types'; import type { FillColor } from '../../../../common/components/charts/donutchart'; import { DonutChart } from '../../../../common/components/charts/donutchart'; import { ChartLabel } from '../../../../overview/components/detection_response/alerts_by_status/chart_label'; import { useGetSeverityTableColumns } from './columns'; import { getSeverityColor } from './helpers'; -import { TOTAL_COUNT_OF_ALERTS } from '../../alerts_table/translations'; const DONUT_HEIGHT = 150; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/actions_cell.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/actions_cell.tsx index 44c51d9910076..0cbd92e26012c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/actions_cell.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/actions_cell.tsx @@ -48,7 +48,7 @@ export const ActionsCellComponent: GetSecurityAlertsTableProp<'renderActionsCell _id: (alert as Ecs)._id, _index: (alert as Ecs)._index, ecs: alert as Ecs, - data: legacyAlert, + data: legacyAlert as TimelineItem['data'], }; return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 35890fef22d9a..14db32d3e6750 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -10,15 +10,12 @@ import type { EuiDataGridRowHeightsOptions, EuiDataGridStyle } from '@elastic/eu import { EuiFlexGroup } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; import type { - Alert, AlertsTableImperativeApi, AlertsTableProps, RenderContext, -} from '@kbn/triggers-actions-ui-plugin/public/types'; -import type { SetOptional } from 'type-fest'; -import { noop } from 'lodash'; -import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; +} from '@kbn/response-ops-alerts-table/types'; import { ALERT_BUILDING_BLOCK_TYPE, AlertConsumers } from '@kbn/rule-data-utils'; +import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; import styled from 'styled-components'; import { useDispatch } from 'react-redux'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; @@ -28,15 +25,18 @@ import { tableDefaults, TableId, } from '@kbn/securitysolution-data-table'; -import { ActionsCell } from './actions_cell'; +import type { SetOptional } from 'type-fest'; +import { noop } from 'lodash'; +import type { Alert } from '@kbn/alerting-types'; +import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import { getBulkActionsByTableType } from '../../hooks/trigger_actions_alert_table/use_bulk_actions'; +import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import type { SecurityAlertsTableContext, GetSecurityAlertsTableProp, SecurityAlertsTableProps, } from './types'; -import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; -import { getBulkActionsByTableType } from '../../hooks/trigger_actions_alert_table/use_bulk_actions'; -import { useAlertsTableFieldsBrowserOptions } from '../../hooks/trigger_actions_alert_table/use_trigger_actions_browser_fields_options'; +import { ActionsCell } from './actions_cell'; import { useGlobalTime } from '../../../common/containers/use_global_time'; import { useLicense } from '../../../common/hooks/use_license'; import { @@ -75,6 +75,7 @@ import { getDefaultControlColumn } from '../../../timelines/components/timeline/ import { AdditionalToolbarControls } from './additional_toolbar_controls'; import { useFetchUserProfilesFromAlerts } from '../../configurations/security_solution_detections/fetch_page_context'; import { useCellActionsOptions } from '../../hooks/trigger_actions_alert_table/use_cell_actions'; +import { useAlertsTableFieldsBrowserOptions } from '../../hooks/trigger_actions_alert_table/use_trigger_actions_browser_fields_options'; const { updateIsLoading, updateTotalCount } = dataTableActions; @@ -154,7 +155,7 @@ const initialSort: GetSecurityAlertsTableProp<'initialSort'> = [ const casesConfiguration = { featureId: CASES_FEATURE_ID, owner: [APP_ID], syncAlerts: true }; const emptyInputFilters: Filter[] = []; -export const AlertsTableComponent: FC = ({ +export const AlertsTableComponent: FC> = ({ inputFilters = emptyInputFilters, tableType = TableId.alertsOnAlertsPage, sourcererScope = SourcererScopeName.detections, @@ -163,10 +164,8 @@ export const AlertsTableComponent: FC = ({ ...tablePropsOverrides }) => { const { id } = tablePropsOverrides; - const { - triggersActionsUi: { getAlertsStateTable: AlertsTable }, - uiSettings, - } = useKibana().services; + const { data, http, notifications, fieldFormats, application, licensing, uiSettings, settings } = + useKibana().services; const [visualizationInFlyoutEnabled] = useUiSetting$( ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING ); @@ -429,7 +428,7 @@ export const AlertsTableComponent: FC = ({ - ref={alertsTableRef} // Stores separate configuration based on the view of the table id={id ?? `detection-engine-alert-table-${tableType}-${tableView}`} @@ -468,6 +467,15 @@ export const AlertsTableComponent: FC = ({ } cellActionsOptions={cellActionsOptions} showInspectButton + services={{ + data, + http, + notifications, + fieldFormats, + application, + licensing, + settings, + }} {...tablePropsOverrides} /> diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx index e402dfe2488fa..2900f1196eff6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx @@ -10,7 +10,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import type { Filter } from '@kbn/es-query'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; -import type { BulkActionsConfig } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { BulkActionsConfig } from '@kbn/response-ops-alerts-table/types'; import { dataTableActions, TableId, tableDefaults } from '@kbn/securitysolution-data-table'; import type { RunTimeMappings } from '@kbn/timelines-plugin/common/search_strategy'; import type { CustomBulkAction } from '../../../../../common/types'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts index 2151382a48317..5422e78f1b80b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts @@ -8,8 +8,8 @@ import type { ISearchStart } from '@kbn/data-plugin/public'; import type { Filter } from '@kbn/es-query'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import type { AlertsTablePropsWithRef } from '@kbn/response-ops-alerts-table/types'; import type { EuiContextMenuPanelItemDescriptorEntry } from '@elastic/eui/src/components/context_menu/context_menu'; -import type { AlertsTablePropsWithRef } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { TableId } from '@kbn/securitysolution-data-table'; import type { SourcererScopeName } from '../../../sourcerer/store/model'; import type { AlertsUserProfilesData } from '../../configurations/security_solution_detections/fetch_page_context'; @@ -98,4 +98,4 @@ export interface SecurityAlertsTableContext { export type SecurityAlertsTableProps = AlertsTablePropsWithRef; export type GetSecurityAlertsTableProp = NonNullable; -export type { SelectedAlertWithLegacyFormats } from '@kbn/triggers-actions-ui-plugin/public/types'; +export type { AlertWithLegacyFormats } from '@kbn/response-ops-alerts-table/types'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx index 81035bdf1d247..69de8745a48ef 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/fetch_page_context.tsx @@ -7,8 +7,8 @@ import { useMemo } from 'react'; import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; -import type { Alerts } from '@kbn/triggers-actions-ui-plugin/public/types'; import type { EuiDataGridColumn } from '@elastic/eui'; +import type { Alert } from '@kbn/alerting-types'; import { useBulkGetUserProfiles } from '../../../common/components/user_profiles/use_bulk_get_user_profiles'; export interface AlertsUserProfilesData { @@ -26,7 +26,7 @@ export const useFetchUserProfilesFromAlerts = ({ alerts, columns, }: { - alerts: Alerts; + alerts: Alert[]; columns: EuiDataGridColumn[]; }) => { const uids = useMemo(() => { @@ -34,8 +34,8 @@ export const useFetchUserProfilesFromAlerts = ({ alerts.forEach((alert) => { profileUidColumns.forEach((columnId) => { if (columns.find((column) => column.id === columnId) != null) { - const userUids = alert[columnId]; - userUids?.forEach((uid) => ids.add(uid as string)); + const userUids = alert[columnId] as string[]; + userUids?.forEach((uid) => ids.add(uid)); } }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx index dce004d452d61..5c8759bb32b52 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx @@ -48,7 +48,7 @@ describe('RenderCellValue', () => { header = cloneDeep(defaultHeaders[0]); props = { columnId, - data, + legacyAlert: data, eventId, header, isDetails: false, @@ -78,7 +78,13 @@ describe('RenderCellValue', () => { ); - expect(wrapper.find(DefaultCellRenderer).props()).toEqual(props); + const { legacyAlert, ...defaultCellRendererProps } = props; + + expect(wrapper.find(DefaultCellRenderer).props()).toEqual({ + ...defaultCellRendererProps, + data: legacyAlert, + scopeId: SourcererScopeName.default, + }); }); test('it renders a GuidedOnboardingTourStep', () => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_alert_actions.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_alert_actions.tsx index 56b5ec780c93c..4fc997c9f6f9c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_alert_actions.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_alert_actions.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import type { BulkActionsConfig } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { BulkActionsConfig } from '@kbn/response-ops-alerts-table/types'; import { useCallback, useMemo } from 'react'; import type { Filter } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx index 3d0f646685607..7dd018243cb0e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx @@ -21,6 +21,7 @@ import type { ComponentProps, JSXElementConstructor, PropsWithChildren } from 'r import React from 'react'; import { makeAction } from '../../../common/components/cell_actions/mocks'; import { VIEW_SELECTION } from '../../../../common/constants'; +import type { LegacyField } from '@kbn/alerting-types'; const mockDataGridRef: { current: EuiDataGridRefProps; @@ -87,7 +88,7 @@ describe('getUseCellActionsHook', () => { () => useCellActionsOptions(TableId.test, { columns: mockColumns as unknown as EuiDataGridColumn[], - oldAlertsData: mockData, + oldAlertsData: mockData as LegacyField[][], dataGridRef: mockDataGridRef, pageSize: 10, pageIndex: 0, @@ -111,7 +112,7 @@ describe('getUseCellActionsHook', () => { () => useCellActionsOptions(TableId.test, { columns: mockColumns as unknown as EuiDataGridColumn[], - oldAlertsData: mockData, + oldAlertsData: mockData as LegacyField[][], dataGridRef: mockDataGridRef, pageSize: 10, pageIndex: 0, diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx index 4d74161c54153..afa0724e140ec 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx @@ -8,7 +8,7 @@ import type { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; import { useCallback, useMemo } from 'react'; import { TableId, tableDefaults, dataTableSelectors } from '@kbn/securitysolution-data-table'; -import type { RenderContext } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { RenderContext } from '@kbn/response-ops-alerts-table/types'; import type { UseDataGridColumnsSecurityCellActionsProps } from '../../../common/components/cell_actions'; import { useDataGridColumnsSecurityCellActions } from '../../../common/components/cell_actions'; import { SecurityCellActionsTrigger, SecurityCellActionType } from '../../../app/actions/constants'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx index 232b1f9f9d43d..1dd23d37206ae 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx @@ -10,16 +10,16 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { SECURITY_SOLUTION_OWNER } from '@kbn/cases-plugin/common'; -import type { CasesService } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/types'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash/fp'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; +import type { CasesPublicStart } from '@kbn/cases-plugin/public'; import { useRiskInputActions } from './use_risk_input_actions'; import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; export const useRiskInputActionsPanels = (inputs: InputAlert[], closePopover: () => void) => { - const { cases: casesService } = useKibana<{ cases?: CasesService }>().services; + const { cases: casesService } = useKibana<{ cases?: CasesPublicStart }>().services; const { addToExistingCase, addToNewCaseClick, addToNewTimeline } = useRiskInputActions( inputs, closePopover diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx index e271fd2dae6cd..088f02c213aac 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx @@ -12,9 +12,9 @@ import { css } from '@emotion/react'; import { type EuiBasicTableColumn, EuiText, EuiInMemoryTable, useEuiFontSize } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { dataTableSelectors, tableDefaults } from '@kbn/securitysolution-data-table'; -import { getCategory } from '@kbn/triggers-actions-ui-plugin/public'; import type { BrowserFields, TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import type { FieldSpec } from '@kbn/data-plugin/common'; +import { getCategory } from '@kbn/response-ops-alerts-fields-browser/helpers'; import { TableFieldNameCell } from '../components/table_field_name_cell'; import { TableFieldValueCell } from '../components/table_field_value_cell'; import { TABLE_TAB_CONTENT_TEST_ID, TABLE_TAB_SEARCH_INPUT_TEST_ID } from './test_ids'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx index 7fff39f0ce452..5d9cee5daffc8 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx @@ -9,7 +9,7 @@ import React, { useCallback } from 'react'; import { EuiButton } from '@elastic/eui'; import styled from 'styled-components'; -import type { CreateFieldComponent } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { CreateFieldComponent } from '@kbn/response-ops-alerts-fields-browser/types'; import type { OpenFieldEditor } from '..'; import * as i18n from './translations'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.test.tsx index a565a31ef67a7..411412a89b1f0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.test.tsx @@ -12,7 +12,7 @@ import { useFieldTableColumns } from '.'; import { TestProviders } from '../../../../common/mock'; import { EuiInMemoryTable } from '@elastic/eui'; -import type { BrowserFieldItem } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { BrowserFieldItem } from '@kbn/response-ops-alerts-fields-browser/types'; const mockOnHide = jest.fn(); const mockOpenFieldEditor = jest.fn(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx index 1b93175fb24f5..bf102dc5a9f82 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx @@ -22,7 +22,7 @@ import type { Action } from '@elastic/eui/src/components/basic_table/action_type import type { BrowserFieldItem, GetFieldTableColumns, -} from '@kbn/triggers-actions-ui-plugin/public/types'; +} from '@kbn/response-ops-alerts-fields-browser/types'; import * as i18n from './translations'; import { getIconFromType } from '../../../../common/components/event_details/helpers'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx index 7b67fb6614adf..a8a48567c8501 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx @@ -21,7 +21,7 @@ import { SourcererScopeName } from '../../../sourcerer/store/model'; import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers'; import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants'; import { EuiInMemoryTable } from '@elastic/eui'; -import type { BrowserFieldItem } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { BrowserFieldItem } from '@kbn/response-ops-alerts-fields-browser/types'; let mockIndexPatternFieldEditor: Start; jest.mock('../../../common/lib/kibana'); diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.tsx index 435168ba5fbaf..c9acc48051911 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/fields_browser/index.tsx @@ -12,7 +12,7 @@ import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; import type { CreateFieldComponent, GetFieldTableColumns, -} from '@kbn/triggers-actions-ui-plugin/public/types'; +} from '@kbn/response-ops-alerts-fields-browser/types'; import type { ColumnHeaderOptions } from '../../../../common/types'; import { useDataView } from '../../../common/containers/source/use_data_view'; import { useKibana } from '../../../common/lib/kibana'; diff --git a/x-pack/solutions/security/plugins/security_solution/tsconfig.json b/x-pack/solutions/security/plugins/security_solution/tsconfig.json index 115115affa66d..1877e7e1756f4 100644 --- a/x-pack/solutions/security/plugins/security_solution/tsconfig.json +++ b/x-pack/solutions/security/plugins/security_solution/tsconfig.json @@ -228,6 +228,9 @@ "@kbn/langchain", "@kbn/discover-shared-plugin", "@kbn/react-hooks", + "@kbn/response-ops-alerts-fields-browser", + "@kbn/response-ops-alerts-table", + "@kbn/alerting-types", "@kbn/index-adapter", "@kbn/core-http-server-utils", "@kbn/security-solution-connectors", diff --git a/x-pack/solutions/security/plugins/threat_intelligence/public/mocks/test_providers.tsx b/x-pack/solutions/security/plugins/threat_intelligence/public/mocks/test_providers.tsx index 29f48c0e15df7..f042b55203420 100644 --- a/x-pack/solutions/security/plugins/threat_intelligence/public/mocks/test_providers.tsx +++ b/x-pack/solutions/security/plugins/threat_intelligence/public/mocks/test_providers.tsx @@ -121,9 +121,7 @@ export const mockedServices = { cases: casesServiceMock, storage, unifiedSearch, - triggersActionsUi: { - getFieldBrowser: jest.fn().mockReturnValue(null), - }, + triggersActionsUi: {}, timelines: timelinesServiceMock, securityLayout: { getPluginWrapper: diff --git a/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.test.tsx b/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.test.tsx index 198332e24b68e..60bfb14b35fc2 100644 --- a/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.test.tsx +++ b/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.test.tsx @@ -5,18 +5,20 @@ * 2.0. */ -import { - mockedTriggersActionsUiService, - TestProvidersComponent, -} from '../../../../mocks/test_providers'; -import { render } from '@testing-library/react'; import React from 'react'; +import { render } from '@testing-library/react'; +import { FieldBrowser } from '@kbn/response-ops-alerts-fields-browser'; +import { TestProvidersComponent } from '../../../../mocks/test_providers'; import { IndicatorsFieldBrowser } from './field_browser'; +jest.mock('@kbn/response-ops-alerts-fields-browser', () => ({ + FieldBrowser: jest.fn().mockReturnValue(
), +})); + const stub = jest.fn(); describe('', () => { - it('should retrieve the field browser widget from respective service', () => { + it('should render the field browser widget with the correct props', () => { render( ', () => { } ); - expect(mockedTriggersActionsUiService.getFieldBrowser).toHaveBeenCalledTimes(1); - expect(mockedTriggersActionsUiService.getFieldBrowser).toHaveBeenCalledWith( + expect(FieldBrowser).toHaveBeenCalledWith( expect.objectContaining({ browserFields: {}, columnIds: [], @@ -39,7 +40,8 @@ describe('', () => { options: { preselectedCategoryIds: ['threat', 'base', 'event', 'agent'], }, - }) + }), + expect.anything() ); }); }); diff --git a/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.tsx b/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.tsx index 26c44ae1de45a..6dc9f8042fb33 100644 --- a/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.tsx +++ b/x-pack/solutions/security/plugins/threat_intelligence/public/modules/indicators/components/table/field_browser.tsx @@ -5,9 +5,9 @@ * 2.0. */ +import React, { VFC } from 'react'; import { BrowserField } from '@kbn/rule-registry-plugin/common'; -import { VFC } from 'react'; -import { useKibana } from '../../../../hooks/use_kibana'; +import { FieldBrowser } from '@kbn/response-ops-alerts-fields-browser'; export interface IndicatorsFieldBrowserProps { browserFields: Readonly>>; @@ -22,15 +22,15 @@ export const IndicatorsFieldBrowser: VFC = ({ onResetColumns, onToggleColumn, }) => { - const { triggersActionsUi } = useKibana().services; - - return triggersActionsUi.getFieldBrowser({ - browserFields, - columnIds, - onResetColumns, - onToggleColumn, - options: { - preselectedCategoryIds: ['threat', 'base', 'event', 'agent'], - }, - }); + return ( + + ); }; diff --git a/x-pack/solutions/security/plugins/threat_intelligence/tsconfig.json b/x-pack/solutions/security/plugins/threat_intelligence/tsconfig.json index ffac934023eb6..9f635e3d816b5 100644 --- a/x-pack/solutions/security/plugins/threat_intelligence/tsconfig.json +++ b/x-pack/solutions/security/plugins/threat_intelligence/tsconfig.json @@ -33,7 +33,8 @@ "@kbn/ui-theme", "@kbn/securitysolution-io-ts-list-types", "@kbn/core-ui-settings-browser", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/response-ops-alerts-fields-browser" ], "exclude": ["target/**/*"] } diff --git a/yarn.lock b/yarn.lock index 0b6a1952f719d..26472dc5745f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6868,6 +6868,18 @@ version "0.0.0" uid "" +"@kbn/response-ops-alerts-apis@link:packages/response-ops/alerts_apis": + version "0.0.0" + uid "" + +"@kbn/response-ops-alerts-fields-browser@link:packages/response-ops/alerts_fields_browser": + version "0.0.0" + uid "" + +"@kbn/response-ops-alerts-table@link:packages/response-ops/alerts_table": + version "0.0.0" + uid "" + "@kbn/response-ops-rule-form@link:src/platform/packages/shared/response-ops/rule_form": version "0.0.0" uid "" From d9eb481696a74689e9af86fb0932c90202367c38 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Fri, 17 Jan 2025 12:09:24 +0100 Subject: [PATCH 03/14] [ResponseOps][Alerts] Add configuration option for alerts table to getCases (#207038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary > [!IMPORTANT] > 🚧 Merging to the `alerts-table-refactor` feature branch, no review needed from teams other than `response-ops` > [!WARNING] > Some tests are intentionally left failing since the converted usages of the alerts table still have to be validated/improved by solutions and may change significantly Adds a `renderAlertsTable` prop to `getCases` to allow solutions to customize the alerts table shown in the `Alerts` tab of their case view. --- .../cases/public/client/ui/get_cases.tsx | 2 + .../cases/public/components/app/routes.tsx | 2 + .../cases/public/components/app/types.ts | 4 +- .../components/case_view/case_view_page.tsx | 7 ++- .../case_view/components/case_view_alerts.tsx | 45 +++++++++++++------ .../public/components/case_view/index.tsx | 2 + .../public/components/case_view/types.ts | 11 ++++- .../public/pages/cases/components/cases.tsx | 3 +- .../public/cases/pages/index.tsx | 2 + .../components/alerts_table/index.tsx | 2 +- 10 files changed, 61 insertions(+), 19 deletions(-) diff --git a/x-pack/platform/plugins/shared/cases/public/client/ui/get_cases.tsx b/x-pack/platform/plugins/shared/cases/public/client/ui/get_cases.tsx index c837165f8bee9..3a3569782b251 100644 --- a/x-pack/platform/plugins/shared/cases/public/client/ui/get_cases.tsx +++ b/x-pack/platform/plugins/shared/cases/public/client/ui/get_cases.tsx @@ -37,6 +37,7 @@ export const getCasesLazy = ({ timelineIntegration, features, releasePhase, + renderAlertsTable, }: GetCasesPropsInternal) => ( diff --git a/x-pack/platform/plugins/shared/cases/public/components/app/routes.tsx b/x-pack/platform/plugins/shared/cases/public/components/app/routes.tsx index 9f9114a729264..d2750797a1476 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/app/routes.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/app/routes.tsx @@ -40,6 +40,7 @@ const CasesRoutesComponent: React.FC = ({ onAlertsTableLoaded, refreshRef, timelineIntegration, + renderAlertsTable, }) => { const { basePath, permissions } = useCasesContext(); const { navigateToAllCases } = useAllCasesNavigation(); @@ -90,6 +91,7 @@ const CasesRoutesComponent: React.FC = ({ onAlertsTableLoaded={onAlertsTableLoaded} refreshRef={refreshRef} timelineIntegration={timelineIntegration} + renderAlertsTable={renderAlertsTable} /> diff --git a/x-pack/platform/plugins/shared/cases/public/components/app/types.ts b/x-pack/platform/plugins/shared/cases/public/components/app/types.ts index 9515e1ed4cbcc..e58b4a3720628 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/app/types.ts +++ b/x-pack/platform/plugins/shared/cases/public/components/app/types.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { MutableRefObject } from 'react'; +import type { ComponentType, MutableRefObject } from 'react'; +import type { CaseViewAlertsTableProps } from '../case_view/types'; import type { CaseViewRefreshPropInterface, UseFetchAlertData } from '../../../common/ui/types'; import type { CasesNavigation } from '../links'; import type { CasesTimelineIntegration } from '../timeline_context'; @@ -22,4 +23,5 @@ export interface CasesRoutesProps { refreshRef?: MutableRefObject; timelineIntegration?: CasesTimelineIntegration; onAlertsTableLoaded?: (eventIds: Array>) => void; + renderAlertsTable?: ComponentType; } diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/case_view_page.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/case_view_page.tsx index 8d1a2c6c8cd08..69bd3489f273a 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/case_view_page.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/case_view_page.tsx @@ -41,6 +41,7 @@ export const CaseViewPage = React.memo( showAlertDetails, useFetchAlertData, onAlertsTableLoaded, + renderAlertsTable, }) => { const { features } = useCasesContext(); const { urlParams } = useUrlParams(); @@ -122,7 +123,11 @@ export const CaseViewPage = React.memo( /> )} {activeTabId === CASE_VIEW_PAGE_TABS.ALERTS && features.alerts.enabled && ( - + )} {activeTabId === CASE_VIEW_PAGE_TABS.FILES && } {activeTabId === CASE_VIEW_PAGE_TABS.OBSERVABLES && ( diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx index e2b9f3933b8b0..39529c2e45d01 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_alerts.tsx @@ -5,12 +5,13 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { type ComponentType, useMemo } from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiProgress } from '@elastic/eui'; import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; -import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import { AlertsTable as DefaultAlertsTable } from '@kbn/response-ops-alerts-table'; import type { SetRequired } from 'type-fest'; +import type { CaseViewAlertsTableProps } from '../types'; import { SECURITY_SOLUTION_OWNER } from '../../../../common/constants'; import type { CaseUI } from '../../../../common'; import { getManualAlertIds } from './helpers'; @@ -23,9 +24,14 @@ import { useKibana } from '../../../common/lib/kibana'; interface CaseViewAlertsProps { caseData: CaseUI; onAlertsTableLoaded?: (eventIds: Array>) => void; + renderAlertsTable?: ComponentType; } -export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlertsProps) => { +export const CaseViewAlerts = ({ + caseData, + renderAlertsTable: CustomAlertsTable, + onAlertsTableLoaded, +}: CaseViewAlertsProps) => { const { services } = useKibana(); const { data, http, notifications, fieldFormats, application, licensing, settings } = services as SetRequired; @@ -55,6 +61,10 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts ); } + const AlertsTable = + CustomAlertsTable ?? + (DefaultAlertsTable as NonNullable); + return isLoadingAlertFeatureIds ? ( @@ -75,17 +85,24 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts query={alertIdsQuery} showAlertStatusWithFlapping={caseData.owner !== SECURITY_SOLUTION_OWNER} onLoaded={onAlertsTableLoaded} - services={{ - data, - http, - notifications, - fieldFormats, - application, - settings, - // In the Cases UI the licensing service is defined - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - licensing: licensing!, - }} + // Only provide the services to the default alerts table. + // Spreading from object to avoid incorrectly overriding + // services to `undefined` in custom solution tables + {...(CustomAlertsTable + ? {} + : { + services: { + data, + http, + notifications, + fieldFormats, + application, + settings, + // In the Cases UI the licensing service is defined + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + licensing: licensing!, + }, + })} /> ); diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/index.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/index.tsx index 0aeeaf56993dd..9fb7456bf6439 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/index.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/index.tsx @@ -35,6 +35,7 @@ export const CaseView = React.memo( useFetchAlertData, onAlertsTableLoaded, refreshRef, + renderAlertsTable, }: CaseViewProps) => { const { spaces: spacesApi } = useKibana().services; const { detailName: caseId } = useCaseViewParams(); @@ -88,6 +89,7 @@ export const CaseView = React.memo( useFetchAlertData={useFetchAlertData} onAlertsTableLoaded={onAlertsTableLoaded} refreshRef={refreshRef} + renderAlertsTable={renderAlertsTable} /> ) : null; diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/types.ts b/x-pack/platform/plugins/shared/cases/public/components/case_view/types.ts index d77836e1246ca..418bc409eae82 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/types.ts +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/types.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { MutableRefObject } from 'react'; +import type { ComponentType, MutableRefObject } from 'react'; +import type { AlertsTableProps } from '@kbn/response-ops-alerts-table/types'; import type { CasesTimelineIntegration } from '../timeline_context'; import type { CasesNavigation } from '../links'; import type { CaseViewRefreshPropInterface, CaseUI } from '../../../common'; @@ -16,6 +17,7 @@ export interface CaseViewBaseProps { ruleDetailsNavigation?: CasesNavigation; showAlertDetails?: (alertId: string, index: string) => void; useFetchAlertData: UseFetchAlertData; + renderAlertsTable?: ComponentType; onAlertsTableLoaded?: (eventIds: Array>) => void; /** * A React `Ref` that Exposes data refresh callbacks. @@ -39,3 +41,10 @@ export interface OnUpdateFields { onSuccess?: () => void; onError?: () => void; } + +export type CaseViewAlertsTableProps = Pick< + AlertsTableProps, + 'id' | 'ruleTypeIds' | 'consumers' | 'query' | 'showAlertStatusWithFlapping' | 'onLoaded' +> & { + services?: AlertsTableProps['services']; +}; diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/cases/components/cases.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/cases/components/cases.tsx index 81db2b793209b..dabf4cb195230 100644 --- a/x-pack/solutions/observability/plugins/observability/public/pages/cases/components/cases.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/pages/cases/components/cases.tsx @@ -12,7 +12,7 @@ import { useKibana } from '../../../utils/kibana_react'; import { usePluginContext } from '../../../hooks/use_plugin_context'; import { useFetchAlertDetail } from '../../../hooks/use_fetch_alert_detail'; import { useFetchAlertData } from '../../../hooks/use_fetch_alert_data'; -import { LazyAlertsFlyout } from '../../..'; +import { LazyAlertsFlyout, ObservabilityAlertsTable } from '../../..'; import { CASES_PATH, paths } from '../../../../common/locators/paths'; export interface CasesProps { @@ -63,6 +63,7 @@ export function Cases({ permissions }: CasesProps) { }} showAlertDetails={handleShowAlertDetails} useFetchAlertData={useFetchAlertData} + renderAlertsTable={(props) => } /> {alertDetail && selectedAlertId !== '' && !alertLoading ? ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/cases/pages/index.tsx index bc90dfcd0dc02..47af252829c21 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/cases/pages/index.tsx @@ -10,6 +10,7 @@ import { useDispatch } from 'react-redux'; import type { CaseViewRefreshPropInterface } from '@kbn/cases-plugin/common'; import { CaseMetricsFeature } from '@kbn/cases-plugin/common'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { AlertsTableComponent } from '../../detections/components/alerts_table'; import { CaseDetailsRefreshContext } from '../../common/components/endpoint'; import { DocumentDetailsRightPanelKey } from '../../flyout/document_details/shared/constants/panel_keys'; import { RulePanelKey } from '../../flyout/rule_details/right'; @@ -145,6 +146,7 @@ const CaseContainerComponent: React.FC = () => { useFetchAlertData, onAlertsTableLoaded, permissions: userCasesPermissions, + renderAlertsTable: (props) => , })} diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 14db32d3e6750..60dbd8d5fb7c9 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -139,7 +139,7 @@ const EuiDataGridContainer = styled.div` interface DetectionEngineAlertTableProps extends SetOptional { inputFilters?: Filter[]; - tableType: TableId; + tableType?: TableId; sourcererScope?: SourcererScopeName; isLoading?: boolean; onRuleChange?: () => void; From d27f869363fe6e843f93f428d2e37310ee38f3af Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 20 Jan 2025 15:55:03 +0100 Subject: [PATCH 04/14] [ResponseOps][Alerts] Fix Security cell value component props (#207095) ## Summary The Security Solution alerts table CellValue component was receiving some wrong props when used in the Rule preview table. This PR removes the spread `{...props}` expression and type cast that didn't catch this error and correctly converts props and types. --- .../preview_table_cell_renderer.tsx | 34 +- .../components/alerts_table/types.ts | 4 +- .../render_cell_value.tsx | 308 +++++++++--------- 3 files changed, 192 insertions(+), 154 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx index 68d40cd18128b..096346312e193 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx @@ -5,23 +5,47 @@ * 2.0. */ -import type { ComponentProps } from 'react'; import React from 'react'; import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; import { TableId } from '@kbn/securitysolution-data-table'; +import type { LegacyField } from '@kbn/alerting-types'; import type { CellValueElementProps } from '../../../../../common/types'; import { SourcererScopeName } from '../../../../sourcerer/store/model'; import { CellValue } from '../../../../detections/configurations/security_solution_detections'; export const PreviewRenderCellValue: React.FC< EuiDataGridCellValueElementProps & CellValueElementProps -> = (props) => { +> = ({ + data, + ecsData, + setCellProps, + isExpandable, + isExpanded, + isDetails, + rowIndex, + colIndex, + columnId, + rowRenderers, + isDraggable, + truncate, +}) => { return ( )} - asPlainText={true} - scopeId={SourcererScopeName.detections} tableType={TableId.rulePreview} + scopeId={SourcererScopeName.detections} + legacyAlert={(data ?? []) as LegacyField[]} + ecsAlert={ecsData} + asPlainText={true} + setCellProps={setCellProps} + isExpandable={isExpandable} + isExpanded={isExpanded} + isDetails={isDetails} + rowIndex={rowIndex} + colIndex={colIndex} + columnId={columnId} + rowRenderers={rowRenderers} + isDraggable={isDraggable} + truncate={truncate} /> ); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts index 5422e78f1b80b..ee75ba69cc747 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/types.ts @@ -86,9 +86,9 @@ export type AlertTableContextMenuItem = EuiContextMenuPanelItemDescriptorEntry; export interface SecurityAlertsTableContext { tableType: TableId; - rowRenderers: RowRenderer[]; + rowRenderers?: RowRenderer[]; isDetails: boolean; - truncate: boolean; + truncate?: boolean; isDraggable: boolean; leadingControlColumn: ControlColumnProps; userProfiles: AlertsUserProfilesData; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx index 05ed6114ea2f7..857265b6c7d62 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx @@ -5,8 +5,8 @@ * 2.0. */ +import React, { useMemo, memo, type ComponentProps } from 'react'; import { EuiIcon, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React, { useMemo, memo } from 'react'; import { find, getOr } from 'lodash/fp'; import type { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; import { tableDefaults, dataTableSelectors } from '@kbn/securitysolution-data-table'; @@ -35,157 +35,171 @@ import type { GetSecurityAlertsTableProp } from '../../components/alerts_table/t * from the TGrid */ -export const CellValue: GetSecurityAlertsTableProp<'renderCellValue'> = memo( - function RenderCellValue(props) { - const { - columnId, - rowIndex, - scopeId, - tableId, - tableType, - header, - legacyAlert, - ecsAlert, - linkValues, - rowRenderers, - isDetails, - isExpandable, - isDraggable = false, - isExpanded, - colIndex, - eventId, - setCellProps, - truncate, - context, - } = props; - const isTourAnchor = useMemo( - () => - columnId === SIGNAL_RULE_NAME_FIELD_NAME && - isDetectionsAlertsTable(tableType) && - rowIndex === 0 && - !props.isDetails, - [columnId, props.isDetails, rowIndex, tableType] - ); - const { browserFields } = useSourcererDataView(scopeId); - const browserFieldsByName = useMemo(() => getAllFieldsByName(browserFields), [browserFields]); - const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); - const license = useLicense(); - const viewMode = - useDeepEqualSelector((state) => (getTable(state, tableId ?? '') ?? tableDefaults).viewMode) ?? - tableDefaults.viewMode; +type RenderCellValueProps = Pick< + ComponentProps>, + | 'columnId' + | 'rowIndex' + | 'tableId' + | 'tableType' + | 'legacyAlert' + | 'ecsAlert' + | 'rowRenderers' + | 'isDetails' + | 'isExpandable' + | 'isDraggable' + | 'isExpanded' + | 'colIndex' + | 'setCellProps' + | 'truncate' +> & + Record; - const gridColumns = useMemo(() => { - return getColumns(license); - }, [license]); +export const CellValue = memo(function RenderCellValue({ + columnId, + rowIndex, + scopeId, + tableId, + tableType, + header, + legacyAlert, + ecsAlert, + linkValues, + rowRenderers, + isDetails, + isExpandable, + isDraggable = false, + isExpanded, + colIndex, + eventId, + setCellProps, + truncate, + context, +}: RenderCellValueProps) { + const isTourAnchor = useMemo( + () => + columnId === SIGNAL_RULE_NAME_FIELD_NAME && + isDetectionsAlertsTable(tableType) && + rowIndex === 0 && + !isDetails, + [columnId, isDetails, rowIndex, tableType] + ); + const { browserFields } = useSourcererDataView(scopeId); + const browserFieldsByName = useMemo(() => getAllFieldsByName(browserFields), [browserFields]); + const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); + const license = useLicense(); + const viewMode = + useDeepEqualSelector((state) => (getTable(state, tableId ?? '') ?? tableDefaults).viewMode) ?? + tableDefaults.viewMode; - const columnHeaders = useMemo(() => { - return viewMode === VIEW_SELECTION.gridView ? gridColumns : eventRenderedViewColumns; - }, [gridColumns, viewMode]); + const gridColumns = useMemo(() => { + return getColumns(license); + }, [license]); - /** - * There is difference between how `triggers actions` fetched data v/s - * how security solution fetches data via timelineSearchStrategy - * - * _id and _index fields are array in timelineSearchStrategy but not in - * ruleStrategy - * - * - */ + const columnHeaders = useMemo(() => { + return viewMode === VIEW_SELECTION.gridView ? gridColumns : eventRenderedViewColumns; + }, [gridColumns, viewMode]); - const finalData = useMemo(() => { - return (legacyAlert as TimelineNonEcsData[]).map((field) => { - if (['_id', '_index'].includes(field.field)) { - const newValue = field.value ?? ''; - return { - field: field.field, - value: Array.isArray(newValue) ? newValue : [newValue], - }; - } else { - return field; - } - }); - }, [legacyAlert]); + /** + * There is difference between how `triggers actions` fetched data v/s + * how security solution fetches data via timelineSearchStrategy + * + * _id and _index fields are array in timelineSearchStrategy but not in + * ruleStrategy + * + * + */ - const actualSuppressionCount = useMemo(() => { - // We check both ecsAlert and data for the suppression count because it could be in either one, - // depending on where RenderCellValue is being used - when used in cases, data is populated, - // whereas in the regular security alerts table it's in ecsAlert - const ecsSuppressionCount = ecsAlert?.kibana?.alert.suppression?.docs_count?.[0]; - const dataSuppressionCount = find( - { field: 'kibana.alert.suppression.docs_count' }, - legacyAlert - )?.value?.[0] as number | undefined; - return ecsSuppressionCount ? parseInt(ecsSuppressionCount, 10) : dataSuppressionCount; - }, [ecsAlert, legacyAlert]); + const finalData = useMemo(() => { + return (legacyAlert as TimelineNonEcsData[]).map((field) => { + if (['_id', '_index'].includes(field.field)) { + const newValue = field.value ?? ''; + return { + field: field.field, + value: Array.isArray(newValue) ? newValue : [newValue], + }; + } else { + return field; + } + }); + }, [legacyAlert]); - const Renderer = useMemo(() => { - const myHeader = header ?? { id: columnId, ...browserFieldsByName[columnId] }; - const colHeader = columnHeaders.find((col) => col.id === columnId); - const localLinkValues = getOr([], colHeader?.linkField ?? '', ecsAlert); - return ( - - - - ); - }, [ - header, - columnId, - browserFieldsByName, - columnHeaders, - ecsAlert, - isTourAnchor, - browserFields, - finalData, - eventId, - isDetails, - isDraggable, - isExpandable, - isExpanded, - linkValues, - rowIndex, - colIndex, - rowRenderers, - setCellProps, - scopeId, - truncate, - context, - ]); + const actualSuppressionCount = useMemo(() => { + // We check both ecsAlert and data for the suppression count because it could be in either one, + // depending on where RenderCellValue is being used - when used in cases, data is populated, + // whereas in the regular security alerts table it's in ecsAlert + const ecsSuppressionCount = ecsAlert?.kibana?.alert.suppression?.docs_count?.[0]; + const dataSuppressionCount = find({ field: 'kibana.alert.suppression.docs_count' }, legacyAlert) + ?.value?.[0] as number | undefined; + return ecsSuppressionCount ? parseInt(ecsSuppressionCount, 10) : dataSuppressionCount; + }, [ecsAlert, legacyAlert]); - return columnId === SIGNAL_RULE_NAME_FIELD_NAME && actualSuppressionCount ? ( - - - - - - - {Renderer} - - ) : ( - <>{Renderer} + const Renderer = useMemo(() => { + const myHeader = header ?? { id: columnId, ...browserFieldsByName[columnId] }; + const colHeader = columnHeaders.find((col) => col.id === columnId); + const localLinkValues = getOr([], colHeader?.linkField ?? '', ecsAlert); + return ( + + + ); - } -); + }, [ + header, + columnId, + browserFieldsByName, + columnHeaders, + ecsAlert, + isTourAnchor, + browserFields, + finalData, + eventId, + isDetails, + isDraggable, + isExpandable, + isExpanded, + linkValues, + rowIndex, + colIndex, + rowRenderers, + setCellProps, + scopeId, + truncate, + context, + ]); + + return columnId === SIGNAL_RULE_NAME_FIELD_NAME && actualSuppressionCount ? ( + + + + + + + {Renderer} + + ) : ( + <>{Renderer} + ); +}); From a7d4bf27840873a2246c064a594cf5ed1389d379 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Tue, 21 Jan 2025 15:41:22 +0100 Subject: [PATCH 05/14] [ResponseOps][Alerts] Fix optional eventId in security cell value renderers --- .../common/types/timeline/cells/index.ts | 2 +- .../preview_table_cell_renderer.tsx | 3 ++- .../render_cell_value.tsx | 22 +++++++++++-------- .../explore/network/components/port/index.tsx | 2 +- .../timelines/components/duration/index.tsx | 2 +- .../components/formatted_ip/index.tsx | 10 ++++----- .../renderers/asset_criticality_level.tsx | 2 +- .../timeline/body/renderers/bytes/index.tsx | 2 +- .../body/renderers/column_renderer.ts | 2 +- .../body/renderers/empty_column_renderer.tsx | 2 +- .../event_summary_column_renderer.tsx | 2 +- .../body/renderers/formatted_field.tsx | 2 +- .../renderers/formatted_field_helpers.tsx | 6 ++--- .../timeline/body/renderers/host_name.tsx | 2 +- .../body/renderers/plain_column_renderer.tsx | 2 +- .../body/renderers/reason_column_renderer.tsx | 2 +- .../timeline/body/renderers/rule_status.tsx | 2 +- .../timeline/body/renderers/service_name.tsx | 2 +- .../timeline/body/renderers/user_name.tsx | 2 +- .../body/renderers/user_profile_renderer.tsx | 2 +- 20 files changed, 39 insertions(+), 34 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/common/types/timeline/cells/index.ts b/x-pack/solutions/security/plugins/security_solution/common/types/timeline/cells/index.ts index 5575254ca886c..09164cddb30cf 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/types/timeline/cells/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/types/timeline/cells/index.ts @@ -21,7 +21,7 @@ export type CellValueElementProps = EuiDataGridCellValueElementProps & { browserFields?: BrowserFields; data: TimelineNonEcsData[]; ecsData?: Ecs; - eventId: string; // _id + eventId?: string; // _id header: ColumnHeaderOptions; isDraggable: boolean; isTimeline?: boolean; // Default cell renderer is used for both the alert table and timeline. This allows us to cheaply separate concerns diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx index 096346312e193..a9c5a6b904e66 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_table_cell_renderer.tsx @@ -32,7 +32,7 @@ export const PreviewRenderCellValue: React.FC< return ( ); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx index 857265b6c7d62..6656c1cccd84d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx @@ -28,6 +28,7 @@ import { VIEW_SELECTION } from '../../../../common/constants'; import { getAllFieldsByName } from '../../../common/containers/source'; import { eventRenderedViewColumns, getColumns } from './columns'; import type { GetSecurityAlertsTableProp } from '../../components/alerts_table/types'; +import type { CellValueElementProps, ColumnHeaderOptions } from '../../../../common/types'; /** * This implementation of `EuiDataGrid`'s `renderCellValue` @@ -51,13 +52,15 @@ type RenderCellValueProps = Pick< | 'colIndex' | 'setCellProps' | 'truncate' + | 'sourcererScope' + | 'userProfiles' > & - Record; + Partial>; export const CellValue = memo(function RenderCellValue({ columnId, rowIndex, - scopeId, + sourcererScope, tableId, tableType, header, @@ -73,7 +76,7 @@ export const CellValue = memo(function RenderCellValue({ eventId, setCellProps, truncate, - context, + userProfiles, }: RenderCellValueProps) { const isTourAnchor = useMemo( () => @@ -83,7 +86,7 @@ export const CellValue = memo(function RenderCellValue({ !isDetails, [columnId, isDetails, rowIndex, tableType] ); - const { browserFields } = useSourcererDataView(scopeId); + const { browserFields } = useSourcererDataView(sourcererScope); const browserFieldsByName = useMemo(() => getAllFieldsByName(browserFields), [browserFields]); const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); const license = useLicense(); @@ -134,7 +137,8 @@ export const CellValue = memo(function RenderCellValue({ }, [ecsAlert, legacyAlert]); const Renderer = useMemo(() => { - const myHeader = header ?? { id: columnId, ...browserFieldsByName[columnId] }; + const myHeader = + header ?? ({ id: columnId, ...browserFieldsByName[columnId] } as ColumnHeaderOptions); const colHeader = columnHeaders.find((col) => col.id === columnId); const localLinkValues = getOr([], colHeader?.linkField ?? '', ecsAlert); return ( @@ -159,10 +163,10 @@ export const CellValue = memo(function RenderCellValue({ colIndex={colIndex} rowRenderers={rowRenderers ?? defaultRowRenderers} setCellProps={setCellProps} - scopeId={scopeId} + scopeId={sourcererScope} truncate={truncate} asPlainText={false} - context={context} + context={userProfiles} /> ); @@ -185,9 +189,9 @@ export const CellValue = memo(function RenderCellValue({ colIndex, rowRenderers, setCellProps, - scopeId, + sourcererScope, truncate, - context, + userProfiles, ]); return columnId === SIGNAL_RULE_NAME_FIELD_NAME && actualSuppressionCount ? ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/port/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/port/index.tsx index 102143e54c959..6a71b2a74dfca 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/port/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/port/index.tsx @@ -15,7 +15,7 @@ import { PortOrServiceNameLink } from '../../../../common/components/links'; export const Port = React.memo<{ contextId: string; Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; - eventId: string; + eventId?: string; fieldName: string; fieldType?: string; isAggregatable?: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/duration/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/duration/index.tsx index cc4950c762401..0af54d9bf8fdb 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/duration/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/duration/index.tsx @@ -18,7 +18,7 @@ export const EVENT_DURATION_FIELD_NAME = 'event.duration'; */ export const Duration = React.memo<{ contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx index aac07fcc40ecf..8d695ee5b4e78 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx @@ -34,7 +34,7 @@ const getUniqueId = ({ address, }: { contextId: string; - eventId: string; + eventId?: string; fieldName: string; address: string | object | null | undefined; }) => `formatted-ip-data-provider-${contextId}-${fieldName}-${address}-${eventId}`; @@ -54,7 +54,7 @@ const getDataProvider = ({ address, }: { contextId: string; - eventId: string; + eventId?: string; fieldName: string; address: string | object | null | undefined; }): DataProvider => ({ @@ -73,7 +73,7 @@ const getDataProvider = ({ const NonDecoratedIpComponent: React.FC<{ contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; @@ -264,7 +264,7 @@ interface AddressLinksProps { addresses: string[]; Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; @@ -345,7 +345,7 @@ const AddressLinks = React.memo( const FormattedIpComponent: React.FC<{ Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/asset_criticality_level.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/asset_criticality_level.tsx index ce98498d9523f..dc277799076c2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/asset_criticality_level.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/asset_criticality_level.tsx @@ -14,7 +14,7 @@ import { DefaultDraggable } from '../../../../../common/components/draggables'; interface Props { contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx index 263a80d16307e..4e84434a19c57 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/bytes/index.tsx @@ -18,7 +18,7 @@ export const BYTES_FORMAT = 'bytes'; */ export const Bytes = React.memo<{ contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts index ae63ef972e565..45a0d7b300e5a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/column_renderer.ts @@ -39,7 +39,7 @@ export interface ColumnRenderer { className?: string; columnName: string; ecsData?: Ecs; - eventId: string; + eventId?: string; field: ColumnHeaderOptions; globalFilters?: Filter[]; isDetails?: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx index b5a527a65abb2..29aaa0c9c05e0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/empty_column_renderer.tsx @@ -34,7 +34,7 @@ export const emptyColumnRenderer: ColumnRenderer = { truncate, }: { columnName: string; - eventId: string; + eventId?: string; field: ColumnHeaderOptions; isDraggable?: boolean; scopeId: string; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/event_summary_column_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/event_summary_column_renderer.tsx index 42b7435e62406..8c662896ebce7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/event_summary_column_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/event_summary_column_renderer.tsx @@ -42,7 +42,7 @@ export const eventSummaryColumnRenderer: ColumnRenderer = { }: { columnName: string; ecsData?: Ecs; - eventId: string; + eventId?: string; field: ColumnHeaderOptions; isDetails?: boolean; isDraggable?: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index 379bf6c544fe3..4f6de85d32a65 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -66,7 +66,7 @@ const FormattedFieldValueComponent: React.FC<{ /** `Component` is only used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality */ Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; contextId: string; - eventId: string; + eventId?: string; isAggregatable?: boolean; isObjectArray?: boolean; isUnifiedDataTable?: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx index 057f108834ee6..23e81441341a4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx @@ -36,7 +36,7 @@ interface RenderRuleNameProps { children?: React.ReactNode; Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; @@ -233,7 +233,7 @@ export const renderEventModule = ({ value, }: { contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; @@ -339,7 +339,7 @@ export const renderUrl = ({ contextId: string; /** `Component` is only used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality */ Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx index b99f634d5a3cf..c6a2ccf4f277f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.tsx @@ -20,7 +20,7 @@ import { useIsInSecurityApp } from '../../../../../common/hooks/is_in_security_a interface Props { contextId: string; Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx index 5a23f4182355a..99f08dbb3f11f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.tsx @@ -34,7 +34,7 @@ export const plainColumnRenderer: ColumnRenderer = { }: { asPlainText?: boolean; columnName: string; - eventId: string; + eventId?: string; field: ColumnHeaderOptions; globalFilters?: Filter[]; isDraggable?: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx index 97e5c3fe5f033..29ea91f3fc272 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/reason_column_renderer.tsx @@ -35,7 +35,7 @@ export const reasonColumnRenderer: ColumnRenderer = { }: { columnName: string; ecsData?: Ecs; - eventId: string; + eventId?: string; field: ColumnHeaderOptions; isDetails?: boolean; isDraggable?: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx index a3a2bb17cdd43..65b215acc9643 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/rule_status.tsx @@ -25,7 +25,7 @@ const StyledEuiBadge = styled(EuiBadge)` interface BaseProps { contextId: string; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/service_name.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/service_name.tsx index 9832a21b6e8a3..de2fe8e728e48 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/service_name.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/service_name.tsx @@ -21,7 +21,7 @@ import { useIsInSecurityApp } from '../../../../../common/hooks/is_in_security_a interface Props { contextId: string; Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx index 27ae105632b94..3842430b0a3b1 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx @@ -20,7 +20,7 @@ import { useIsInSecurityApp } from '../../../../../common/hooks/is_in_security_a interface Props { contextId: string; Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; - eventId: string; + eventId?: string; fieldName: string; fieldType: string; isAggregatable: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx index e767685272356..e7ab1304176f1 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_profile_renderer.tsx @@ -33,7 +33,7 @@ export const userProfileColumnRenderer: ColumnRenderer = { }: { columnName: string; ecsData?: Ecs; - eventId: string; + eventId?: string; field: ColumnHeaderOptions; isDetails?: boolean; isDraggable?: boolean; From 87ff005562fa20c1163a63486361ca5114462890 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Thu, 23 Jan 2025 15:44:22 +0100 Subject: [PATCH 06/14] Fix documentation --- packages/response-ops/alerts_table/README.md | 3 ++- .../response-ops/alerts_table/components/alerts_table.tsx | 7 ++++--- .../alerts_table/components/cell_popover_host.tsx | 2 +- packages/response-ops/alerts_table/types.ts | 2 -- .../src/common/hooks/use_alerts_data_view.ts | 7 ++++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/response-ops/alerts_table/README.md b/packages/response-ops/alerts_table/README.md index 2d4ef0fa3f2eb..e507383166d37 100644 --- a/packages/response-ops/alerts_table/README.md +++ b/packages/response-ops/alerts_table/README.md @@ -11,7 +11,8 @@ The `id` prop is used to persist the table state in `localStorage`. ```tsx { const { rowIndex, DefaultCellPopover } = props; diff --git a/packages/response-ops/alerts_table/types.ts b/packages/response-ops/alerts_table/types.ts index f86b388438aa8..7d04983868e37 100644 --- a/packages/response-ops/alerts_table/types.ts +++ b/packages/response-ops/alerts_table/types.ts @@ -25,7 +25,6 @@ import { ALERT_CASE_IDS, ALERT_STATUS, ALERT_MAINTENANCE_WINDOW_IDS, - type ValidFeatureId, } from '@kbn/rule-data-utils'; import type { HttpStart } from '@kbn/core-http-browser'; import type { EsQuerySnapshot, LegacyField } from '@kbn/alerting-types'; @@ -437,7 +436,6 @@ export interface AlertsDataGridProps void; diff --git a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts b/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts index db76798f66112..b038a690d0aa7 100644 --- a/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts +++ b/src/platform/packages/shared/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts @@ -87,7 +87,8 @@ const resolveDataView = ({ * * @returns * A {@link DataViewBase} object, intentionally not typed as a complete {@link DataView} object - * since only Security Solution uses an actual in-memory data view (when `featureIds = ['siem']). + * since only Security Solution uses an actual in-memory data view (when `ruleTypeIds` only contains + * siem rule types). * In all other cases the data view is computed from the index names and fields fetched from the * alerting APIs. */ @@ -109,7 +110,7 @@ export const useAlertsDataView = ({ } = useFetchAlertsIndexNamesQuery( { http, ruleTypeIds }, { - // Don't fetch index names when featureIds includes both Security Solution and other features + // Don't fetch index names when ruleTypeIds includes both Security Solution and other features enabled: !!ruleTypeIds.length && (isOnlySecurity || !includesSecurity), } ); @@ -133,7 +134,7 @@ export const useAlertsDataView = ({ indexNames, }, { - // Create data view only when featureIds = ['siem'] and indexNames have been fetched + // Create data view only when ruleTypeIds only includes siem rules and indexNames have been fetched enabled: isOnlySecurity && !!indexNames?.length, } ); From 34f4ad5c11c2e53da963fceb706db0eaa3edc976 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Fri, 24 Jan 2025 12:30:20 +0100 Subject: [PATCH 07/14] Make the actions cell renderer a standalone memoized component --- .../components/actions_cell_host.test.tsx | 49 +++++++++++ .../components/actions_cell_host.tsx | 75 ++++++++++++++++ .../components/alerts_data_grid.tsx | 85 ++++++------------- .../components/alert_actions_cell.tsx | 14 ++- .../components/stack_alerts_page.tsx | 2 +- 5 files changed, 156 insertions(+), 69 deletions(-) create mode 100644 packages/response-ops/alerts_table/components/actions_cell_host.test.tsx create mode 100644 packages/response-ops/alerts_table/components/actions_cell_host.tsx rename {packages/response-ops/alerts_table => x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page}/components/alert_actions_cell.tsx (77%) diff --git a/packages/response-ops/alerts_table/components/actions_cell_host.test.tsx b/packages/response-ops/alerts_table/components/actions_cell_host.test.tsx new file mode 100644 index 0000000000000..6ab52ed9c19c2 --- /dev/null +++ b/packages/response-ops/alerts_table/components/actions_cell_host.test.tsx @@ -0,0 +1,49 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { ComponentProps, useEffect } from 'react'; +import { screen, render } from '@testing-library/react'; +import { ActionsCellHost } from './actions_cell_host'; +import { createPartialObjectMock } from '../utils/test'; +import { mockRenderContext } from '../mocks/context.mock'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +const props = createPartialObjectMock>({ + ...mockRenderContext, + rowIndex: 0, +}); + +describe('ActionsCellHost', () => { + it('should render the provided custom actions cell', () => { + render( + ( +
+ ))} + /> + ); + expect(screen.getByTestId('renderActionsCell')).toBeInTheDocument(); + }); + + it('should catch errors from the custom actions cell', async () => { + const CustomActionsCell = () => { + useEffect(() => { + throw new Error('test error'); + }, []); + return null; + }; + render( + + + + ); + expect(await screen.findByTestId('errorCell')).toBeInTheDocument(); + }); +}); diff --git a/packages/response-ops/alerts_table/components/actions_cell_host.tsx b/packages/response-ops/alerts_table/components/actions_cell_host.tsx new file mode 100644 index 0000000000000..131a4edc1be24 --- /dev/null +++ b/packages/response-ops/alerts_table/components/actions_cell_host.tsx @@ -0,0 +1,75 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { SetRequired } from 'type-fest'; +import React, { ComponentProps, useCallback } from 'react'; +import { EuiFlexGroup } from '@elastic/eui'; +import { typedMemo } from '../utils/react'; +import { AdditionalContext, AlertsTableProps, BulkActionsVerbs } from '../types'; +import { ErrorBoundary } from './error_boundary'; +import { ErrorCell } from './error_cell'; + +/** + * Entry point for rendering actions cells (in control columns) + */ +export const ActionsCellHost = typedMemo( + ( + // The actions control column is active only when the user provided the `renderActionsCell` prop + props: SetRequired< + ComponentProps['renderActionsCell']>>, + 'renderActionsCell' + > + ) => { + const { + rowIndex, + pageSize, + pageIndex, + alerts, + oldAlertsData, + ecsAlertsData, + bulkActionsStore, + renderActionsCell: ActionsCell, + visibleRowIndex, + } = props; + const idx = rowIndex - pageSize * pageIndex; + const alert = alerts[idx]; + const legacyAlert = oldAlertsData[idx]; + const ecsAlert = ecsAlertsData[idx]; + const [, updateBulkActionsState] = bulkActionsStore; + + const setIsActionLoading = useCallback( + (_isLoading: boolean = true) => { + updateBulkActionsState({ + action: BulkActionsVerbs.updateRowLoadingState, + rowIndex: visibleRowIndex, + isLoading: _isLoading, + }); + }, + [visibleRowIndex, updateBulkActionsState] + ); + + if (!alert) { + return null; + } + + return ( + + + ['renderActionsCell']>>)} + alert={alert} + legacyAlert={legacyAlert} + ecsAlert={ecsAlert} + setIsActionLoading={setIsActionLoading} + /> + + + ); + } +); diff --git a/packages/response-ops/alerts_table/components/alerts_data_grid.tsx b/packages/response-ops/alerts_table/components/alerts_data_grid.tsx index 1438d0b7020ff..846a366ef5eda 100644 --- a/packages/response-ops/alerts_table/components/alerts_data_grid.tsx +++ b/packages/response-ops/alerts_table/components/alerts_data_grid.tsx @@ -13,23 +13,18 @@ import { EuiDataGridControlColumn, EuiDataGridProps, EuiDataGridStyle, - EuiFlexGroup, RenderCellValue, tint, useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; +import { ActionsCellHost } from './actions_cell_host'; import { ControlColumnHeaderCell } from './control_column_header_cell'; import { CellValueHost } from './cell_value_host'; import { BulkActionsCell } from './bulk_actions_cell'; import { BulkActionsHeader } from './bulk_actions_header_cell'; -import { - AdditionalContext, - AlertsDataGridProps, - BulkActionsVerbs, - CellActionsOptions, -} from '../types'; +import { AdditionalContext, AlertsDataGridProps, CellActionsOptions } from '../types'; import { useGetToolbarVisibility } from '../hooks/use_toolbar_visibility'; import { InspectButtonContainer } from './alerts_query_inspector'; import { typedMemo } from '../utils/react'; @@ -41,19 +36,17 @@ import { NonVirtualizedGridBody } from './non_virtualized_grid_body'; const AlertsFlyout = lazy(() => import('./alerts_flyout')) as typeof AlertsFlyoutType; -const DefaultGridStyle: EuiDataGridStyle = { +const defaultGridStyle: EuiDataGridStyle = { border: 'none', header: 'underline', fontSize: 's', }; - const defaultCellActionsOptions: CellActionsOptions = { getCellActionsForColumn: () => [], disabledCellActions: [], }; const DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50, 100]; const DEFAULT_ACTIONS_COLUMN_WIDTH = 75; - const stableMappedRowClasses: EuiDataGridStyle['rowClasses'] = {}; export const AlertsDataGrid = typedMemo( @@ -99,7 +92,7 @@ export const AlertsDataGrid = typedMemo( alertsCount, isLoadingAlerts, browserFields, - renderActionsCell: ActionsCell, + renderActionsCell, pageIndex, pageSize, refresh: refreshQueries, @@ -116,7 +109,6 @@ export const AlertsDataGrid = typedMemo( bulkActions, setIsBulkActionsLoading, clearSelection, - updateBulkActionsState, } = useBulkActions({ ruleTypeIds, query, @@ -157,50 +149,6 @@ export const AlertsDataGrid = typedMemo( settings, }); - const customActionsColumn: EuiDataGridControlColumn | undefined = useMemo(() => { - if (ActionsCell) { - const RowCellRender: EuiDataGridControlColumn['rowCellRender'] = (_props) => { - const idx = _props.rowIndex - _props.pageSize * _props.pageIndex; - const alert = _props.alerts[idx]; - const legacyAlert = _props.oldAlertsData[idx]; - const ecsAlert = _props.ecsAlertsData[idx]; - const setIsActionLoading = useCallback( - (_isLoading: boolean = true) => { - updateBulkActionsState({ - action: BulkActionsVerbs.updateRowLoadingState, - rowIndex: _props.visibleRowIndex, - isLoading: _isLoading, - }); - }, - [_props.visibleRowIndex] - ); - - if (!alert) { - return null; - } - - return ( - - - - ); - }; - return { - id: 'expandColumn', - width: actionsColumnWidth, - headerCellRender: ControlColumnHeaderCell, - rowCellRender: RowCellRender, - }; - } - }, [ActionsCell, actionsColumnWidth, updateBulkActionsState]); - const leadingControlColumns: EuiDataGridControlColumn[] | undefined = useMemo(() => { const controlColumns = [ ...(additionalLeadingControlColumns ?? []), @@ -214,12 +162,29 @@ export const AlertsDataGrid = typedMemo( }, ] : []), - ...(customActionsColumn ? [customActionsColumn] : []), + // If the user provided an actions cell renderer, add the actions column + ...(renderActionsCell + ? [ + { + id: 'expandColumn', + width: actionsColumnWidth, + headerCellRender: ControlColumnHeaderCell, + // Though untyped, rowCellRender's CellPropsWithContext contains the correct context + rowCellRender: + ActionsCellHost as unknown as EuiDataGridControlColumn['rowCellRender'], + }, + ] + : []), ]; if (controlColumns.length) { return controlColumns; } - }, [additionalLeadingControlColumns, isBulkActionsColumnActive, customActionsColumn]); + }, [ + additionalLeadingControlColumns, + isBulkActionsColumnActive, + renderActionsCell, + actionsColumnWidth, + ]); const flyoutRowIndex = flyoutAlertIndex + pageIndex * pageSize; @@ -285,7 +250,7 @@ export const AlertsDataGrid = typedMemo( const propGridStyle: NonNullable = props.gridStyle ?? {}; // Merges default row classes, custom ones and adds the active row class style return { - ...DefaultGridStyle, + ...defaultGridStyle, ...propGridStyle, rowClasses: { // We're spreading the highlighted row classes first, so that the active @@ -400,7 +365,7 @@ export const AlertsDataGrid = typedMemo( rowCount={alertsCount} renderCustomGridBody={dynamicRowHeight ? renderCustomGridBody : undefined} cellContext={renderContext} - // Cast necessary because the `cellContext` type is too wide in EuiDataGrid + // Cast necessary because `cellContext` is untyped in EuiDataGrid renderCellValue={CellValueHost as RenderCellValue} renderCellPopover={CellPopoverHost} gridStyle={actualGridStyle} diff --git a/packages/response-ops/alerts_table/components/alert_actions_cell.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/alert_actions_cell.tsx similarity index 77% rename from packages/response-ops/alerts_table/components/alert_actions_cell.tsx rename to x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/alert_actions_cell.tsx index b76dec514f306..abd5b088aac6a 100644 --- a/packages/response-ops/alerts_table/components/alert_actions_cell.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/alert_actions_cell.tsx @@ -1,10 +1,8 @@ /* * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". + * 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 { @@ -17,15 +15,15 @@ import { import React, { useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { DefaultAlertActions } from './default_alert_actions'; -import type { GetAlertsTableProp } from '../types'; +import { DefaultAlertActions } from '@kbn/response-ops-alerts-table/components/default_alert_actions'; +import type { GetAlertsTableProp } from '@kbn/response-ops-alerts-table/types'; const actionsToolTip = i18n.translate('xpack.triggersActionsUI.alertsTable.moreActionsTextLabel', { defaultMessage: 'More actions', }); /** - * The cell containing contextual actions for a single alert row in the table + * Cell containing contextual actions for a single alert row in the table */ export const AlertActionsCell: GetAlertsTableProp<'renderActionsCell'> = (props) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx index ff8b0ed6f9203..45471aaf29d7d 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/alerts_page/components/stack_alerts_page.tsx @@ -24,7 +24,7 @@ import { alertProducersData } from '@kbn/response-ops-alerts-table/constants'; import { alertsTableQueryClient } from '@kbn/response-ops-alerts-table/query_client'; import { defaultAlertsTableSort } from '@kbn/response-ops-alerts-table/configuration'; import { AlertsTableSupportedConsumers } from '@kbn/response-ops-alerts-table/types'; -import { AlertActionsCell } from '@kbn/response-ops-alerts-table/components/alert_actions_cell'; +import { AlertActionsCell } from './alert_actions_cell'; import { ALERTS_PAGE_ID } from '../../../../common/constants'; import { QuickFiltersMenuItem } from '../../alerts_search_bar/quick_filters'; import { NoPermissionPrompt } from '../../../components/prompts/no_permission_prompt'; From 214a66b96bdcd10aba8b79851b1c92c6b7461f4c Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Fri, 24 Jan 2025 12:27:01 +0100 Subject: [PATCH 08/14] Memoize toolbarVisibility.columnIds --- .../response-ops/alerts_table/components/alerts_data_grid.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/response-ops/alerts_table/components/alerts_data_grid.tsx b/packages/response-ops/alerts_table/components/alerts_data_grid.tsx index 846a366ef5eda..0b8a107555214 100644 --- a/packages/response-ops/alerts_table/components/alerts_data_grid.tsx +++ b/packages/response-ops/alerts_table/components/alerts_data_grid.tsx @@ -128,13 +128,15 @@ export const AlertsDataGrid = typedMemo( clearSelection(); }, [clearSelection, refreshQueries]); + const columnIds = useMemo(() => columns.map((column) => column.id), [columns]); + const toolbarVisibility = useGetToolbarVisibility({ bulkActions, alertsCount, rowSelection: bulkActionsState.rowSelection, alerts, isLoading, - columnIds: columns.map((column) => column.id), + columnIds, onToggleColumn, onResetColumns, browserFields, From 4f4e327cb490cb11754cd4d2377685e8c3f9c415 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 27 Jan 2025 14:58:00 +0100 Subject: [PATCH 09/14] [ResponseOps][Alerts] Fix alerts table column toggling not working from security flyout (#208052) ## Summary Fixes the toggleColumn functionality not working from the Security Solution flyout. Uses a global context to share a reference to the `toggleColumn` function as a **temporary solution** until the handling of the columns inside the alerts table is refactored to correctly receive updates from the outside. --- .../cell_action/toggle_column.test.ts | 18 ++++---- .../cell_action/toggle_column.ts | 7 +++ .../public/app/actions/types.ts | 7 +++ .../app/home/template_wrapper/index.tsx | 11 +++-- .../alerts_table/alerts_context.tsx | 44 +++++++++++++++++++ .../components/alerts_table/index.tsx | 14 ++---- .../render_cell_value.test.tsx | 6 ++- .../shared/components/cell_actions.tsx | 7 ++- 8 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/alerts_context.tsx diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.test.ts b/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.test.ts index 72c7f3e523737..58e06a1b6e6bf 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.test.ts @@ -12,13 +12,10 @@ import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { createToggleColumnCellActionFactory } from './toggle_column'; import { mockGlobalState } from '../../../../common/mock'; import { createStartServicesMock } from '../../../../common/lib/kibana/kibana_react.mock'; +import type { AlertsTableImperativeApi } from '@kbn/response-ops-alerts-table/types'; const services = createStartServicesMock(); -const mockAlertConfigGetActions = jest.fn(); const mockToggleColumn = jest.fn(); -mockAlertConfigGetActions.mockImplementation(() => ({ - toggleColumn: mockToggleColumn, -})); const mockDispatch = jest.fn(); const mockGetState = jest.fn().mockReturnValue(mockGlobalState); @@ -81,7 +78,6 @@ describe('createToggleColumnCellActionFactory', () => { describe('execute', () => { afterEach(() => { mockToggleColumn.mockClear(); - mockAlertConfigGetActions.mockClear(); }); it('should remove column', async () => { await toggleColumnAction.execute(context); @@ -112,27 +108,31 @@ describe('createToggleColumnCellActionFactory', () => { ); }); - it('should call triggersActionsUi.alertsTableConfigurationRegistry to add a column in alert', async () => { + it('should call toggleColumn on the visible alerts table to add a column in alert', async () => { const name = 'fake-field-name'; await toggleColumnAction.execute({ ...context, data: [{ ...context.data[0], field: { ...context.data[0].field, name } }], metadata: { scopeId: TableId.alertsOnAlertsPage, + alertsTableRef: { + current: { toggleColumn: mockToggleColumn } as unknown as AlertsTableImperativeApi, + }, }, }); - expect(mockAlertConfigGetActions).toHaveBeenCalledWith('securitySolution-alerts-page'); expect(mockToggleColumn).toHaveBeenCalledWith(name); }); - it('should call triggersActionsUi.alertsTableConfigurationRegistry to remove a column in alert', async () => { + it('should call toggleColumn on the visible alerts table to remove a column in alert', async () => { await toggleColumnAction.execute({ ...context, metadata: { scopeId: TableId.alertsOnAlertsPage, + alertsTableRef: { + current: { toggleColumn: mockToggleColumn } as unknown as AlertsTableImperativeApi, + }, }, }); - expect(mockAlertConfigGetActions).toHaveBeenCalledWith('securitySolution-alerts-page'); expect(mockToggleColumn).toHaveBeenCalledWith(fieldName); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.ts b/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.ts index 4d6639b0ad16a..b41866fded5b6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/actions/toggle_column/cell_action/toggle_column.ts @@ -11,6 +11,7 @@ import { defaultColumnHeaderType, tableDefaults, dataTableSelectors, + TableId, } from '@kbn/securitysolution-data-table'; import { fieldHasCellActions } from '../../utils'; import type { SecurityAppStore } from '../../../../common/store'; @@ -67,6 +68,12 @@ export const createToggleColumnCellActionFactory = createCellActionFactory( return; } + // When the flyout was initiated from an alerts table, use its toggleColumn action + if (metadata.alertsTableRef?.current && scopeId === TableId.alertsOnAlertsPage) { + metadata.alertsTableRef.current.toggleColumn(field.name); + return; + } + const selector = isTimelineScope(scopeId) ? timelineSelectors.getTimelineByIdSelector() : dataTableSelectors.getTableByIdSelector(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/actions/types.ts b/x-pack/solutions/security/plugins/security_solution/public/app/actions/types.ts index 999bc9ed99c0f..41d4b1f4ef0ec 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/actions/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/actions/types.ts @@ -6,6 +6,8 @@ */ import type { CellAction, CellActionExecutionContext, CellActionFactory } from '@kbn/cell-actions'; +import type { RefObject } from 'react'; +import type { AlertsTableImperativeApi } from '@kbn/response-ops-alerts-table/types'; import type { QueryOperator } from '../../../common/types'; export { EsqlInTimelineTrigger, EsqlInTimelineAction } from './constants'; export interface AndFilter { @@ -49,6 +51,11 @@ export interface SecurityCellActionMetadata extends Record { andFilters?: AndFilter[]; dataViewId?: string; + + /** + * Ref to the currently visible alerts table + */ + alertsTableRef?: RefObject; } export interface SecurityCellActionExecutionContext extends CellActionExecutionContext { diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/app/home/template_wrapper/index.tsx index f547d128ab54b..c595589e2fa27 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -12,6 +12,7 @@ import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout'; +import { AlertsContextProvider } from '../../../detections/components/alerts_table/alerts_context'; import { URL_PARAM_KEY } from '../../../common/hooks/use_url_state'; import { SecuritySolutionFlyout, TimelineFlyout } from '../../../flyout'; import { useSecuritySolutionNavigation } from '../../../common/components/navigation/use_security_solution_navigation'; @@ -98,10 +99,12 @@ export const SecuritySolutionTemplateWrapper: React.FC - - {children} - - + + + {children} + + + {isTimelineBottomBarVisible && ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/alerts_context.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/alerts_context.tsx new file mode 100644 index 0000000000000..0c3e5bdc30406 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/alerts_context.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { + createContext, + memo, + useContext, + useRef, + type RefObject, + type PropsWithChildren, +} from 'react'; +import type { AlertsTableImperativeApi } from '@kbn/response-ops-alerts-table/types'; + +/** + * Temporary context to share imperative APIs between the alerts table and other higher level + * components such as the alerts details flyout + * + * TODO remove once the alerts table columns are controllable from the outside + */ +const AlertsContext = createContext<{ + alertsTableRef: RefObject; +} | null>(null); + +const AlertsContextProviderComponent = ({ children }: PropsWithChildren) => { + const alertsTableRef = useRef(null); + return {children}; +}; + +export const AlertsContextProvider = memo(AlertsContextProviderComponent); + +export const useAlertsContext = () => { + const fallbackRef = useRef(null); + const value = useContext(AlertsContext); + if (!value) { + return { + alertsTableRef: fallbackRef, + }; + } + return value; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 60dbd8d5fb7c9..c9b22fc61d035 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -5,15 +5,11 @@ * 2.0. */ -import React, { useRef, useEffect, useState, useCallback, useMemo, type FC } from 'react'; +import React, { useEffect, useState, useCallback, useMemo, type FC } from 'react'; import type { EuiDataGridRowHeightsOptions, EuiDataGridStyle } from '@elastic/eui'; import { EuiFlexGroup } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; -import type { - AlertsTableImperativeApi, - AlertsTableProps, - RenderContext, -} from '@kbn/response-ops-alerts-table/types'; +import type { AlertsTableProps, RenderContext } from '@kbn/response-ops-alerts-table/types'; import { ALERT_BUILDING_BLOCK_TYPE, AlertConsumers } from '@kbn/rule-data-utils'; import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; import styled from 'styled-components'; @@ -29,6 +25,7 @@ import type { SetOptional } from 'type-fest'; import { noop } from 'lodash'; import type { Alert } from '@kbn/alerting-types'; import { AlertsTable } from '@kbn/response-ops-alerts-table'; +import { useAlertsContext } from './alerts_context'; import { getBulkActionsByTableType } from '../../hooks/trigger_actions_alert_table/use_bulk_actions'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import type { @@ -169,11 +166,10 @@ export const AlertsTableComponent: FC( ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING ); + const { alertsTableRef } = useAlertsContext(); const { from, to, setQuery } = useGlobalTime(); - const alertTableRefreshHandlerRef = useRef<(() => void) | null>(null); - const dispatch = useDispatch(); // Store context in state rather than creating object in provider value={} to prevent re-renders caused by a new object being created @@ -293,7 +289,6 @@ export const AlertsTableComponent: FC { onLoad(context.alerts); setTableContext(context); - alertTableRefreshHandlerRef.current = context.refresh; dispatch( updateIsLoading({ id: tableType, @@ -372,7 +367,6 @@ export const AlertsTableComponent: FC(null); const fieldsBrowserOptions = useAlertsTableFieldsBrowserOptions( SourcererScopeName.detections, alertsTableRef.current?.toggleColumn diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx index 5c8759bb32b52..b7934c906a52b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx @@ -73,7 +73,11 @@ describe('RenderCellValue', () => { const wrapper = mount( - + ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/cell_actions.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/cell_actions.tsx index 8e87dd6583fd3..97503cced4281 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/cell_actions.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/cell_actions.tsx @@ -7,6 +7,7 @@ import type { FC } from 'react'; import React, { useMemo } from 'react'; +import { useAlertsContext } from '../../../../detections/components/alerts_table/alerts_context'; import { useDocumentDetailsContext } from '../context'; import { getSourcererScopeId } from '../../../../helpers'; import { SecurityCellActionType } from '../../../../app/actions/constants'; @@ -40,9 +41,13 @@ interface CellActionsProps { */ export const CellActions: FC = ({ field, value, isObjectArray, children }) => { const { scopeId, isPreview } = useDocumentDetailsContext(); + const { alertsTableRef } = useAlertsContext(); const data = useMemo(() => ({ field, value }), [field, value]); - const metadata = useMemo(() => ({ scopeId, isObjectArray }), [scopeId, isObjectArray]); + const metadata = useMemo( + () => ({ scopeId, isObjectArray, alertsTableRef }), + [scopeId, isObjectArray, alertsTableRef] + ); const disabledActionTypes = useMemo( () => (isPreview ? [SecurityCellActionType.FILTER, SecurityCellActionType.TOGGLE_COLUMN] : []), [isPreview] From 84450b78c7bd687a7f727d5f6bde9764343fc486 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 27 Jan 2025 23:26:39 +0100 Subject: [PATCH 10/14] Fix tests --- .../alerts_preview/index.test.tsx | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx index c07192df54872..da4d620895e67 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx @@ -5,14 +5,17 @@ * 2.0. */ -import { AlertConsumers } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; -import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; import { render } from '@testing-library/react'; import React from 'react'; import * as uuid from 'uuid'; import { AlertsPreview } from '.'; -import { useKibana } from '../../../../common/lib/kibana'; +import { TableId } from '@kbn/securitysolution-data-table'; +import { AlertsTableComponent } from '../../../../detections/components/alerts_table'; + +jest.mock('../../../../detections/components/alerts_table'); + +jest.mocked(AlertsTableComponent).mockReturnValue(
{'Mocked Alerts Table'}
); jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('mocked-uuid'), @@ -28,17 +31,6 @@ jest.mock('../../../../common/lib/kibana', () => ({ get: jest.fn(), list: jest.fn(), }, - alertsTableConfigurationRegistry: { - objectTypes: {}, - has: jest.fn(), - register: jest.fn(), - get: jest.fn(), - getActions: jest.fn(), - list: jest.fn(), - update: jest.fn(), - getAlertConfigIdPerRuleTypes: jest.fn(), - }, - getAlertsStateTable: jest.fn().mockReturnValue(
{'Mocked Alerts Table'}
), ruleTypeRegistry: { has: jest.fn(), register: jest.fn(), @@ -60,22 +52,21 @@ describe('AlertsPreview', () => { expect(getByTestId('alertsPreview')).toBeInTheDocument(); }); - it('invokes getAlertsStateTable with the expected props', () => { + it('renders the alerts table component with the expected props', () => { const query = { bool: {} }; const size = 10; render(); - expect(useKibana().services.triggersActionsUi.getAlertsStateTable).toHaveBeenCalledWith({ - alertsTableConfigurationRegistry: - useKibana().services.triggersActionsUi.alertsTableConfigurationRegistry, - configurationId: 'securitySolution-rule-details', - consumers: [AlertConsumers.SIEM], - id: `attack-discovery-alerts-preview-${uuid.v4()}`, - initialPageSize: size, - query, - ruleTypeIds: SECURITY_SOLUTION_RULE_TYPE_IDS, - showAlertStatusWithFlapping: false, - }); + expect(AlertsTableComponent).toHaveBeenCalledWith( + { + id: `attack-discovery-alerts-preview-${uuid.v4()}`, + tableType: TableId.alertsOnRuleDetailsPage, + initialPageSize: size, + query, + showAlertStatusWithFlapping: false, + }, + expect.anything() + ); }); }); From bcc025b2126adc48d44e6312491f0e76f5275f70 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Wed, 29 Jan 2025 12:22:13 +0100 Subject: [PATCH 11/14] Show column id when column dislpay name is not available in flyout overview --- .../components/default_alerts_flyout.tsx | 2 +- .../components/default_cell_value.tsx | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/response-ops/alerts_table/components/default_alerts_flyout.tsx b/packages/response-ops/alerts_table/components/default_alerts_flyout.tsx index 43eefb7a88303..77c3cc66733fc 100644 --- a/packages/response-ops/alerts_table/components/default_alerts_flyout.tsx +++ b/packages/response-ops/alerts_table/components/default_alerts_flyout.tsx @@ -46,7 +46,7 @@ export const DefaultAlertsFlyoutBody = ( const value = alert[column.id]?.[0]; return { - title: column.displayAsText as string, + title: (column.displayAsText as string) ?? column.id, description: value != null ? ( diff --git a/packages/response-ops/alerts_table/components/default_cell_value.tsx b/packages/response-ops/alerts_table/components/default_cell_value.tsx index 11d4705306a7a..97bcdc88a8adb 100644 --- a/packages/response-ops/alerts_table/components/default_cell_value.tsx +++ b/packages/response-ops/alerts_table/components/default_cell_value.tsx @@ -20,6 +20,7 @@ import { ALERT_RULE_PRODUCER, } from '@kbn/rule-data-utils'; import { EuiBadge, EuiLink } from '@elastic/eui'; +import { JsonValue } from '@kbn/utility-types'; import { AlertsTableSupportedConsumers, GetAlertsTableProp } from '../types'; import { alertProducersData, observabilityFeatureIds } from '../constants'; import { useFieldFormatter } from '../hooks/use_field_formatter'; @@ -33,8 +34,8 @@ export const DefaultCellValue = ({ services: { fieldFormats, http }, } = useAlertsTableContext(); const formatField = useFieldFormatter(fieldFormats); - const rawValue = alert[columnId]?.[0]; - const value = getRenderValue(rawValue); + const rawValue = alert[columnId]; + const value = extractFieldValue(rawValue); switch (columnId) { case TIMESTAMP: @@ -72,11 +73,13 @@ export const DefaultCellValue = ({ case ALERT_RULE_CONSUMER: const producer = alert?.[ALERT_RULE_PRODUCER]?.[0] as AlertConsumers; - const consumer: AlertsTableSupportedConsumers = observabilityFeatureIds.includes(producer) - ? 'observability' - : producer && (value === 'alerts' || value === 'stackAlerts' || value === 'discover') - ? producer - : value; + const consumer = ( + observabilityFeatureIds.includes(producer) + ? 'observability' + : producer && (value === 'alerts' || value === 'stackAlerts' || value === 'discover') + ? producer + : value + ) as AlertsTableSupportedConsumers; const consumerData = alertProducersData[consumer]; if (!consumerData) { return <>{value}; @@ -88,8 +91,11 @@ export const DefaultCellValue = ({ } }; -const getRenderValue = (mappedNonEcsValue: any) => { - const value = Array.isArray(mappedNonEcsValue) ? mappedNonEcsValue.join() : mappedNonEcsValue; +/** + * Extracts the value from the raw json ES field + */ +const extractFieldValue = (rawValue: string | JsonValue[]) => { + const value = Array.isArray(rawValue) ? rawValue.join() : rawValue; if (!isEmpty(value)) { if (typeof value === 'object') { From cced19ef385e67a923091640d764f18316c6d16b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 29 Jan 2025 11:42:38 +0000 Subject: [PATCH 12/14] [CI] Auto-commit changed files from 'node scripts/notice' --- packages/response-ops/alerts_table/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/response-ops/alerts_table/tsconfig.json b/packages/response-ops/alerts_table/tsconfig.json index 99d9f4e260b86..0d167685f0d04 100644 --- a/packages/response-ops/alerts_table/tsconfig.json +++ b/packages/response-ops/alerts_table/tsconfig.json @@ -46,5 +46,6 @@ "@kbn/core-ui-settings-browser", "@kbn/core-user-profile-browser-mocks", "@kbn/utility-types-jest", + "@kbn/utility-types", ] } From b11c7b90e71c0f8789374643187ff437eeee1790 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Thu, 6 Feb 2025 12:37:30 +0100 Subject: [PATCH 13/14] Fix Security alerts table regression and perf --- .../tabs/alerts_tab/index.tsx | 4 +- .../alerts_preview/index.test.tsx | 6 +-- .../settings_flyout/alerts_preview/index.tsx | 4 +- .../public/cases/pages/index.tsx | 4 +- .../pages/rule_details/index.tsx | 4 +- .../components/alerts_table/actions_cell.tsx | 40 ++++++++++++------- .../components/alerts_table/index.tsx | 30 ++++++++------ .../detection_engine/detection_engine.tsx | 4 +- .../index.tsx | 4 +- 9 files changed, 58 insertions(+), 42 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx index b3ed53b023098..a848a04bb6317 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/results/attack_discovery_panel/tabs/alerts_tab/index.tsx @@ -11,7 +11,7 @@ import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules'; import { TableId } from '@kbn/securitysolution-data-table'; import { AlertConsumers } from '@kbn/rule-data-utils'; -import { AlertsTableComponent } from '../../../../../../detections/components/alerts_table'; +import { DetectionEngineAlertsTable } from '../../../../../../detections/components/alerts_table'; interface Props { attackDiscovery: AttackDiscovery; @@ -38,7 +38,7 @@ const AlertsTabComponent: React.FC = ({ attackDiscovery, replacements }) return (
- {'Mocked Alerts Table'}
); +jest.mocked(DetectionEngineAlertsTable).mockReturnValue(
{'Mocked Alerts Table'}
); jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('mocked-uuid'), @@ -58,7 +58,7 @@ describe('AlertsPreview', () => { render(); - expect(AlertsTableComponent).toHaveBeenCalledWith( + expect(DetectionEngineAlertsTable).toHaveBeenCalledWith( { id: `attack-discovery-alerts-preview-${uuid.v4()}`, tableType: TableId.alertsOnRuleDetailsPage, diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.tsx index abcf2c207ef82..2b78174783771 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.tsx @@ -11,7 +11,7 @@ import React from 'react'; import * as uuid from 'uuid'; import { TableId } from '@kbn/securitysolution-data-table'; -import { AlertsTableComponent } from '../../../../detections/components/alerts_table'; +import { DetectionEngineAlertsTable } from '../../../../detections/components/alerts_table'; interface Props { query: Pick; @@ -26,7 +26,7 @@ const AlertsPreviewComponent: React.FC = ({ query, size }) => { `} data-test-subj="alertsPreview" > - { useFetchAlertData, onAlertsTableLoaded, permissions: userCasesPermissions, - renderAlertsTable: (props) => , + renderAlertsTable: (props) => , })} diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx index 557c8d4de3455..5a5fa5e10795a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx @@ -40,7 +40,7 @@ import { } from '@kbn/securitysolution-data-table'; import type { RunTimeMappings } from '@kbn/timelines-plugin/common/search_strategy'; import { EndpointExceptionsViewer } from '../../../endpoint_exceptions/endpoint_exceptions_viewer'; -import { AlertsTableComponent } from '../../../../detections/components/alerts_table'; +import { DetectionEngineAlertsTable } from '../../../../detections/components/alerts_table'; import { GroupedAlertsTable } from '../../../../detections/components/alerts_table/alerts_grouping'; import { useDataTableFilters } from '../../../../common/hooks/use_data_table_filters'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; @@ -503,7 +503,7 @@ const RuleDetailsPageComponent: React.FC = ({ const renderGroupedAlertTable = useCallback( (groupingFilters: Filter[]) => { return ( - eventsViewerSelector(state, tableType)); const eventContext = useContext(StatefulEventContext); - const timelineItem: TimelineItem = { - _id: (alert as Ecs)._id, - _index: (alert as Ecs)._index, - ecs: alert as Ecs, - data: legacyAlert as TimelineItem['data'], - }; + const timelineItem = useMemo( + () => ({ + _id: (alert as Ecs)._id, + _index: (alert as Ecs)._index, + ecs: alert as Ecs, + data: legacyAlert as TimelineItem['data'], + }), + [alert, legacyAlert] + ); + + const setEventsLoading = useCallback( + ({ isLoading }) => { + if (!isLoading) { + clearSelection(); + return; + } + if (setIsActionLoading) setIsActionLoading(isLoading); + }, + [clearSelection, setIsActionLoading] + ); return ( { - if (!isLoading) { - clearSelection(); - return; - } - if (setIsActionLoading) setIsActionLoading(isLoading); - }} - setEventsDeleted={() => {}} + setEventsLoading={setEventsLoading} + setEventsDeleted={noop} refetch={alertsTableRefresh} /> ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx index c9b22fc61d035..5bafb6b17b8df 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useState, useCallback, useMemo, type FC } from 'react'; +import React, { useEffect, useState, useCallback, useMemo, memo, type FC } from 'react'; import type { EuiDataGridRowHeightsOptions, EuiDataGridStyle } from '@elastic/eui'; import { EuiFlexGroup } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; @@ -152,7 +152,7 @@ const initialSort: GetSecurityAlertsTableProp<'initialSort'> = [ const casesConfiguration = { featureId: CASES_FEATURE_ID, owner: [APP_ID], syncAlerts: true }; const emptyInputFilters: Filter[] = []; -export const AlertsTableComponent: FC> = ({ +const DetectionEngineAlertsTableComponent: FC> = ({ inputFilters = emptyInputFilters, tableType = TableId.alertsOnAlertsPage, sourcererScope = SourcererScopeName.detections, @@ -412,6 +412,19 @@ export const AlertsTableComponent: FC ({ + data, + http, + notifications, + fieldFormats, + application, + licensing, + settings, + }), + [application, data, fieldFormats, http, licensing, notifications, settings] + ); + if (isLoading) { return null; } @@ -445,7 +458,6 @@ export const AlertsTableComponent: FC @@ -478,3 +482,5 @@ export const AlertsTableComponent: FC ); }; + +export const DetectionEngineAlertsTable = memo(DetectionEngineAlertsTableComponent); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index b0075e926207a..75a5e7eb0bc67 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -83,7 +83,7 @@ import { HeaderPage } from '../../../common/components/header_page'; import { EmptyPrompt } from '../../../common/components/empty_prompt'; import type { Status } from '../../../../common/api/detection_engine'; import { GroupedAlertsTable } from '../../components/alerts_table/alerts_grouping'; -import { AlertsTableComponent } from '../../components/alerts_table'; +import { DetectionEngineAlertsTable } from '../../components/alerts_table'; import type { AddFilterProps } from '../../components/alerts_kpis/common/types'; /** @@ -320,7 +320,7 @@ const DetectionEnginePageComponent: React.FC = () const renderAlertTable = useCallback( (groupingFilters: Filter[]) => { return ( - ({ const renderGroupedAlertTable = useCallback( (groupingFilters: Filter[]) => { return ( - From bd28e289a9f26b0ea966527e02518c116f912ba3 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Thu, 6 Feb 2025 14:36:55 +0100 Subject: [PATCH 14/14] Fix test --- .../pages/settings_flyout/alerts_preview/index.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx index f607be72b20e9..a8d6d0521e5c0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/attack_discovery/pages/settings_flyout/alerts_preview/index.test.tsx @@ -13,9 +13,9 @@ import { AlertsPreview } from '.'; import { TableId } from '@kbn/securitysolution-data-table'; import { DetectionEngineAlertsTable } from '../../../../detections/components/alerts_table'; -jest.mock('../../../../detections/components/alerts_table'); - -jest.mocked(DetectionEngineAlertsTable).mockReturnValue(
{'Mocked Alerts Table'}
); +jest.mock('../../../../detections/components/alerts_table', () => ({ + DetectionEngineAlertsTable: jest.fn().mockReturnValue(
{'Mocked Alerts Table'}
), +})); jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('mocked-uuid'),