From abe58cb0113e7c58f3bab3ab7d297e65ca01c0d9 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 5 Jul 2023 10:30:28 +0200 Subject: [PATCH 01/89] [Logs Shared] Move LogStream and LogView into new shared plugin (#161151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📓 Summary Closes #159128 Due to a dependencies issue when disabling a plugin in serverless mode, the LogStream feature and related logic were disabled for every consumer. We decided to split this shared component and endpoint into their own plugin of shared logs utilities, reducing to the minimum the required dependency that could disable the plugin. What we moved can be summarized with: - `infrastructure-monitoring-log-view` saved object definition and registration - LogViews server/client services (exposed with start contract) + related endpoints - LogEntries server service + related endpoints - LogEntriesDomain logic (exposed with start contract) - `` component - `` component and related logic - LogView state machine - Containers/Hooks to consume the moved APIs. - Common types/utils definition, now exported and consumed as a dependency from the `infra` plugin. ## 🤓 Review hints Most of the changes are just renaming and moving stuff into the new plugin, but for some operations was required to implement new logic, which may deserve a more critical review: - server/public `plugin.ts` files for the `infra` and `logs_shared` plugins. The new plugin now registers the fallback actions to retrieve a source configuration if there's no stored log view. It also set the configuration for the message field and registers the log view saved object. - the `logEntriesDomain` has also been moved inside the new plugin, but is also used by the logs-analysis endpoints, so it is exposed by the logs_shared plugin and consumed by `infra`. ## 👣 Following steps We currently are still using the `observability` plugin for consuming the CoPilot feature on our LogsStream flyout. The plugin dependency is marked as optional, so disabling the `observability` plugin in a serverless environment won't disable also the exposed features in this new plugin, but it'll affect only the CoPilot feature, which won't be loaded. In future, would be nice to extract the CoPilot feature into its own package/plugin, so that also serverless projects can consume it without depending on `observability. --------- Co-authored-by: Marco Antonio Ghiani Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 1 + docs/developer/plugin-list.asciidoc | 4 + package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 + x-pack/.i18nrc.json | 1 + x-pack/plugins/apm/kibana.jsonc | 1 + .../components/app/service_logs/index.tsx | 2 +- .../transaction_tabs.tsx | 2 +- x-pack/plugins/apm/tsconfig.json | 1 + .../common/types/kibana_deps.ts | 2 - x-pack/plugins/enterprise_search/kibana.jsonc | 2 +- .../shared/log_stream/log_stream.tsx | 8 +- .../enterprise_search/server/plugin.ts | 14 +- .../plugins/enterprise_search/tsconfig.json | 2 +- x-pack/plugins/fleet/kibana.jsonc | 2 +- .../components/agent_logs/agent_logs.tsx | 2 +- x-pack/plugins/fleet/tsconfig.json | 2 +- .../alerting/logs/log_threshold/types.ts | 2 +- x-pack/plugins/infra/common/http_api/index.ts | 2 - .../plugins/infra/common/http_api/latest.ts | 2 - .../log_alerts/v1/chart_preview_data.ts | 2 +- .../results/v1/log_entry_anomalies.ts | 2 +- .../v1/log_entry_anomalies_datasets.ts | 2 +- .../results/v1/log_entry_categories.ts | 2 +- .../results/v1/log_entry_category_datasets.ts | 2 +- .../results/v1/log_entry_category_examples.ts | 6 +- .../results/v1/log_entry_examples.ts | 2 +- .../plugins/infra/common/locators/helpers.ts | 24 +- .../infra/common/locators/locators.test.ts | 2 +- .../infra/common/locators/logs_locator.ts | 2 +- .../log_entries/log_entries.ts | 7 +- .../log_entries/log_entry.ts | 7 +- .../url_state_storage_service.ts | 8 +- x-pack/plugins/infra/kibana.jsonc | 1 + .../components/explain_log_rate_spike.tsx | 9 +- .../alert_details_app_section/index.tsx | 6 +- .../components/alert_dropdown.tsx | 2 +- .../components/expression_editor/criteria.tsx | 2 +- .../expression_editor/criterion.tsx | 2 +- .../criterion_preview_chart.tsx | 2 +- .../components/expression_editor/editor.tsx | 8 +- .../hooks/use_chart_preview_data.tsx | 2 +- .../expression_editor/log_view_switcher.tsx | 2 +- .../infra/public/apps/discover_app.tsx | 2 +- .../context/fixtures/log_entries.ts | 2 +- .../asset_details/tabs/logs/logs.tsx | 4 +- .../public/components/asset_details/types.ts | 2 +- .../log_stream/log_stream_embeddable.tsx | 4 +- .../logging/log_minimap/density_chart.tsx | 2 +- .../logging/log_minimap/log_minimap.tsx | 13 +- .../logging/log_minimap/search_marker.tsx | 7 +- .../logging/log_minimap/search_markers.tsx | 4 +- .../log_search_buttons.tsx | 3 +- .../log_search_controls.tsx | 2 +- .../view_log_in_context.ts | 3 +- .../infra/public/hooks/use_log_view.mock.ts | 77 ----- x-pack/plugins/infra/public/index.ts | 2 - x-pack/plugins/infra/public/mocks.tsx | 2 - .../state/src/state_machine.ts | 4 +- .../log_stream_page/state/src/types.ts | 12 +- .../src/url_state_storage_service.ts | 11 + .../src/state_machine.ts | 2 +- .../src/url_state_storage_service.ts | 6 +- .../src/state_machine_playground.tsx | 2 +- .../public/pages/link_to/redirect_to_logs.tsx | 2 +- .../pages/link_to/redirect_to_node_logs.tsx | 2 +- .../log_entry_categories/page_content.tsx | 2 +- .../log_entry_categories/page_providers.tsx | 2 +- .../page_results_content.tsx | 2 +- .../top_categories/category_details_row.tsx | 2 +- .../category_example_message.tsx | 25 +- .../top_categories/top_categories_section.tsx | 2 +- .../top_categories/top_categories_table.tsx | 2 +- .../get_log_entry_category_datasets.ts | 2 +- .../get_log_entry_category_examples.ts | 2 +- .../get_top_log_entry_categories.ts | 2 +- .../use_log_entry_categories_results.ts | 2 +- .../use_log_entry_category_examples.tsx | 2 +- .../logs/log_entry_rate/page_content.tsx | 2 +- .../logs/log_entry_rate/page_providers.tsx | 2 +- .../log_entry_rate/page_results_content.tsx | 3 +- .../sections/anomalies/expanded_row.tsx | 2 +- .../sections/anomalies/log_entry_example.tsx | 8 +- .../service_calls/get_log_entry_anomalies.ts | 2 +- .../get_log_entry_anomalies_datasets.ts | 2 +- .../service_calls/get_log_entry_examples.ts | 2 +- .../use_log_entry_anomalies_results.ts | 2 +- .../log_entry_rate/use_log_entry_examples.ts | 2 +- .../public/pages/logs/page_providers.tsx | 12 +- .../index_names_configuration_panel.tsx | 2 +- .../index_pattern_configuration_panel.tsx | 2 +- .../indices_configuration_form_state.ts | 2 +- .../settings/indices_configuration_panel.tsx | 2 +- .../source_configuration_form_state.tsx | 2 +- .../source_configuration_settings.tsx | 2 +- .../pages/logs/shared/page_log_view_error.tsx | 4 +- .../infra/public/pages/logs/stream/page.tsx | 2 +- .../pages/logs/stream/page_logs_content.tsx | 38 ++- .../pages/logs/stream/page_providers.tsx | 15 +- .../public/pages/logs/stream/page_toolbar.tsx | 8 +- .../logs/stream/page_view_log_in_context.tsx | 4 +- .../tabs/logs/logs_link_to_stream.tsx | 2 +- .../components/tabs/logs/logs_tab_content.tsx | 2 +- .../hosts/hooks/use_log_view_reference.ts | 8 +- .../components/node_details/tabs/logs.tsx | 2 +- .../metric_detail/hooks/use_metrics_time.ts | 2 +- x-pack/plugins/infra/public/plugin.ts | 18 +- .../infra/public/test_utils/entries.ts | 11 +- x-pack/plugins/infra/public/types.ts | 8 +- .../public/utils/logs_overview_fetchers.ts | 14 +- .../utils/logs_overview_fetches.test.ts | 74 +++-- .../plugins/infra/public/utils/url_state.tsx | 2 +- .../infra/public/utils/use_url_state.ts | 2 +- x-pack/plugins/infra/server/features.ts | 2 +- x-pack/plugins/infra/server/infra_server.ts | 10 - .../lib/adapters/framework/adapter_types.ts | 3 + ...nventory_metric_threshold_executor.test.ts | 3 +- .../inventory_metric_threshold_executor.ts | 4 +- .../log_threshold_chart_preview.ts | 2 +- .../log_threshold/log_threshold_executor.ts | 4 +- .../log_threshold_references_manager.ts | 4 +- .../plugins/infra/server/lib/infra_types.ts | 4 +- .../lib/log_analysis/log_entry_anomalies.ts | 2 +- .../log_entry_categories_analysis.ts | 7 +- x-pack/plugins/infra/server/mocks.ts | 16 +- x-pack/plugins/infra/server/plugin.ts | 61 ++-- .../routes/log_alerts/chart_preview_data.ts | 6 +- .../results/log_entry_category_examples.ts | 6 +- .../results/log_entry_examples.ts | 6 +- .../infra/server/routes/snapshot/index.ts | 4 +- .../infra/server/saved_objects/index.ts | 1 - x-pack/plugins/infra/server/types.ts | 12 +- .../utils/elasticsearch_runtime_types.ts | 7 - .../utils/map_source_to_log_view.test.ts | 59 ++++ .../server/utils/map_source_to_log_view.ts | 38 +++ x-pack/plugins/infra/tsconfig.json | 2 +- x-pack/plugins/logs_shared/README.md | 3 + .../plugins/logs_shared/common/constants.ts | 10 + x-pack/plugins/logs_shared/common/dynamic.tsx | 30 ++ .../logs_shared/common/formatters/datetime.ts | 18 ++ .../logs_shared/common/http_api/index.ts | 13 + .../logs_shared/common/http_api/latest.ts | 9 + .../http_api/log_entries/v1/highlights.ts | 0 .../common/http_api/log_entries/v1/index.ts | 0 .../common/http_api/log_entries/v1/summary.ts | 0 .../log_entries/v1/summary_highlights.ts | 0 .../common/http_api/log_views/common.ts | 0 .../common/http_api/log_views/index.ts | 0 .../http_api/log_views/v1/get_log_view.ts | 0 .../common/http_api/log_views/v1/index.ts | 0 .../http_api/log_views/v1/put_log_view.ts | 0 x-pack/plugins/logs_shared/common/index.ts | 58 ++++ .../common/log_entry/index.ts | 0 .../common/log_entry/log_entry.ts | 0 .../common/log_entry/log_entry_cursor.ts | 0 .../common/log_text_scale/index.ts | 8 + .../common/log_text_scale/log_text_scale.ts | 12 + .../common/log_views/defaults.ts | 7 +- .../common/log_views/errors.ts | 0 .../common/log_views/index.ts | 1 - .../common/log_views/log_view.mock.ts | 0 .../log_views/resolved_log_view.mock.ts | 0 .../common/log_views/resolved_log_view.ts | 9 +- .../common/log_views/types.ts | 4 +- x-pack/plugins/logs_shared/common/mocks.ts | 8 + .../logs_shared/common/runtime_types.ts | 69 ++++ .../common/search_strategies/common/errors.ts | 43 +++ .../log_entries/log_entries.ts | 75 +++++ .../log_entries/log_entry.ts | 40 +++ .../plugins/logs_shared/common/time/index.ts | 8 + .../logs_shared/common/time/time_key.ts | 80 +++++ .../plugins/logs_shared/common/typed_json.ts | 26 ++ x-pack/plugins/logs_shared/jest.config.js | 17 + x-pack/plugins/logs_shared/kibana.jsonc | 22 ++ .../public/components/auto_sizer.tsx | 188 +++++++++++ .../components/centered_flyout_body.tsx | 0 .../data_search_error_callout.stories.tsx | 0 .../components/data_search_error_callout.tsx | 6 +- .../data_search_progress.stories.tsx | 0 .../components/data_search_progress.tsx | 2 +- .../public/components/empty_states/index.tsx | 8 + .../components/empty_states/no_data.tsx | 49 +++ .../public/components/formatted_time.tsx | 0 .../loading/__examples__/index.stories.tsx | 23 ++ .../public/components/loading/index.tsx | 49 +++ .../public/components/log_stream/index.ts | 0 .../log_stream/log_stream.stories.mdx | 22 +- .../log_stream/log_stream.stories.tsx | 0 .../log_stream.story_decorators.tsx | 0 .../components/log_stream/log_stream.tsx | 0 .../log_stream/log_stream_error_boundary.tsx | 4 +- .../logging/log_entry_flyout/index.tsx | 0 .../log_entry_actions_menu.test.tsx | 0 .../log_entry_actions_menu.tsx | 19 +- .../log_entry_fields_table.tsx | 8 +- .../log_entry_flyout/log_entry_flyout.tsx | 26 +- .../log_text_stream/column_headers.tsx | 4 +- .../logging/log_text_stream/field_value.tsx | 0 .../logging/log_text_stream/highlighting.tsx | 0 .../logging/log_text_stream/index.ts | 7 +- .../logging/log_text_stream/item.ts | 0 .../logging/log_text_stream/jump_to_tail.tsx | 4 +- .../log_text_stream/loading_item_view.tsx | 28 +- .../logging/log_text_stream/log_date_row.tsx | 0 .../log_text_stream/log_entry_column.tsx | 0 .../log_entry_context_menu.tsx | 2 +- .../log_entry_field_column.test.tsx | 0 .../log_entry_field_column.tsx | 0 .../log_entry_message_column.test.tsx | 0 .../log_entry_message_column.tsx | 0 .../logging/log_text_stream/log_entry_row.tsx | 6 +- .../log_entry_timestamp_column.tsx | 0 .../log_text_stream/log_text_separator.tsx | 0 .../log_text_stream/measurable_item_view.tsx | 0 .../scrollable_log_text_stream_view.tsx | 22 +- .../logging/log_text_stream/text_styles.tsx | 0 .../log_text_stream/vertical_scroll_panel.tsx | 0 .../components/resettable_error_boundary.tsx | 0 .../public/containers/logs/log_entry.ts | 0 .../api/fetch_log_entries_highlights.ts | 0 .../api/fetch_log_summary_highlights.ts | 0 .../containers/logs/log_highlights/index.ts | 0 .../log_highlights/log_entry_highlights.tsx | 2 +- .../logs/log_highlights/log_highlights.tsx | 7 +- .../log_highlights/log_summary_highlights.ts | 2 +- .../logs/log_highlights/next_and_previous.tsx | 0 .../containers/logs/log_position/index.ts | 0 .../logs/log_position/use_log_position.ts | 43 ++- .../containers/logs/log_stream/index.ts | 0 .../log_stream/use_fetch_log_entries_after.ts | 0 .../use_fetch_log_entries_around.ts | 0 .../use_fetch_log_entries_before.ts | 0 .../logs/log_summary/api/fetch_log_summary.ts | 0 .../logs/log_summary/bucket_size.ts | 0 .../containers/logs/log_summary/index.ts | 0 .../logs/log_summary/log_summary.test.tsx | 0 .../logs/log_summary/log_summary.tsx | 2 +- .../logs/log_summary/with_summary.ts | 20 +- .../logs_shared/public/hooks/use_kibana.tsx | 71 +++++ .../public/hooks/use_log_view.ts | 0 x-pack/plugins/logs_shared/public/index.ts | 92 ++++++ x-pack/plugins/logs_shared/public/mocks.tsx | 16 + .../log_view_state/README.md | 0 .../log_view_state/index.ts | 0 .../log_view_state/src/index.ts | 0 .../log_view_state/src/notifications.ts | 0 .../log_view_state/src/state_machine.ts | 0 .../log_view_state/src/types.ts | 0 .../src/url_state_storage_service.ts | 0 .../xstate_helpers/README.md | 3 + .../xstate_helpers/index.ts | 8 + .../xstate_helpers/src/index.ts | 9 + .../src/notification_channel.ts | 41 +++ .../xstate_helpers/src/types.ts | 43 +++ x-pack/plugins/logs_shared/public/plugin.ts | 38 +++ .../public/services/log_views/index.ts | 0 .../log_views/log_views_client.mock.ts | 0 .../services/log_views/log_views_client.ts | 0 .../log_views/log_views_service.mock.ts | 0 .../services/log_views/log_views_service.ts | 14 +- .../public/services/log_views/types.ts | 5 +- .../logs_shared/public/test_utils/entries.ts | 75 +++++ .../test_utils/use_global_storybook_theme.tsx | 59 ++++ x-pack/plugins/logs_shared/public/types.ts | 57 ++++ .../utils/data_search/data_search.stories.mdx | 152 +++++++++ .../flatten_data_search_response.ts | 30 ++ .../public/utils/data_search/index.ts | 13 + .../normalize_data_search_responses.ts | 81 +++++ .../public/utils/data_search/types.ts | 50 +++ .../use_data_search_request.test.tsx | 198 ++++++++++++ .../data_search/use_data_search_request.ts | 104 ++++++ .../use_data_search_response_state.ts | 36 +++ ...test_partial_data_search_response.test.tsx | 121 +++++++ ...use_latest_partial_data_search_response.ts | 43 +++ .../logs_shared/public/utils/datemath.ts | 293 +++++++++++++++++ .../logs_shared/public/utils/dev_mode.ts | 12 + .../logs_shared/public/utils/handlers.ts | 25 ++ .../utils/log_column_render_configuration.tsx | 64 ++++ .../public/utils/log_entry/index.ts | 0 .../public/utils/log_entry/log_entry.ts | 0 .../utils/log_entry/log_entry_highlight.ts | 0 .../public/utils/styles.ts | 0 .../public/utils/typed_react.tsx} | 10 +- .../public/utils/use_kibana_query_settings.ts | 31 ++ .../public/utils/use_kibana_ui_setting.ts | 53 ++++ .../public/utils/use_observable.ts | 152 +++++++++ .../public/utils/use_tracked_promise.ts | 299 ++++++++++++++++++ .../public/utils/use_visibility_state.ts | 26 ++ x-pack/plugins/logs_shared/server/index.ts | 21 ++ .../lib/adapters/framework/adapter_types.ts | 96 ++++++ .../server/lib/adapters/framework/index.ts | 8 + .../framework/kibana_framework_adapter.ts | 179 +++++++++++ .../log_entries/kibana_log_entries_adapter.ts | 10 +- .../lib/domains/log_entries_domain/index.ts | 0 .../log_entries_domain.mock.ts | 19 ++ .../log_entries_domain/log_entries_domain.ts | 62 +++- .../queries/log_entry_datasets.ts | 2 - .../server/lib/logs_shared_types.ts | 24 ++ .../logs_shared/server/logs_shared_server.ts | 21 ++ x-pack/plugins/logs_shared/server/mocks.ts | 35 ++ x-pack/plugins/logs_shared/server/plugin.ts | 98 ++++++ .../server/routes/log_entries/highlights.ts | 4 +- .../server/routes/log_entries/index.ts | 0 .../server/routes/log_entries/summary.ts | 15 +- .../routes/log_entries/summary_highlights.ts | 4 +- .../server/routes/log_views/get_log_view.ts | 4 +- .../server/routes/log_views/index.ts | 4 +- .../server/routes/log_views/put_log_view.ts | 4 +- .../logs_shared/server/saved_objects/index.ts | 8 + .../server/saved_objects/log_view/index.ts | 0 .../log_view/log_view_saved_object.ts | 2 +- .../log_view/references/index.ts | 0 .../log_view/references/log_indices.ts | 12 +- .../server/saved_objects/log_view/types.ts | 2 + .../server/saved_objects/references.test.ts | 121 +++++++ .../server/saved_objects/references.ts | 78 +++++ .../server/services/log_entries/index.ts | 0 .../log_entries_search_strategy.test.ts | 0 .../log_entries_search_strategy.ts | 0 .../log_entries/log_entries_service.ts | 0 .../log_entry_search_strategy.test.ts | 0 .../log_entries/log_entry_search_strategy.ts | 0 .../builtin_rules/filebeat_apache2.test.ts | 0 .../message/builtin_rules/filebeat_apache2.ts | 0 .../builtin_rules/filebeat_auditd.test.ts | 0 .../message/builtin_rules/filebeat_auditd.ts | 0 .../builtin_rules/filebeat_haproxy.test.ts | 0 .../message/builtin_rules/filebeat_haproxy.ts | 0 .../builtin_rules/filebeat_icinga.test.ts | 0 .../message/builtin_rules/filebeat_icinga.ts | 0 .../builtin_rules/filebeat_iis.test.ts | 0 .../message/builtin_rules/filebeat_iis.ts | 0 .../builtin_rules/filebeat_kafka.test.ts | 0 .../builtin_rules/filebeat_logstash.test.ts | 0 .../builtin_rules/filebeat_logstash.ts | 0 .../builtin_rules/filebeat_mongodb.test.ts | 0 .../message/builtin_rules/filebeat_mongodb.ts | 0 .../builtin_rules/filebeat_mysql.test.ts | 0 .../message/builtin_rules/filebeat_mysql.ts | 0 .../builtin_rules/filebeat_nginx.test.ts | 0 .../message/builtin_rules/filebeat_nginx.ts | 0 .../builtin_rules/filebeat_osquery.test.ts | 0 .../message/builtin_rules/filebeat_osquery.ts | 0 .../message/builtin_rules/filebeat_redis.ts | 0 .../message/builtin_rules/filebeat_system.ts | 0 .../builtin_rules/filebeat_traefik.test.ts | 0 .../message/builtin_rules/filebeat_traefik.ts | 0 .../message/builtin_rules/generic.test.ts | 0 .../message/builtin_rules/generic.ts | 0 .../builtin_rules/generic_webserver.ts | 0 .../message/builtin_rules/helpers.ts | 0 .../message/builtin_rules/index.ts | 0 .../services/log_entries/message/index.ts | 0 .../services/log_entries/message/message.ts | 0 .../log_entries/message/rule_types.ts | 0 .../services/log_entries/queries/common.ts | 0 .../log_entries/queries/log_entries.ts | 0 .../services/log_entries/queries/log_entry.ts | 0 .../server/services/log_entries/types.ts | 0 .../server/services/log_views/errors.ts | 9 + .../server/services/log_views/index.ts | 0 .../log_views/log_views_client.mock.ts | 0 .../log_views/log_views_client.test.ts | 52 +-- .../services/log_views/log_views_client.ts | 51 +-- .../log_views/log_views_service.mock.ts | 2 + .../services/log_views/log_views_service.ts | 33 +- .../server/services/log_views/types.ts | 13 +- x-pack/plugins/logs_shared/server/types.ts | 48 +++ .../utils/elasticsearch_runtime_types.ts | 41 +++ .../server/utils/serialized_query.ts | 30 ++ .../server/utils/typed_search_strategy.ts | 0 x-pack/plugins/logs_shared/tsconfig.json | 31 ++ x-pack/plugins/monitoring/kibana.jsonc | 1 + ...{init_infra_source.ts => init_log_view.ts} | 8 +- .../collection/get_collection_status.test.ts | 2 + x-pack/plugins/monitoring/server/plugin.ts | 4 +- x-pack/plugins/monitoring/server/types.ts | 2 + x-pack/plugins/monitoring/tsconfig.json | 1 + .../translations/translations/fr-FR.json | 82 ++--- .../translations/translations/ja-JP.json | 82 ++--- .../translations/translations/zh-CN.json | 82 ++--- x-pack/plugins/upgrade_assistant/kibana.jsonc | 3 +- .../upgrade_assistant/server/plugin.ts | 8 +- .../plugins/upgrade_assistant/tsconfig.json | 2 +- .../api_integration/apis/logs_ui/log_views.ts | 4 +- .../apis/metrics_ui/log_entry_highlights.ts | 2 +- .../apis/metrics_ui/log_summary.ts | 2 +- .../test/common/services/infra_log_views.ts | 4 +- x-pack/test/tsconfig.json | 1 + yarn.lock | 4 + 391 files changed, 4955 insertions(+), 836 deletions(-) rename x-pack/plugins/infra/common/{log_views => }/url_state_storage_service.ts (90%) delete mode 100644 x-pack/plugins/infra/public/hooks/use_log_view.mock.ts create mode 100644 x-pack/plugins/infra/server/utils/map_source_to_log_view.test.ts create mode 100644 x-pack/plugins/infra/server/utils/map_source_to_log_view.ts create mode 100755 x-pack/plugins/logs_shared/README.md create mode 100644 x-pack/plugins/logs_shared/common/constants.ts create mode 100644 x-pack/plugins/logs_shared/common/dynamic.tsx create mode 100644 x-pack/plugins/logs_shared/common/formatters/datetime.ts create mode 100644 x-pack/plugins/logs_shared/common/http_api/index.ts create mode 100644 x-pack/plugins/logs_shared/common/http_api/latest.ts rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_entries/v1/highlights.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_entries/v1/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_entries/v1/summary.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_entries/v1/summary_highlights.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_views/common.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_views/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_views/v1/get_log_view.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_views/v1/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/http_api/log_views/v1/put_log_view.ts (100%) create mode 100644 x-pack/plugins/logs_shared/common/index.ts rename x-pack/plugins/{infra => logs_shared}/common/log_entry/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/log_entry/log_entry.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/log_entry/log_entry_cursor.ts (100%) create mode 100644 x-pack/plugins/logs_shared/common/log_text_scale/index.ts create mode 100644 x-pack/plugins/logs_shared/common/log_text_scale/log_text_scale.ts rename x-pack/plugins/{infra => logs_shared}/common/log_views/defaults.ts (82%) rename x-pack/plugins/{infra => logs_shared}/common/log_views/errors.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/log_views/index.ts (89%) rename x-pack/plugins/{infra => logs_shared}/common/log_views/log_view.mock.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/log_views/resolved_log_view.mock.ts (100%) rename x-pack/plugins/{infra => logs_shared}/common/log_views/resolved_log_view.ts (91%) rename x-pack/plugins/{infra => logs_shared}/common/log_views/types.ts (96%) create mode 100644 x-pack/plugins/logs_shared/common/mocks.ts create mode 100644 x-pack/plugins/logs_shared/common/runtime_types.ts create mode 100644 x-pack/plugins/logs_shared/common/search_strategies/common/errors.ts create mode 100644 x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entries.ts create mode 100644 x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entry.ts create mode 100644 x-pack/plugins/logs_shared/common/time/index.ts create mode 100644 x-pack/plugins/logs_shared/common/time/time_key.ts create mode 100644 x-pack/plugins/logs_shared/common/typed_json.ts create mode 100644 x-pack/plugins/logs_shared/jest.config.js create mode 100644 x-pack/plugins/logs_shared/kibana.jsonc create mode 100644 x-pack/plugins/logs_shared/public/components/auto_sizer.tsx rename x-pack/plugins/{infra => logs_shared}/public/components/centered_flyout_body.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/data_search_error_callout.stories.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/data_search_error_callout.tsx (92%) rename x-pack/plugins/{infra => logs_shared}/public/components/data_search_progress.stories.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/data_search_progress.tsx (93%) create mode 100644 x-pack/plugins/logs_shared/public/components/empty_states/index.tsx create mode 100644 x-pack/plugins/logs_shared/public/components/empty_states/no_data.tsx rename x-pack/plugins/{infra => logs_shared}/public/components/formatted_time.tsx (100%) create mode 100644 x-pack/plugins/logs_shared/public/components/loading/__examples__/index.stories.tsx create mode 100644 x-pack/plugins/logs_shared/public/components/loading/index.tsx rename x-pack/plugins/{infra => logs_shared}/public/components/log_stream/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/log_stream/log_stream.stories.mdx (91%) rename x-pack/plugins/{infra => logs_shared}/public/components/log_stream/log_stream.stories.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/log_stream/log_stream.story_decorators.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/log_stream/log_stream.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/log_stream/log_stream_error_boundary.tsx (93%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_entry_flyout/index.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx (90%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx (90%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_entry_flyout/log_entry_flyout.tsx (91%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/column_headers.tsx (96%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/field_value.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/highlighting.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/index.ts (79%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/item.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/jump_to_tail.tsx (93%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/loading_item_view.tsx (89%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_date_row.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_column.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_context_menu.tsx (97%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_field_column.test.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_field_column.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_message_column.test.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_message_column.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_row.tsx (97%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/log_text_separator.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/measurable_item_view.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx (95%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/text_styles.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/logging/log_text_stream/vertical_scroll_panel.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/components/resettable_error_boundary.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_entry.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/log_entry_highlights.tsx (98%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/log_highlights.tsx (96%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/log_summary_highlights.ts (97%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_highlights/next_and_previous.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_position/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_position/use_log_position.ts (80%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_stream/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_stream/use_fetch_log_entries_after.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_stream/use_fetch_log_entries_around.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_stream/use_fetch_log_entries_before.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_summary/api/fetch_log_summary.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_summary/bucket_size.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_summary/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_summary/log_summary.test.tsx (100%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_summary/log_summary.tsx (97%) rename x-pack/plugins/{infra => logs_shared}/public/containers/logs/log_summary/with_summary.ts (66%) create mode 100644 x-pack/plugins/logs_shared/public/hooks/use_kibana.tsx rename x-pack/plugins/{infra => logs_shared}/public/hooks/use_log_view.ts (100%) create mode 100644 x-pack/plugins/logs_shared/public/index.ts create mode 100644 x-pack/plugins/logs_shared/public/mocks.tsx rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/README.md (100%) rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/src/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/src/notifications.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/src/state_machine.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/src/types.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/observability_logs/log_view_state/src/url_state_storage_service.ts (100%) create mode 100644 x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/README.md create mode 100644 x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/index.ts create mode 100644 x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/index.ts create mode 100644 x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/notification_channel.ts create mode 100644 x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/types.ts create mode 100644 x-pack/plugins/logs_shared/public/plugin.ts rename x-pack/plugins/{infra => logs_shared}/public/services/log_views/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/services/log_views/log_views_client.mock.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/services/log_views/log_views_client.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/services/log_views/log_views_service.mock.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/services/log_views/log_views_service.ts (61%) rename x-pack/plugins/{infra => logs_shared}/public/services/log_views/types.ts (90%) create mode 100644 x-pack/plugins/logs_shared/public/test_utils/entries.ts create mode 100644 x-pack/plugins/logs_shared/public/test_utils/use_global_storybook_theme.tsx create mode 100644 x-pack/plugins/logs_shared/public/types.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/data_search.stories.mdx create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/flatten_data_search_response.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/index.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/normalize_data_search_responses.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/types.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.test.tsx create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_response_state.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.test.tsx create mode 100644 x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/datemath.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/dev_mode.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/handlers.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/log_column_render_configuration.tsx rename x-pack/plugins/{infra => logs_shared}/public/utils/log_entry/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/utils/log_entry/log_entry.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/utils/log_entry/log_entry_highlight.ts (100%) rename x-pack/plugins/{infra => logs_shared}/public/utils/styles.ts (100%) rename x-pack/plugins/{infra/public/components/log_stream/lazy_log_stream_wrapper.tsx => logs_shared/public/utils/typed_react.tsx} (50%) create mode 100644 x-pack/plugins/logs_shared/public/utils/use_kibana_query_settings.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/use_kibana_ui_setting.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/use_observable.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/use_tracked_promise.ts create mode 100644 x-pack/plugins/logs_shared/public/utils/use_visibility_state.ts create mode 100644 x-pack/plugins/logs_shared/server/index.ts create mode 100644 x-pack/plugins/logs_shared/server/lib/adapters/framework/adapter_types.ts create mode 100644 x-pack/plugins/logs_shared/server/lib/adapters/framework/index.ts create mode 100644 x-pack/plugins/logs_shared/server/lib/adapters/framework/kibana_framework_adapter.ts rename x-pack/plugins/{infra => logs_shared}/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts (96%) rename x-pack/plugins/{infra => logs_shared}/server/lib/domains/log_entries_domain/index.ts (100%) create mode 100644 x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.mock.ts rename x-pack/plugins/{infra => logs_shared}/server/lib/domains/log_entries_domain/log_entries_domain.ts (84%) rename x-pack/plugins/{infra => logs_shared}/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts (96%) create mode 100644 x-pack/plugins/logs_shared/server/lib/logs_shared_types.ts create mode 100644 x-pack/plugins/logs_shared/server/logs_shared_server.ts create mode 100644 x-pack/plugins/logs_shared/server/mocks.ts create mode 100644 x-pack/plugins/logs_shared/server/plugin.ts rename x-pack/plugins/{infra => logs_shared}/server/routes/log_entries/highlights.ts (96%) rename x-pack/plugins/{infra => logs_shared}/server/routes/log_entries/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/routes/log_entries/summary.ts (83%) rename x-pack/plugins/{infra => logs_shared}/server/routes/log_entries/summary_highlights.ts (95%) rename x-pack/plugins/{infra => logs_shared}/server/routes/log_views/get_log_view.ts (92%) rename x-pack/plugins/{infra => logs_shared}/server/routes/log_views/index.ts (82%) rename x-pack/plugins/{infra => logs_shared}/server/routes/log_views/put_log_view.ts (93%) create mode 100644 x-pack/plugins/logs_shared/server/saved_objects/index.ts rename x-pack/plugins/{infra => logs_shared}/server/saved_objects/log_view/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/saved_objects/log_view/log_view_saved_object.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/saved_objects/log_view/references/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/saved_objects/log_view/references/log_indices.ts (85%) rename x-pack/plugins/{infra => logs_shared}/server/saved_objects/log_view/types.ts (95%) create mode 100644 x-pack/plugins/logs_shared/server/saved_objects/references.test.ts create mode 100644 x-pack/plugins/logs_shared/server/saved_objects/references.ts rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/log_entries_search_strategy.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/log_entries_search_strategy.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/log_entries_service.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/log_entry_search_strategy.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/log_entry_search_strategy.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_apache2.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_apache2.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_auditd.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_auditd.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_haproxy.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_haproxy.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_icinga.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_icinga.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_iis.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_iis.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_kafka.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_logstash.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_logstash.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_mongodb.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_mongodb.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_mysql.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_mysql.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_nginx.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_nginx.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_osquery.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_osquery.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_redis.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_system.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_traefik.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/filebeat_traefik.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/generic.test.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/generic.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/generic_webserver.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/helpers.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/builtin_rules/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/message.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/message/rule_types.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/queries/common.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/queries/log_entries.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/queries/log_entry.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_entries/types.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/errors.ts (65%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/index.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/log_views_client.mock.ts (100%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/log_views_client.test.ts (88%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/log_views_client.ts (77%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/log_views_service.mock.ts (90%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/log_views_service.ts (65%) rename x-pack/plugins/{infra => logs_shared}/server/services/log_views/types.ts (81%) create mode 100644 x-pack/plugins/logs_shared/server/types.ts create mode 100644 x-pack/plugins/logs_shared/server/utils/elasticsearch_runtime_types.ts create mode 100644 x-pack/plugins/logs_shared/server/utils/serialized_query.ts rename x-pack/plugins/{infra => logs_shared}/server/utils/typed_search_strategy.ts (100%) create mode 100644 x-pack/plugins/logs_shared/tsconfig.json rename x-pack/plugins/monitoring/server/lib/logs/{init_infra_source.ts => init_log_view.ts} (74%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9f41080bd5a5..4248bd86c7d5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -468,6 +468,7 @@ examples/locator_examples @elastic/kibana-app-services examples/locator_explorer @elastic/kibana-app-services packages/kbn-logging @elastic/kibana-core packages/kbn-logging-mocks @elastic/kibana-core +x-pack/plugins/logs_shared @elastic/infra-monitoring-ui x-pack/plugins/logstash @elastic/logstash packages/kbn-managed-vscode-config @elastic/kibana-operations packages/kbn-managed-vscode-config-cli @elastic/kibana-operations diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 919703efff78..8e001dcd6fdc 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -628,6 +628,10 @@ the infrastructure monitoring use-case within Kibana. using the CURL scripts in the scripts folder. +|{kib-repo}blob/{branch}/x-pack/plugins/logs_shared/README.md[logsShared] +|Exposes the shared components and APIs to access and visualize logs. + + |{kib-repo}blob/{branch}/x-pack/plugins/logstash[logstash] |WARNING: Missing README. diff --git a/package.json b/package.json index 9a3366b65ee2..7658626741ea 100644 --- a/package.json +++ b/package.json @@ -487,6 +487,7 @@ "@kbn/locator-explorer-plugin": "link:examples/locator_explorer", "@kbn/logging": "link:packages/kbn-logging", "@kbn/logging-mocks": "link:packages/kbn-logging-mocks", + "@kbn/logs-shared-plugin": "link:x-pack/plugins/logs_shared", "@kbn/logstash-plugin": "link:x-pack/plugins/logstash", "@kbn/management-cards-navigation": "link:packages/kbn-management/cards_navigation", "@kbn/management-plugin": "link:src/plugins/management", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index eb7ca100c56f..c0a9f99b09d4 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -88,6 +88,7 @@ pageLoadAssetSize: licenseManagement: 41817 licensing: 29004 lists: 22900 + logsShared: 281060 logstash: 53548 management: 46112 maps: 90000 diff --git a/tsconfig.base.json b/tsconfig.base.json index 6db4864f58f0..1f70f8471564 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -930,6 +930,8 @@ "@kbn/logging/*": ["packages/kbn-logging/*"], "@kbn/logging-mocks": ["packages/kbn-logging-mocks"], "@kbn/logging-mocks/*": ["packages/kbn-logging-mocks/*"], + "@kbn/logs-shared-plugin": ["x-pack/plugins/logs_shared"], + "@kbn/logs-shared-plugin/*": ["x-pack/plugins/logs_shared/*"], "@kbn/logstash-plugin": ["x-pack/plugins/logstash"], "@kbn/logstash-plugin/*": ["x-pack/plugins/logstash/*"], "@kbn/managed-vscode-config": ["packages/kbn-managed-vscode-config"], diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index eea7dced5e27..2fa461531b49 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -37,6 +37,7 @@ "xpack.idxMgmt": "plugins/index_management", "xpack.indexLifecycleMgmt": "plugins/index_lifecycle_management", "xpack.infra": "plugins/infra", + "xpack.logsShared": "plugins/logs_shared", "xpack.fleet": "plugins/fleet", "xpack.ingestPipelines": "plugins/ingest_pipelines", "xpack.kubernetesSecurity": "plugins/kubernetes_security", diff --git a/x-pack/plugins/apm/kibana.jsonc b/x-pack/plugins/apm/kibana.jsonc index ac33a0ee0b84..0b248bbbe53f 100644 --- a/x-pack/plugins/apm/kibana.jsonc +++ b/x-pack/plugins/apm/kibana.jsonc @@ -15,6 +15,7 @@ "embeddable", "features", "infra", + "logsShared", "inspector", "licensing", "observability", diff --git a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx index 83cf09b3e586..bde1749f33d3 100644 --- a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; import moment from 'moment'; -import { LogStream } from '@kbn/infra-plugin/public'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { useFetcher } from '../../../hooks/use_fetcher'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx index 7e363d50ae66..78e6a89d3062 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx @@ -7,7 +7,7 @@ import { EuiSpacer, EuiTab, EuiTabs, EuiSkeletonText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { LogStream } from '@kbn/infra-plugin/public'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; import React, { useMemo } from 'react'; import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { TransactionMetadata } from '../../../shared/metadata_table/transaction_metadata'; diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index 405269352361..6947c8cfcd62 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -92,6 +92,7 @@ "@kbn/dashboard-plugin", "@kbn/controls-plugin", "@kbn/core-http-server", + "@kbn/logs-shared-plugin", "@kbn/unified-field-list", "@kbn/slo-schema", "@kbn/discover-plugin" diff --git a/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts b/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts index 2bc2fed33aa4..1e268aad1f73 100644 --- a/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts +++ b/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts @@ -11,7 +11,6 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DiscoverStart } from '@kbn/discover-plugin/public'; import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; -import type { InfraClientStartExports } from '@kbn/infra-plugin/public'; import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { SecurityPluginStart } from '@kbn/security-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; @@ -24,7 +23,6 @@ export interface KibanaDeps { discover: DiscoverStart; features: FeaturesPluginStart; guidedOnboarding: GuidedOnboardingPluginStart; - infra: InfraClientStartExports; licensing: LicensingPluginStart; security: SecurityPluginStart; share: SharePluginStart; diff --git a/x-pack/plugins/enterprise_search/kibana.jsonc b/x-pack/plugins/enterprise_search/kibana.jsonc index 0a6d3ebee52a..01e43542e84f 100644 --- a/x-pack/plugins/enterprise_search/kibana.jsonc +++ b/x-pack/plugins/enterprise_search/kibana.jsonc @@ -17,7 +17,7 @@ "data", "discover", "charts", - "infra", + "logsShared", "cloud", "esUiShared", "guidedOnboarding", diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/log_stream/log_stream.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/log_stream/log_stream.tsx index d655a7c57a97..5f3af9a500b9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/log_stream/log_stream.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/log_stream/log_stream.tsx @@ -7,17 +7,17 @@ import React from 'react'; -import { LogStream, LogStreamProps } from '@kbn/infra-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { LogStream, LogStreamProps } from '@kbn/logs-shared-plugin/public'; /* - * EnterpriseSearchLogStream is a light wrapper on top of infra's embeddable LogStream component. + * EnterpriseSearchLogStream is a light wrapper on top of logsShared's embeddable LogStream component. * It prepopulates our log source ID (set in server/plugin.ts) and sets a basic 24-hours-ago * default for timestamps. All other props get passed as-is to the underlying LogStream. * * Documentation links for reference: - * - https://github.com/elastic/kibana/blob/main/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx - * - Run `yarn storybook infra` for live docs + * - https://github.com/elastic/kibana/blob/main/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.mdx + * - Run `yarn storybook logsShared` for live docs */ interface Props extends Omit { diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index b072fbdf5150..041a295ff483 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -21,7 +21,7 @@ import { DataPluginStart } from '@kbn/data-plugin/server/plugin'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; -import { InfraPluginSetup } from '@kbn/infra-plugin/server'; +import { LogsSharedPluginSetup } from '@kbn/logs-shared-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; @@ -88,7 +88,7 @@ interface PluginsSetup { features: FeaturesPluginSetup; globalSearch: GlobalSearchPluginSetup; guidedOnboarding: GuidedOnboardingPluginSetup; - infra: InfraPluginSetup; + logsShared: LogsSharedPluginSetup; ml?: MlPluginSetup; security: SecurityPluginSetup; usageCollection?: UsageCollectionSetup; @@ -125,7 +125,7 @@ export class EnterpriseSearchPlugin implements Plugin { security, features, globalSearch, - infra, + logsShared, customIntegrations, ml, guidedOnboarding, @@ -261,9 +261,9 @@ export class EnterpriseSearchPlugin implements Plugin { /* * Register logs source configuration, used by LogStream components - * @see https://github.com/elastic/kibana/blob/main/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx#with-a-source-configuration + * @see https://github.com/elastic/kibana/blob/main/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.mdx#with-a-source-configuration */ - infra.logViews.defineInternalLogView(ENTERPRISE_SEARCH_RELEVANCE_LOGS_SOURCE_ID, { + logsShared.logViews.defineInternalLogView(ENTERPRISE_SEARCH_RELEVANCE_LOGS_SOURCE_ID, { logIndices: { indexName: 'logs-app_search.search_relevance_suggestions-*', type: 'index_name', @@ -271,7 +271,7 @@ export class EnterpriseSearchPlugin implements Plugin { name: 'Enterprise Search Search Relevance Logs', }); - infra.logViews.defineInternalLogView(ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID, { + logsShared.logViews.defineInternalLogView(ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID, { logIndices: { indexName: 'logs-enterprise_search*', type: 'index_name', @@ -279,7 +279,7 @@ export class EnterpriseSearchPlugin implements Plugin { name: 'Enterprise Search Audit Logs', }); - infra.logViews.defineInternalLogView(ENTERPRISE_SEARCH_ANALYTICS_LOGS_SOURCE_ID, { + logsShared.logViews.defineInternalLogView(ENTERPRISE_SEARCH_ANALYTICS_LOGS_SOURCE_ID, { logIndices: { indexName: 'behavioral_analytics-events-*', type: 'index_name', diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index f8407385478f..39758cb51110 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -22,7 +22,6 @@ "@kbn/usage-collection-plugin", "@kbn/cloud-plugin", "@kbn/cloud-chat-plugin", - "@kbn/infra-plugin", "@kbn/features-plugin", "@kbn/lens-plugin", "@kbn/licensing-plugin", @@ -60,6 +59,7 @@ "@kbn/core-elasticsearch-server-mocks", "@kbn/shared-ux-link-redirect-app", "@kbn/global-search-plugin", + "@kbn/logs-shared-plugin", "@kbn/share-plugin", "@kbn/core-saved-objects-migration-server-internal", ] diff --git a/x-pack/plugins/fleet/kibana.jsonc b/x-pack/plugins/fleet/kibana.jsonc index f86c93945644..f0b01080db22 100644 --- a/x-pack/plugins/fleet/kibana.jsonc +++ b/x-pack/plugins/fleet/kibana.jsonc @@ -40,7 +40,7 @@ "kibanaReact", "cloudChat", "esUiShared", - "infra", + "logsShared", "kibanaUtils", "usageCollection", "unifiedSearch" diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 4af6b30c99fb..e6dae29cea88 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -30,7 +30,7 @@ import semverCoerce from 'semver/functions/coerce'; import { createStateContainerReactHelpers } from '@kbn/kibana-utils-plugin/public'; import { RedirectAppLinks } from '@kbn/kibana-react-plugin/public'; import type { TimeRange } from '@kbn/es-query'; -import { LogStream, type LogStreamProps } from '@kbn/infra-plugin/public'; +import { LogStream, type LogStreamProps } from '@kbn/logs-shared-plugin/public'; import type { Agent, AgentPolicy } from '../../../../../types'; import { useLink, useStartServices } from '../../../../../hooks'; diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index dc858ff5029a..4e8ec7de3fcc 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -42,7 +42,7 @@ "@kbn/cloud-chat-plugin", "@kbn/kibana-react-plugin", "@kbn/es-ui-shared-plugin", - "@kbn/infra-plugin", + "@kbn/logs-shared-plugin", "@kbn/kibana-utils-plugin", "@kbn/unified-search-plugin", "@kbn/storybook", diff --git a/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts index 2eec5b035c79..4f5b977dd3b8 100644 --- a/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts +++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import * as rt from 'io-ts'; -import { persistedLogViewReferenceRT } from '../../../log_views'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; export const LOG_DOCUMENT_COUNT_RULE_TYPE_ID = 'logs.alert.document.count'; diff --git a/x-pack/plugins/infra/common/http_api/index.ts b/x-pack/plugins/infra/common/http_api/index.ts index 9f63896dbca9..cfa4841d9aa5 100644 --- a/x-pack/plugins/infra/common/http_api/index.ts +++ b/x-pack/plugins/infra/common/http_api/index.ts @@ -20,6 +20,4 @@ export * as inventoryViewsV1 from './inventory_views/v1'; export * as logAlertsV1 from './log_alerts/v1'; export * as logAnalysisResultsV1 from './log_analysis/results/v1'; export * as logAnalysisValidationV1 from './log_analysis/validation/v1'; -export * as logEntriesV1 from './log_entries/v1'; -export * as logViewsV1 from './log_views/v1'; export * as metricsExplorerViewsV1 from './metrics_explorer_views/v1'; diff --git a/x-pack/plugins/infra/common/http_api/latest.ts b/x-pack/plugins/infra/common/http_api/latest.ts index 28cdf220f710..98787a2c581a 100644 --- a/x-pack/plugins/infra/common/http_api/latest.ts +++ b/x-pack/plugins/infra/common/http_api/latest.ts @@ -9,6 +9,4 @@ export * from './inventory_views/v1'; export * from './log_alerts/v1'; export * from './log_analysis/results/v1'; export * from './log_analysis/validation/v1'; -export * from './log_entries/v1'; -export * from './log_views/v1'; export * from './metrics_explorer_views/v1'; diff --git a/x-pack/plugins/infra/common/http_api/log_alerts/v1/chart_preview_data.ts b/x-pack/plugins/infra/common/http_api/log_alerts/v1/chart_preview_data.ts index 5647b9d09bdc..7f0424f0df53 100644 --- a/x-pack/plugins/infra/common/http_api/log_alerts/v1/chart_preview_data.ts +++ b/x-pack/plugins/infra/common/http_api/log_alerts/v1/chart_preview_data.ts @@ -6,6 +6,7 @@ */ import * as rt from 'io-ts'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { ThresholdRT, countCriteriaRT, @@ -13,7 +14,6 @@ import { timeSizeRT, groupByRT, } from '../../../alerting/logs/log_threshold/types'; -import { persistedLogViewReferenceRT } from '../../../log_views'; export const LOG_ALERTS_CHART_PREVIEW_DATA_PATH = '/api/infra/log_alerts/chart_preview_data'; diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies.ts index 56ee97a46846..355396206399 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies.ts @@ -7,7 +7,7 @@ import * as rt from 'io-ts'; -import { persistedLogViewReferenceRT } from '../../../../log_views'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { timeRangeRT, routeTimingMetadataRT } from '../../../shared'; import { logEntryAnomalyRT, diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies_datasets.ts index 8de55d96ba3c..c07007be0511 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies_datasets.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_anomalies_datasets.ts @@ -6,7 +6,7 @@ */ import * as rt from 'io-ts'; -import { persistedLogViewReferenceRT } from '../../../../log_views'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { badRequestErrorRT, diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_categories.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_categories.ts index 9e838174bb6a..e84825b8c683 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_categories.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_categories.ts @@ -7,7 +7,7 @@ import * as rt from 'io-ts'; -import { persistedLogViewReferenceRT } from '../../../../log_views'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { badRequestErrorRT, forbiddenErrorRT, diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_datasets.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_datasets.ts index 6523559103fd..e051e313d9b8 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_datasets.ts @@ -7,13 +7,13 @@ import * as rt from 'io-ts'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { badRequestErrorRT, forbiddenErrorRT, timeRangeRT, routeTimingMetadataRT, } from '../../../shared'; -import { persistedLogViewReferenceRT } from '../../../../log_views'; export const LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH = '/api/infra/log_analysis/results/log_entry_category_datasets'; diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_examples.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_examples.ts index 00fff8aabf9d..fc6ece5d7b7f 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_examples.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_category_examples.ts @@ -5,16 +5,14 @@ * 2.0. */ +import { logEntryContextRT, persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import * as rt from 'io-ts'; - -import { persistedLogViewReferenceRT } from '../../../../log_views'; import { badRequestErrorRT, forbiddenErrorRT, - timeRangeRT, routeTimingMetadataRT, + timeRangeRT, } from '../../../shared'; -import { logEntryContextRT } from '../../../../log_entry'; export const LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_EXAMPLES_PATH = '/api/infra/log_analysis/results/log_entry_category_examples'; diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_examples.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_examples.ts index 8233962df6bf..ebc78693f498 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_examples.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/v1/log_entry_examples.ts @@ -6,7 +6,7 @@ */ import * as rt from 'io-ts'; -import { persistedLogViewReferenceRT } from '../../../../log_views'; +import { persistedLogViewReferenceRT } from '@kbn/logs-shared-plugin/common'; import { logEntryExampleRT } from '../../../../log_analysis'; import { badRequestErrorRT, diff --git a/x-pack/plugins/infra/common/locators/helpers.ts b/x-pack/plugins/infra/common/locators/helpers.ts index 9ede09e7f1b1..b4258053a0c0 100644 --- a/x-pack/plugins/infra/common/locators/helpers.ts +++ b/x-pack/plugins/infra/common/locators/helpers.ts @@ -5,23 +5,25 @@ * 2.0. */ -import { flowRight } from 'lodash'; import type { DiscoverAppLocatorParams } from '@kbn/discover-plugin/common'; import type { DiscoverStart } from '@kbn/discover-plugin/public'; -import { findInventoryFields } from '../inventory_models'; -import { MESSAGE_FIELD, TIMESTAMP_FIELD } from '../constants'; -import type { TimeRange } from '../time'; -import type { LogsLocatorParams } from './logs_locator'; -import type { InfraClientCoreSetup } from '../../public/types'; import { DEFAULT_LOG_VIEW, LogViewColumnConfiguration, LogViewReference, + ResolvedLogView, +} from '@kbn/logs-shared-plugin/common'; +import { flowRight } from 'lodash'; +import type { InfraClientCoreSetup } from '../../public/types'; +import { MESSAGE_FIELD, TIMESTAMP_FIELD } from '../constants'; +import { findInventoryFields } from '../inventory_models'; +import type { TimeRange } from '../time'; +import { replaceLogFilterInQueryString, replaceLogPositionInQueryString, replaceLogViewInQueryString, - ResolvedLogView, -} from '../log_views'; +} from '../url_state_storage_service'; +import type { LogsLocatorParams } from './logs_locator'; import type { NodeLogsLocatorParams } from './node_logs_locator'; interface LocationToDiscoverParams { @@ -59,9 +61,9 @@ export const getLocationToDiscover = async ({ filter, logView = DEFAULT_LOG_VIEW, }: LocationToDiscoverParams) => { - const [, plugins, pluginStart] = await core.getStartServices(); - const { discover } = plugins; - const { logViews } = pluginStart; + const [, plugins] = await core.getStartServices(); + const { discover, logsShared } = plugins; + const { logViews } = logsShared; const resolvedLogView = await logViews.client.getResolvedLogView(logView); const discoverParams: DiscoverAppLocatorParams = { diff --git a/x-pack/plugins/infra/common/locators/locators.test.ts b/x-pack/plugins/infra/common/locators/locators.test.ts index 5a4ea0047e2d..c0573d942e75 100644 --- a/x-pack/plugins/infra/common/locators/locators.test.ts +++ b/x-pack/plugins/infra/common/locators/locators.test.ts @@ -13,7 +13,7 @@ import type { NodeLogsLocatorParams } from './node_logs_locator'; import { coreMock } from '@kbn/core/public/mocks'; import { findInventoryFields } from '../inventory_models'; import moment from 'moment'; -import { DEFAULT_LOG_VIEW, LogViewReference } from '../log_views'; +import { DEFAULT_LOG_VIEW, LogViewReference } from '@kbn/logs-shared-plugin/common'; const setupLogsLocator = async () => { const deps: LogsLocatorDependencies = { diff --git a/x-pack/plugins/infra/common/locators/logs_locator.ts b/x-pack/plugins/infra/common/locators/logs_locator.ts index f4e65634aa76..ee9006f359e6 100644 --- a/x-pack/plugins/infra/common/locators/logs_locator.ts +++ b/x-pack/plugins/infra/common/locators/logs_locator.ts @@ -7,7 +7,7 @@ import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; import { SerializableRecord } from '@kbn/utility-types'; -import type { LogViewReference } from '../log_views'; +import type { LogViewReference } from '@kbn/logs-shared-plugin/common'; import type { TimeRange } from '../time'; import type { InfraClientCoreSetup } from '../../public/types'; diff --git a/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts b/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts index f8daaa1b9227..c6b53abee714 100644 --- a/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts +++ b/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts @@ -6,14 +6,15 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import * as rt from 'io-ts'; import { logEntryAfterCursorRT, logEntryBeforeCursorRT, logEntryCursorRT, logEntryRT, -} from '../../log_entry'; -import { logViewColumnConfigurationRT, logViewReferenceRT } from '../../log_views'; + logViewColumnConfigurationRT, + logViewReferenceRT, +} from '@kbn/logs-shared-plugin/common'; +import * as rt from 'io-ts'; import { jsonObjectRT } from '../../typed_json'; import { searchStrategyErrorRT } from '../common/errors'; diff --git a/x-pack/plugins/infra/common/search_strategies/log_entries/log_entry.ts b/x-pack/plugins/infra/common/search_strategies/log_entries/log_entry.ts index 6d2a7891264d..131450d588c0 100644 --- a/x-pack/plugins/infra/common/search_strategies/log_entries/log_entry.ts +++ b/x-pack/plugins/infra/common/search_strategies/log_entries/log_entry.ts @@ -5,9 +5,12 @@ * 2.0. */ +import { + logEntryCursorRT, + logEntryFieldRT, + logViewReferenceRT, +} from '@kbn/logs-shared-plugin/common'; import * as rt from 'io-ts'; -import { logEntryCursorRT, logEntryFieldRT } from '../../log_entry'; -import { logViewReferenceRT } from '../../log_views'; import { searchStrategyErrorRT } from '../common/errors'; export const LOG_ENTRY_SEARCH_STRATEGY = 'infra-log-entry'; diff --git a/x-pack/plugins/infra/common/log_views/url_state_storage_service.ts b/x-pack/plugins/infra/common/url_state_storage_service.ts similarity index 90% rename from x-pack/plugins/infra/common/log_views/url_state_storage_service.ts rename to x-pack/plugins/infra/common/url_state_storage_service.ts index 973fcd53ca98..5d701ad1876b 100644 --- a/x-pack/plugins/infra/common/log_views/url_state_storage_service.ts +++ b/x-pack/plugins/infra/common/url_state_storage_service.ts @@ -10,15 +10,15 @@ import { encode } from '@kbn/rison'; import type { Query } from '@kbn/es-query'; import { parse, stringify } from 'query-string'; import moment, { DurationInputObject } from 'moment'; -import type { FilterStateInUrl } from '../../public/observability_logs/log_stream_query_state'; -import type { PositionStateInUrl } from '../../public/observability_logs/log_stream_position_state/src/url_state_storage_service'; import { defaultFilterStateKey, defaultPositionStateKey, DEFAULT_REFRESH_INTERVAL, LogViewReference, -} from '.'; -import type { TimeRange } from '../time'; +} from '@kbn/logs-shared-plugin/common'; +import type { FilterStateInUrl } from '../public/observability_logs/log_stream_query_state'; +import type { PositionStateInUrl } from '../public/observability_logs/log_stream_position_state/src/url_state_storage_service'; +import type { TimeRange } from './time'; export const defaultLogViewKey = 'logView'; diff --git a/x-pack/plugins/infra/kibana.jsonc b/x-pack/plugins/infra/kibana.jsonc index c9a9ca0e1867..973d979ed90a 100644 --- a/x-pack/plugins/infra/kibana.jsonc +++ b/x-pack/plugins/infra/kibana.jsonc @@ -20,6 +20,7 @@ "features", "fieldFormats", "lens", + "logsShared", "observability", "observabilityShared", "ruleRegistry", diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx index b51c9beae383..a8acacd8debd 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx @@ -52,7 +52,7 @@ export const ExplainLogRateSpikes: FC { const { services } = useKibanaContextForPlugin(); - const { dataViews, logViews } = services; + const { dataViews, logsShared } = services; const [dataView, setDataView] = useState(); const [esSearchQuery, setEsSearchQuery] = useState(); const [logSpikeParams, setLogSpikeParams] = useState< @@ -61,9 +61,8 @@ export const ExplainLogRateSpikes: FC { const getDataView = async () => { - const { timestampField, dataViewReference } = await logViews.client.getResolvedLogView( - rule.params.logView - ); + const { timestampField, dataViewReference } = + await logsShared.logViews.client.getResolvedLogView(rule.params.logView); if (dataViewReference.id) { const logDataView = await dataViews.get(dataViewReference.id); @@ -94,7 +93,7 @@ export const ExplainLogRateSpikes: FC import('./components/logs_history_chart')); @@ -44,7 +44,7 @@ const AlertDetailsAppSection = ({ alert, setAlertSummaryFields, }: AlertDetailsAppSectionProps) => { - const { observability, logViews } = useKibanaContextForPlugin().services; + const { observability, logsShared } = useKibanaContextForPlugin().services; const theme = useTheme(); const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; @@ -66,7 +66,7 @@ const AlertDetailsAppSection = ({ const { derivedDataView } = useLogView({ initialLogViewReference: rule.params.logView, - logViews: logViews.client, + logViews: logsShared.logViews.client, }); const { hasAtLeast } = useLicense(); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx index df324169c98e..bb2dbc7afdc3 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx @@ -9,7 +9,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiPopover, EuiContextMenuItem, EuiContextMenuPanel, EuiHeaderLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useLogViewContext } from '../../../hooks/use_log_view'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { AlertFlyout } from './alert_flyout'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index c5af2938f545..33f63fe6a49c 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import type { PersistedLogViewReference, ResolvedLogViewField, -} from '../../../../../common/log_views'; +} from '@kbn/logs-shared-plugin/common'; import { Criterion } from './criterion'; import { PartialRuleParams, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx index 2e394bb093ec..f2bb1d8ed578 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx @@ -22,12 +22,12 @@ import { i18n } from '@kbn/i18n'; import { isFinite, isNumber } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; import type { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; +import type { ResolvedLogViewField } from '@kbn/logs-shared-plugin/common'; import { Comparator, ComparatorToi18nMap, Criterion as CriterionType, } from '../../../../../common/alerting/logs/log_threshold/types'; -import type { ResolvedLogViewField } from '../../../../../common/log_views'; const firstCriterionFieldPrefix = i18n.translate( 'xpack.infra.logs.alertFlyout.firstCriterionFieldPrefix', diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index b8dbc56402b2..6b2357a4f461 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -21,9 +21,9 @@ import { } from '@elastic/charts'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { getChartTheme } from '../../../../utils/get_chart_theme'; import { useIsDarkMode } from '../../../../hooks/use_is_dark_mode'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; import { ExecutionTimeRange } from '../../../../types'; import { ChartContainer, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index d9275a1ff017..475bb0b1fbde 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -13,7 +13,8 @@ import { ForLastExpression, RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; -import { PersistedLogViewReference, ResolvedLogViewField } from '../../../../../common/log_views'; +import { LogViewProvider, useLogViewContext } from '@kbn/logs-shared-plugin/public'; +import { PersistedLogViewReference, ResolvedLogViewField } from '@kbn/logs-shared-plugin/common'; import { Comparator, isOptimizableGroupedThreshold, @@ -28,7 +29,6 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; import { ObjectEntries } from '../../../../../common/utility_types'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; -import { LogViewProvider, useLogViewContext } from '../../../../hooks/use_log_view'; import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; import { errorsRT } from '../../validation'; import { Criteria } from './criteria'; @@ -95,7 +95,7 @@ export const ExpressionEditor: React.FC< > = (props) => { const isInternal = props.metadata?.isInternal ?? false; const { - services: { logViews }, + services: { logsShared }, } = useKibanaContextForPlugin(); // injected during alert registration return ( @@ -105,7 +105,7 @@ export const ExpressionEditor: React.FC< ) : ( - + diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx index d633a055c46e..ad042d77a584 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx @@ -8,6 +8,7 @@ import { HttpHandler } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { isRatioRule } from '../../../../../../common/alerting/logs/log_threshold'; import { GetLogAlertsChartPreviewDataAlertParamsSubset, @@ -16,7 +17,6 @@ import { getLogAlertsChartPreviewDataSuccessResponsePayloadRT, LOG_ALERTS_CHART_PREVIEW_DATA_PATH, } from '../../../../../../common/http_api'; -import { PersistedLogViewReference } from '../../../../../../common/log_views'; import { decodeOrThrow } from '../../../../../../common/runtime_types'; import { ExecutionTimeRange } from '../../../../../types'; import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/log_view_switcher.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/log_view_switcher.tsx index f3988002a7f7..8dfa7295b676 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/log_view_switcher.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/log_view_switcher.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexItem, EuiFlexGroup, EuiExpression, EuiToolTip } from '@elastic/eui'; -import { ResolvedLogView } from '../../../../../common/log_views'; +import { ResolvedLogView } from '@kbn/logs-shared-plugin/common'; const description = i18n.translate('xpack.infra.logs.alertFlyout.logViewDescription', { defaultMessage: 'Log View', diff --git a/x-pack/plugins/infra/public/apps/discover_app.tsx b/x-pack/plugins/infra/public/apps/discover_app.tsx index dd99a5e4dd62..2ea704fa9b21 100644 --- a/x-pack/plugins/infra/public/apps/discover_app.tsx +++ b/x-pack/plugins/infra/public/apps/discover_app.tsx @@ -6,8 +6,8 @@ */ import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import type { AppMountParameters, CoreStart } from '@kbn/core/public'; +import { getLogViewReferenceFromUrl } from '@kbn/logs-shared-plugin/public'; import type { InfraClientStartExports } from '../types'; -import { getLogViewReferenceFromUrl } from '../observability_logs/log_view_state'; export const renderApp = ( core: CoreStart, diff --git a/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/log_entries.ts b/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/log_entries.ts index 6212c256647e..35549673701f 100644 --- a/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/log_entries.ts +++ b/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/log_entries.ts @@ -11,11 +11,11 @@ import type { IKibanaSearchResponse, ISearchOptions, } from '@kbn/data-plugin/public'; +import { defaultLogViewAttributes } from '@kbn/logs-shared-plugin/common'; import { type LogEntriesSearchResponsePayload, LOG_ENTRIES_SEARCH_STRATEGY, } from '../../../../../../common/search_strategies/log_entries/log_entries'; -import { defaultLogViewAttributes } from '../../../../../../common/log_views'; import { generateFakeEntries } from '../../../../../test_utils/entries'; export const getLogEntries = ({ params }: IKibanaSearchRequest, options?: ISearchOptions) => { diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx index d928eda5a213..d2311b91fa1b 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/logs/logs.tsx @@ -11,10 +11,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; -import { DEFAULT_LOG_VIEW, LogViewReference } from '../../../../../common/log_views'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; +import { DEFAULT_LOG_VIEW, LogViewReference } from '@kbn/logs-shared-plugin/common'; import type { InventoryItemType } from '../../../../../common/inventory_models/types'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; -import { LogStream } from '../../../log_stream'; import { findInventoryFields } from '../../../../../common/inventory_models'; import { InfraLoadingPanel } from '../../../loading'; diff --git a/x-pack/plugins/infra/public/components/asset_details/types.ts b/x-pack/plugins/infra/public/components/asset_details/types.ts index c42953ab8fb3..a3d519641da3 100644 --- a/x-pack/plugins/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/infra/public/components/asset_details/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { LogViewReference } from '../../../common/log_views'; +import { LogViewReference } from '@kbn/logs-shared-plugin/common'; import type { InventoryItemType } from '../../../common/inventory_models/types'; import type { InfraAssetMetricType, SnapshotCustomMetricInput } from '../../../common/http_api'; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx index d2d59fe51935..1986354b5e9b 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx @@ -13,10 +13,10 @@ import { Subscription } from 'rxjs'; import type { TimeRange } from '@kbn/es-query'; import { Embeddable, EmbeddableInput, IContainer } from '@kbn/embeddable-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; import { CoreProviders } from '../../apps/common_providers'; import { InfraClientStartDeps, InfraClientStartExports } from '../../types'; import { datemathToEpochMillis } from '../../utils/datemath'; -import { LazyLogStreamWrapper } from './lazy_log_stream_wrapper'; export const LOG_STREAM_EMBEDDABLE = 'LOG_STREAM_EMBEDDABLE'; @@ -90,7 +90,7 @@ export class LogStreamEmbeddable extends Embeddable { >
- void; diff --git a/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx index e60a606ce0ce..6533bebf74fc 100644 --- a/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import classNames from 'classnames'; import * as React from 'react'; -import { LogEntryTime } from '../../../../common/log_entry'; +import { LogEntryTime } from '@kbn/logs-shared-plugin/common'; import { LogSearchButtons } from './log_search_buttons'; import { LogSearchInput } from './log_search_input'; diff --git a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts index 1ea8f71da129..8327a14c2825 100644 --- a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts +++ b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts @@ -7,8 +7,7 @@ import { useState } from 'react'; import createContainer from 'constate'; -import { LogViewReference } from '../../../../common/log_views'; -import { LogEntry } from '../../../../common/log_entry'; +import { LogEntry, LogViewReference } from '@kbn/logs-shared-plugin/common'; interface ViewLogInContextProps { logViewReference: LogViewReference; diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts deleted file mode 100644 index 3d95dfb72abb..000000000000 --- a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts +++ /dev/null @@ -1,77 +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 { interpret } from 'xstate'; -import { createLogViewMock } from '../../common/log_views/log_view.mock'; -import { createResolvedLogViewMockFromAttributes } from '../../common/log_views/resolved_log_view.mock'; -import { - createLogViewNotificationChannel, - createPureLogViewStateMachine, -} from '../observability_logs/log_view_state/src'; -import { useLogView } from './use_log_view'; - -type UseLogView = typeof useLogView; -type IUseLogView = ReturnType; - -const defaultLogViewId = 'default'; - -export const createUninitializedUseLogViewMock = - (logViewId: string = defaultLogViewId) => - (): IUseLogView => ({ - derivedDataView: undefined, - hasFailedLoading: false, - hasFailedLoadingLogView: false, - hasFailedLoadingLogViewStatus: false, - hasFailedResolvingLogView: false, - isLoading: false, - isLoadingLogView: false, - isLoadingLogViewStatus: false, - isResolvingLogView: false, - isUninitialized: true, - latestLoadLogViewFailures: [], - load: jest.fn(), - retry: jest.fn(), - logView: undefined, - logViewReference: { type: 'log-view-reference', logViewId }, - logViewStatus: undefined, - resolvedLogView: undefined, - update: jest.fn(), - changeLogViewReference: jest.fn(), - revertToDefaultLogView: jest.fn(), - logViewStateService: interpret( - createPureLogViewStateMachine({ logViewReference: { type: 'log-view-reference', logViewId } }) - ), - logViewStateNotifications: createLogViewNotificationChannel(), - isPersistedLogView: false, - isInlineLogView: false, - }); - -export const createLoadingUseLogViewMock = - (logViewId: string = defaultLogViewId) => - (): IUseLogView => ({ - ...createUninitializedUseLogViewMock(logViewId)(), - isLoading: true, - isLoadingLogView: true, - isLoadingLogViewStatus: true, - isResolvingLogView: true, - }); - -export const createLoadedUseLogViewMock = async (logViewId: string = defaultLogViewId) => { - const logView = createLogViewMock(logViewId); - const resolvedLogView = await createResolvedLogViewMockFromAttributes(logView.attributes); - - return (): IUseLogView => { - return { - ...createUninitializedUseLogViewMock(logViewId)(), - logView, - resolvedLogView, - logViewStatus: { - index: 'available', - }, - }; - }; -}; diff --git a/x-pack/plugins/infra/public/index.ts b/x-pack/plugins/infra/public/index.ts index 4bd094b3d79f..49a0257a22b3 100644 --- a/x-pack/plugins/infra/public/index.ts +++ b/x-pack/plugins/infra/public/index.ts @@ -29,6 +29,4 @@ export { InfraFormatterType } from './lib/lib'; export type InfraAppId = 'logs' | 'metrics'; // Shared components -export { LazyLogStreamWrapper as LogStream } from './components/log_stream/lazy_log_stream_wrapper'; -export type { LogStreamProps } from './components/log_stream'; export type { InfraClientStartExports } from './types'; diff --git a/x-pack/plugins/infra/public/mocks.tsx b/x-pack/plugins/infra/public/mocks.tsx index d977e13ddd98..9b232c515ce8 100644 --- a/x-pack/plugins/infra/public/mocks.tsx +++ b/x-pack/plugins/infra/public/mocks.tsx @@ -8,14 +8,12 @@ import React from 'react'; import { createLocatorMock } from '../common/locators/locators.mock'; import { createInventoryViewsServiceStartMock } from './services/inventory_views/inventory_views_service.mock'; -import { createLogViewsServiceStartMock } from './services/log_views/log_views_service.mock'; import { createMetricsExplorerViewsServiceStartMock } from './services/metrics_explorer_views/metrics_explorer_views_service.mock'; import { createTelemetryServiceMock } from './services/telemetry/telemetry_service.mock'; import { InfraClientStartExports } from './types'; export const createInfraPluginStartMock = () => ({ inventoryViews: createInventoryViewsServiceStartMock(), - logViews: createLogViewsServiceStartMock(), metricsExplorerViews: createMetricsExplorerViewsServiceStartMock(), telemetry: createTelemetryServiceMock(), locators: createLocatorMock(), diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts index b7c02e916dc5..d2a71c65702e 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts @@ -8,7 +8,8 @@ import { RefreshInterval } from '@kbn/data-plugin/public'; import { TimeRange } from '@kbn/es-query'; import { actions, ActorRefFrom, createMachine, EmittedFrom } from 'xstate'; -import { DEFAULT_REFRESH_INTERVAL } from '../../../../../common/log_views'; +import { DEFAULT_REFRESH_INTERVAL } from '@kbn/logs-shared-plugin/common'; +import type { LogViewNotificationChannel } from '@kbn/logs-shared-plugin/public'; import { datemathToEpochMillis } from '../../../../utils/datemath'; import { createLogStreamPositionStateMachine } from '../../../log_stream_position_state/src/state_machine'; import { @@ -16,7 +17,6 @@ import { DEFAULT_TIMERANGE, LogStreamQueryStateMachineDependencies, } from '../../../log_stream_query_state'; -import type { LogViewNotificationChannel } from '../../../log_view_state'; import { OmitDeprecatedState } from '../../../xstate_helpers'; import { waitForInitialQueryParameters, diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts index eb42dccdf248..12c707ebb91f 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts @@ -6,8 +6,13 @@ */ import { TimeRange } from '@kbn/es-query'; +import type { LogViewStatus } from '@kbn/logs-shared-plugin/common'; +import type { + LogViewContextWithError, + LogViewContextWithResolvedLogView, + LogViewNotificationEvent, +} from '@kbn/logs-shared-plugin/public'; import { TimeKey } from '../../../../../common/time'; -import type { LogViewStatus } from '../../../../../common/log_views'; import { JumpToTargetPositionEvent, LogStreamPositionContext, @@ -22,11 +27,6 @@ import { UpdateTimeRangeEvent, } from '../../../log_stream_query_state'; import { LogStreamQueryNotificationEvent } from '../../../log_stream_query_state/src/notifications'; -import type { - LogViewContextWithError, - LogViewContextWithResolvedLogView, - LogViewNotificationEvent, -} from '../../../log_view_state'; export interface ReceivedInitialQueryParametersEvent { type: 'RECEIVED_INITIAL_QUERY_PARAMETERS'; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_position_state/src/url_state_storage_service.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_position_state/src/url_state_storage_service.ts index 5607bf920705..c98ab9e14744 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_position_state/src/url_state_storage_service.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_position_state/src/url_state_storage_service.ts @@ -10,6 +10,7 @@ import { IKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugi import * as Either from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/function'; import { InvokeCreator } from 'xstate'; +import { replaceStateKeyInQueryString } from '../../../../common/url_state_storage_service'; import { minimalTimeKeyRT, pickTimeKey } from '../../../../common/time'; import { createPlainError, formatErrors } from '../../../../common/runtime_types'; import type { LogStreamPositionContext, LogStreamPositionEvent } from './types'; @@ -97,3 +98,13 @@ export type PositionStateInUrl = rt.TypeOf; const decodePositionQueryValueFromUrl = (queryValueFromUrl: unknown) => { return positionStateInUrlRT.decode(queryValueFromUrl); }; + +export const replaceLogPositionInQueryString = (time?: number) => + Number.isNaN(time) || time == null + ? (value: string) => value + : replaceStateKeyInQueryString(defaultPositionStateKey, { + position: { + time, + tiebreaker: 0, + }, + }); diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts index 1a69642037d1..1c0de464121c 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts @@ -14,7 +14,7 @@ import type { import { EsQueryConfig } from '@kbn/es-query'; import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { actions, ActorRefFrom, createMachine, SpecialTargets, send } from 'xstate'; -import { DEFAULT_REFRESH_INTERVAL } from '../../../../common/log_views'; +import { DEFAULT_REFRESH_INTERVAL } from '@kbn/logs-shared-plugin/common'; import { OmitDeprecatedState, sendIfDefined } from '../../xstate_helpers'; import { logStreamQueryNotificationEventSelectors } from './notifications'; import { diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts index 448fb941e037..fb65fcd987a1 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts @@ -16,9 +16,11 @@ import { defaultFilterStateKey, defaultPositionStateKey, DEFAULT_REFRESH_INTERVAL, - getTimeRangeStartFromTime, +} from '@kbn/logs-shared-plugin/common'; +import { getTimeRangeEndFromTime, -} from '../../../../common/log_views'; + getTimeRangeStartFromTime, +} from '../../../../common/url_state_storage_service'; import { minimalTimeKeyRT } from '../../../../common/time'; import { datemathStringRT } from '../../../utils/datemath'; import { createPlainError, formatErrors } from '../../../../common/runtime_types'; diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx index 78f1c5f15dee..5a40bd5d3229 100644 --- a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx @@ -7,7 +7,7 @@ import { EuiButton } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { useLogViewContext } from '../../../hooks/use_log_view'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; export const StateMachinePlayground = () => { const { changeLogViewReference, revertToDefaultLogView, update, isLoading, logViewStateService } = diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx index 3bbe00c5314c..663df4c0f4d1 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx @@ -7,7 +7,7 @@ import { useEffect } from 'react'; import { useLocation, useParams } from 'react-router-dom'; -import { DEFAULT_LOG_VIEW } from '../../../common/log_views'; +import { DEFAULT_LOG_VIEW } from '@kbn/logs-shared-plugin/common'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx index 66898bf38b4b..e3382d43c0e1 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx @@ -7,7 +7,7 @@ import { useEffect } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { DEFAULT_LOG_VIEW } from '../../../common/log_views'; +import { DEFAULT_LOG_VIEW } from '@kbn/logs-shared-plugin/common'; import { InventoryItemType } from '../../../common/inventory_models/types'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx index bde85aa99ec7..d6a1e9f2ddc5 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useEffect } from 'react'; import type { LazyObservabilityPageTemplateProps } from '@kbn/observability-shared-plugin/public'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { isJobStatusWithResults } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; import { @@ -22,7 +23,6 @@ import { import { SubscriptionSplashPage } from '../../../components/subscription_splash_content'; import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis'; import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { LogsPageTemplate } from '../shared/page_template'; import { LogEntryCategoriesResultsContent } from './page_results_content'; import { LogEntryCategoriesSetupContent } from './page_setup_content'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx index 6e1d3f4a4f0b..5cb6a12166c5 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx @@ -6,12 +6,12 @@ */ import React from 'react'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { InlineLogViewSplashPage } from '../../../components/logging/inline_log_view_splash_page'; import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ children }) => { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index f26f8768a445..e1e317136dee 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -15,6 +15,7 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { MLJobsAwaitingNodeWarning, ML_PAGES, useMlHref } from '@kbn/ml-plugin/public'; import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { TimeRange } from '../../../../common/time/time_range'; import { CategoryJobNoticesSection } from '../../../components/logging/log_analysis_job_status'; import { AnalyzeInMlButton } from '../../../components/logging/log_analysis_results'; @@ -24,7 +25,6 @@ import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_ import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { LogsPageTemplate } from '../shared/page_template'; import { PageViewLogInContext } from '../stream/page_view_log_in_context'; import { TopCategoriesSection } from './sections/top_categories'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_details_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_details_row.tsx index dbf143f95d0b..34cced7d92ff 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_details_row.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_details_row.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect } from 'react'; -import { PersistedLogViewReference } from '../../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { useLogEntryCategoryExamples } from '../../use_log_entry_category_examples'; import { LogEntryExampleMessages } from '../../../../../components/logging/log_entry_examples/log_entry_examples'; import { TimeRange } from '../../../../../../common/time/time_range'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx index 96c0a23ac755..0811ec170853 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx @@ -5,28 +5,27 @@ * 2.0. */ -import React, { useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; +import { LogEntry, LogEntryContext } from '@kbn/logs-shared-plugin/common'; +import { + LogEntryColumn, + LogEntryContextMenu, + LogEntryFieldColumn, + LogEntryMessageColumn, + LogEntryRowWrapper, + LogEntryTimestampColumn, +} from '@kbn/logs-shared-plugin/public'; +import { useLinkProps, useUiTracker } from '@kbn/observability-shared-plugin/public'; import { encode } from '@kbn/rison'; import moment from 'moment'; - -import { useUiTracker, useLinkProps } from '@kbn/observability-shared-plugin/public'; -import { LogEntry, LogEntryContext } from '../../../../../../common/log_entry'; -import { TimeRange } from '../../../../../../common/time'; +import React, { useCallback, useState } from 'react'; import { getFriendlyNameForPartitionId, partitionField, } from '../../../../../../common/log_analysis'; +import { TimeRange } from '../../../../../../common/time'; import { useViewLogInProviderContext } from '../../../../../containers/logs/view_log_in_context'; -import { - LogEntryColumn, - LogEntryFieldColumn, - LogEntryMessageColumn, - LogEntryRowWrapper, - LogEntryTimestampColumn, -} from '../../../../../components/logging/log_text_stream'; import { LogColumnConfiguration } from '../../../../../utils/source_configuration'; -import { LogEntryContextMenu } from '../../../../../components/logging/log_text_stream/log_entry_context_menu'; export const exampleMessageScale = 'medium' as const; export const exampleTimestampFormat = 'dateTime' as const; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx index c9f93ee618dd..6dd07a80c865 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx @@ -9,7 +9,7 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { PersistedLogViewReference } from '../../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { LogEntryCategory } from '../../../../../../common/log_analysis'; import { TimeRange } from '../../../../../../common/time'; import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx index 3d06a212fe58..f119a08cbd10 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx @@ -12,7 +12,7 @@ import React, { useMemo, useCallback } from 'react'; import useSet from 'react-use/lib/useSet'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { PersistedLogViewReference } from '../../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { LogEntryCategory, LogEntryCategoryDataset, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts index db17c0b866b7..14e7ebfbebd3 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts @@ -6,7 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { getLogEntryCategoryDatasetsRequestPayloadRT, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts index a27e734235c3..3e4947ca1e84 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts @@ -6,7 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { getLogEntryCategoryExamplesRequestPayloadRT, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts index 5104ad897c88..c4a1b6d095a2 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts @@ -6,7 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { getLogEntryCategoriesRequestPayloadRT, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts index 7ef8d57b29d9..20f7adb10685 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts @@ -7,7 +7,7 @@ import { useMemo, useState } from 'react'; -import { PersistedLogViewReference } from '../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { GetLogEntryCategoriesSuccessResponsePayload, GetLogEntryCategoryDatasetsSuccessResponsePayload, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx index 8152cae42644..c5516fdbc02f 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx @@ -6,7 +6,7 @@ */ import { useMemo, useState } from 'react'; -import { PersistedLogViewReference } from '../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { LogEntryCategoryExample } from '../../../../common/http_api'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx index 1f6fe04e5916..533381dcbf7c 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import React, { memo, useCallback, useEffect } from 'react'; import useInterval from 'react-use/lib/useInterval'; import type { LazyObservabilityPageTemplateProps } from '@kbn/observability-shared-plugin/public'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { isJobStatusWithResults } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; import { @@ -24,7 +25,6 @@ import { SubscriptionSplashPage } from '../../../components/subscription_splash_ import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis'; import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; import { useLogEntryRateModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_rate'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { LogsPageTemplate } from '../shared/page_template'; import { LogEntryRateResultsContent } from './page_results_content'; import { LogEntryRateSetupContent } from './page_setup_content'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx index 7e1381cbd4f2..46ce90cff63c 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { InlineLogViewSplashPage } from '../../../components/logging/inline_log_view_splash_page'; import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; @@ -13,7 +14,6 @@ import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_a import { LogEntryRateModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_rate'; import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) => { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx index b535c1fd155d..a4d861e38ade 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx @@ -14,6 +14,7 @@ import { encode } from '@kbn/rison'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { MLJobsAwaitingNodeWarning } from '@kbn/ml-plugin/public'; import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; +import { useLogViewContext, LogEntryFlyout } from '@kbn/logs-shared-plugin/public'; import { isJobStatusWithResults } from '../../../../common/log_analysis'; import { TimeKey } from '../../../../common/time'; import { @@ -23,12 +24,10 @@ import { import { DatasetsSelector } from '../../../components/logging/log_analysis_results/datasets_selector'; import { ManageJobsButton } from '../../../components/logging/log_analysis_setup/manage_jobs_button'; import { useLogAnalysisSetupFlyoutStateContext } from '../../../components/logging/log_analysis_setup/setup_flyout'; -import { LogEntryFlyout } from '../../../components/logging/log_entry_flyout'; import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis/log_analysis_capabilities'; import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; import { useLogEntryRateModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_rate'; import { useLogEntryFlyoutContext } from '../../../containers/logs/log_flyout'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { LogsPageTemplate } from '../shared/page_template'; import { AnomaliesResults } from './sections/anomalies'; import { useDatasetFiltering } from './use_dataset_filtering'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx index 493b5c4077c6..5fe150e3c272 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx @@ -11,10 +11,10 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import useMount from 'react-use/lib/useMount'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { isCategoryAnomaly, LogEntryAnomaly } from '../../../../../../common/log_analysis'; import { TimeRange } from '../../../../../../common/time/time_range'; import { LogEntryExampleMessages } from '../../../../../components/logging/log_entry_examples/log_entry_examples'; -import { useLogViewContext } from '../../../../../hooks/use_log_view'; import { useLogEntryExamples } from '../../use_log_entry_examples'; import { LogEntryExampleMessage, LogEntryExampleMessageHeaders } from './log_entry_example'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx index 5b23f5f9e2a4..288fc3df39c8 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx @@ -12,8 +12,6 @@ import { i18n } from '@kbn/i18n'; import { useMlHref, ML_PAGES } from '@kbn/ml-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useLinkProps, shouldHandleLinkEvent } from '@kbn/observability-shared-plugin/public'; -import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; -import { getFriendlyNameForPartitionId } from '../../../../../../common/log_analysis'; import { LogEntryColumn, LogEntryFieldColumn, @@ -23,11 +21,11 @@ import { LogEntryContextMenu, LogEntryColumnWidths, iconColumnId, -} from '../../../../../components/logging/log_text_stream'; -import { LogColumnHeadersWrapper, LogColumnHeader, -} from '../../../../../components/logging/log_text_stream/column_headers'; +} from '@kbn/logs-shared-plugin/public'; +import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; +import { getFriendlyNameForPartitionId } from '../../../../../../common/log_analysis'; import { TimeRange } from '../../../../../../common/time/time_range'; import { partitionField } from '../../../../../../common/log_analysis/job_parameters'; import { LogEntryExample, isCategoryAnomaly } from '../../../../../../common/log_analysis'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts index dc8ab4772473..b6a515ae6f13 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts @@ -6,7 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { getLogEntryAnomaliesRequestPayloadRT, getLogEntryAnomaliesSuccessReponsePayloadRT, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts index 093a90600858..a93712c5d5a8 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts @@ -6,7 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { getLogEntryAnomaliesDatasetsRequestPayloadRT, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts index f6d90692ad47..6cf35b95868e 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts @@ -6,7 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; -import { PersistedLogViewReference } from '../../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { getLogEntryExamplesRequestPayloadRT, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts index db4fc7796417..745b5a7cd5ae 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts @@ -7,7 +7,7 @@ import { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; import useMount from 'react-use/lib/useMount'; -import { PersistedLogViewReference } from '../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { useTrackedPromise, CanceledPromiseError } from '../../../utils/use_tracked_promise'; import { callGetLogEntryAnomaliesAPI } from './service_calls/get_log_entry_anomalies'; import { callGetLogEntryAnomaliesDatasetsAPI } from './service_calls/get_log_entry_anomalies_datasets'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts index a678f5deaf07..4301f08ab41d 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts @@ -6,7 +6,7 @@ */ import { useMemo, useState } from 'react'; -import { PersistedLogViewReference } from '../../../../common/log_views'; +import { PersistedLogViewReference } from '@kbn/logs-shared-plugin/common'; import { LogEntryExample } from '../../../../common/log_analysis'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; diff --git a/x-pack/plugins/infra/public/pages/logs/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx index e185a62ea964..f4112b7bd0a0 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx @@ -6,21 +6,21 @@ */ import React, { useState } from 'react'; -import { LogAnalysisCapabilitiesProvider } from '../../containers/logs/log_analysis'; -import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; -import { LogViewProvider } from '../../hooks/use_log_view'; import { + LogViewProvider, initializeFromUrl as createInitializeFromUrl, updateContextInUrl as createUpdateContextInUrl, listenForUrlChanges as createListenForUrlChanges, -} from '../../observability_logs/log_view_state/src/url_state_storage_service'; +} from '@kbn/logs-shared-plugin/public'; +import { LogAnalysisCapabilitiesProvider } from '../../containers/logs/log_analysis'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { useKbnUrlStateStorageFromRouterContext } from '../../utils/kbn_url_state_context'; export const LogsPageProviders: React.FunctionComponent = ({ children }) => { const { services: { notifications: { toasts: toastsService }, - logViews: { client }, + logsShared, }, } = useKibanaContextForPlugin(); @@ -38,7 +38,7 @@ export const LogsPageProviders: React.FunctionComponent = ({ children }) => { return ( { return ( - + {({ buckets, start, end }) => ( )} - + ); }} @@ -293,6 +299,16 @@ export const StreamPageLogsContent = React.memo<{ ); }); +const WithSummaryAndQuery = (props: Omit) => { + const serializedParsedQuery = useSelector(useLogStreamPageStateContext(), (logStreamPageState) => + logStreamPageState.matches({ hasLogViewIndices: 'initialized' }) + ? stringify(logStreamPageState.context.parsedQuery) + : null + ); + + return ; +}; + type InitializedLogStreamPageState = MatchedStateFromActor< LogStreamPageActorRef, { hasLogViewIndices: 'initialized' } diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index 0a9f5ae9395e..9b058bd03acd 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -7,20 +7,21 @@ import stringify from 'json-stable-stringify'; import React, { useMemo } from 'react'; +import { + LogHighlightsStateProvider, + LogPositionStateProvider, + LogStreamProvider, + useLogPositionStateContext, + useLogStreamContext, + useLogViewContext, +} from '@kbn/logs-shared-plugin/public'; import { LogStreamPageActorRef, LogStreamPageCallbacks, } from '../../../observability_logs/log_stream_page/state'; import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; -import { LogHighlightsStateProvider } from '../../../containers/logs/log_highlights/log_highlights'; -import { - LogPositionStateProvider, - useLogPositionStateContext, -} from '../../../containers/logs/log_position'; -import { LogStreamProvider, useLogStreamContext } from '../../../containers/logs/log_stream'; import { LogViewConfigurationProvider } from '../../../containers/logs/log_view_configuration'; import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; const ViewLogInContext: React.FC = ({ children }) => { diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 99c9d498fc6c..9ea8e60eef0b 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -8,15 +8,17 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; +import { + useLogHighlightsStateContext, + useLogPositionStateContext, + useLogViewContext, +} from '@kbn/logs-shared-plugin/public'; import { LogCustomizationMenu } from '../../../components/logging/log_customization_menu'; import { LogHighlightsMenu } from '../../../components/logging/log_highlights_menu'; import { LogTextScaleControls } from '../../../components/logging/log_text_scale_controls'; import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_controls'; -import { useLogHighlightsStateContext } from '../../../containers/logs/log_highlights/log_highlights'; -import { useLogPositionStateContext } from '../../../containers/logs/log_position'; import { useLogViewConfigurationContext } from '../../../containers/logs/log_view_configuration'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; -import { useLogViewContext } from '../../../hooks/use_log_view'; import { StreamLiveButton } from './components/stream_live_button'; export const LogsToolbar = () => { diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx index 15dbbcca7ce9..2917b7b21d78 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx @@ -17,10 +17,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { isEmpty } from 'lodash'; import React, { useCallback, useMemo } from 'react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { LogEntry } from '../../../../common/log_entry'; +import { LogEntry } from '@kbn/logs-shared-plugin/common'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; import { useViewLogInProviderContext } from '../../../containers/logs/view_log_in_context'; import { useViewportDimensions } from '../../../utils/use_viewport_dimensions'; -import { LogStream } from '../../../components/log_stream'; const MODAL_MARGIN = 25; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx index ac3981026ea8..cd2537418e46 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { LogViewReference } from '../../../../../../../common/log_views'; +import { LogViewReference } from '@kbn/logs-shared-plugin/common'; import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana'; interface LogsLinkToStreamProps { diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx index 6fe796d33e90..66cbc9d1c1b1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx @@ -8,8 +8,8 @@ import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; import { InfraLoadingPanel } from '../../../../../../components/loading'; -import { LogStream } from '../../../../../../components/log_stream'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; import { useLogsSearchUrlState } from '../../../hooks/use_logs_search_url_state'; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_log_view_reference.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_log_view_reference.ts index 733585518952..46a062001bb7 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_log_view_reference.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_log_view_reference.ts @@ -8,8 +8,8 @@ // import { useMemo } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { v4 as uuidv4 } from 'uuid'; +import { DEFAULT_LOG_VIEW, LogViewReference } from '@kbn/logs-shared-plugin/common'; import { useLazyRef } from '../../../../hooks/use_lazy_ref'; -import { DEFAULT_LOG_VIEW, type LogViewReference } from '../../../../../common/log_views'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; interface Props { @@ -18,13 +18,11 @@ interface Props { } export const useLogViewReference = ({ id, extraFields = [] }: Props) => { const { - services: { - logViews: { client }, - }, + services: { logsShared }, } = useKibanaContextForPlugin(); const { loading, value: defaultLogView } = useAsync( - () => client.getLogView(DEFAULT_LOG_VIEW), + () => logsShared.logViews.client.getLogView(DEFAULT_LOG_VIEW), [] ); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/logs.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/logs.tsx index 3024ff3d1bd0..ddbf4ae52788 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/logs.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/logs.tsx @@ -14,9 +14,9 @@ import { EuiFlexGroup } from '@elastic/eui'; import { EuiFlexItem } from '@elastic/eui'; import { EuiButtonEmpty } from '@elastic/eui'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { LogStream } from '@kbn/logs-shared-plugin/public'; import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana'; import { TabContent, TabProps } from './shared'; -import { LogStream } from '../../../../../../components/log_stream'; import { useWaffleOptionsContext } from '../../../hooks/use_waffle_options'; import { findInventoryFields } from '../../../../../../../common/inventory_models'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts index 73b09017fecc..9b48b9391f9b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts @@ -13,7 +13,7 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { replaceStateKeyInQueryString } from '../../../../../common/log_views'; +import { replaceStateKeyInQueryString } from '../../../../../common/url_state_storage_service'; import { useUrlState } from '../../../../utils/use_url_state'; const parseRange = (range: MetricsTimeInput) => { diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 36c873c3b22d..7ea1dbdfa7cd 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -19,7 +19,6 @@ import { ObservabilityTriggerId } from '@kbn/observability-shared-plugin/common' import { BehaviorSubject, combineLatest, from } from 'rxjs'; import { map } from 'rxjs/operators'; import { DISCOVER_APP_TARGET, LOGS_APP_TARGET } from '../common/constants'; -import { defaultLogViewsStaticConfig } from '../common/log_views'; import { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -39,7 +38,6 @@ import { import { createMetricsFetchData, createMetricsHasData } from './metrics_overview_fetchers'; import { registerFeatures } from './register_feature'; import { InventoryViewsService } from './services/inventory_views'; -import { LogViewsService } from './services/log_views'; import { MetricsExplorerViewsService } from './services/metrics_explorer_views'; import { TelemetryService } from './services/telemetry'; import { @@ -56,7 +54,6 @@ import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_ export class Plugin implements InfraClientPluginClass { public config: InfraPublicConfig; private inventoryViews: InventoryViewsService; - private logViews: LogViewsService; private metricsExplorerViews: MetricsExplorerViewsService; private telemetry: TelemetryService; private locators?: InfraLocators; @@ -68,10 +65,6 @@ export class Plugin implements InfraClientPluginClass { this.config = context.config.get(); this.inventoryViews = new InventoryViewsService(); - this.logViews = new LogViewsService({ - messageFields: - this.config.sources?.default?.fields?.message ?? defaultLogViewsStaticConfig.messageFields, - }); this.metricsExplorerViews = new MetricsExplorerViewsService(); this.telemetry = new TelemetryService(); this.appTarget = this.config.logs.app_target; @@ -106,6 +99,10 @@ export class Plugin implements InfraClientPluginClass { fetchData: createMetricsFetchData(core.getStartServices), }); + pluginsSetup.logsShared.logViews.setLogViewsStaticConfig({ + messageFields: this.config.sources?.default?.fields?.message, + }); + const startDep$AndHostViewFlag$ = combineLatest([ from(core.getStartServices()), core.uiSettings.get$(enableInfrastructureHostsView), @@ -342,12 +339,6 @@ export class Plugin implements InfraClientPluginClass { http: core.http, }); - const logViews = this.logViews.start({ - http: core.http, - dataViews: plugins.dataViews, - search: plugins.data.search, - }); - const metricsExplorerViews = this.metricsExplorerViews.start({ http: core.http, }); @@ -356,7 +347,6 @@ export class Plugin implements InfraClientPluginClass { const startContract: InfraClientStartExports = { inventoryViews, - logViews, metricsExplorerViews, telemetry, locators: this.locators!, diff --git a/x-pack/plugins/infra/public/test_utils/entries.ts b/x-pack/plugins/infra/public/test_utils/entries.ts index 4dc3732fd49d..35dad808cdf4 100644 --- a/x-pack/plugins/infra/public/test_utils/entries.ts +++ b/x-pack/plugins/infra/public/test_utils/entries.ts @@ -6,16 +6,7 @@ */ import faker from 'faker'; -import { LogEntry } from '../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../common/log_views'; - -export const ENTRIES_EMPTY = { - data: { - entries: [], - topCursor: null, - bottomCursor: null, - }, -}; +import { LogEntry, LogViewColumnConfiguration } from '@kbn/logs-shared-plugin/common'; export function generateFakeEntries( count: number, diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index 5c78c1534a5a..488d92573e74 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -38,6 +38,10 @@ import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { CasesUiStart } from '@kbn/cases-plugin/public'; import { DiscoverStart } from '@kbn/discover-plugin/public'; import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { + LogsSharedClientSetupExports, + LogsSharedClientStartExports, +} from '@kbn/logs-shared-plugin/public'; import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { UnwrapPromise } from '../common/utility_types'; @@ -46,7 +50,6 @@ import type { UseNodeMetricsTableOptions, } from './components/infrastructure_node_metrics_tables/shared'; import { InventoryViewsServiceStart } from './services/inventory_views'; -import { LogViewsServiceStart } from './services/log_views'; import { MetricsExplorerViewsServiceStart } from './services/metrics_explorer_views'; import { ITelemetryClient } from './services/telemetry'; import type { InfraLocators } from '../common/locators'; @@ -58,7 +61,6 @@ export interface InfraClientSetupExports { export interface InfraClientStartExports { inventoryViews: InventoryViewsServiceStart; - logViews: LogViewsServiceStart; metricsExplorerViews: MetricsExplorerViewsServiceStart; telemetry: ITelemetryClient; locators: InfraLocators; @@ -74,6 +76,7 @@ export interface InfraClientStartExports { } export interface InfraClientSetupDeps { + logsShared: LogsSharedClientSetupExports; home?: HomePublicPluginSetup; observability: ObservabilityPublicSetup; observabilityShared: ObservabilitySharedPluginSetup; @@ -97,6 +100,7 @@ export interface InfraClientStartDeps { embeddable?: EmbeddableStart; kibanaVersion?: string; lens: LensPublicStart; + logsShared: LogsSharedClientStartExports; ml: MlPluginStart; observability: ObservabilityPublicStart; observabilityShared: ObservabilitySharedPluginStart; diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts index 5ebacc0e7773..186e4c9bc1ed 100644 --- a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts +++ b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts @@ -12,7 +12,7 @@ import { FetchDataParams, LogsFetchDataResponse, } from '@kbn/observability-plugin/public'; -import { DEFAULT_LOG_VIEW } from '../../common/log_views'; +import { DEFAULT_LOG_VIEW } from '@kbn/logs-shared-plugin/common'; import { TIMESTAMP_FIELD } from '../../common/constants'; import { InfraClientStartDeps, InfraClientStartServicesAccessor } from '../types'; @@ -38,9 +38,11 @@ type StatsAndSeries = Pick; export function getLogsHasDataFetcher(getStartServices: InfraClientStartServicesAccessor) { return async () => { - const [, , { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); - const logViewStatus = await logViews.client.getResolvedLogViewStatus(resolvedLogView); + const [, { logsShared }] = await getStartServices(); + const resolvedLogView = await logsShared.logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); + const logViewStatus = await logsShared.logViews.client.getResolvedLogViewStatus( + resolvedLogView + ); const hasData = logViewStatus.index === 'available'; const indices = resolvedLogView.indices; @@ -56,8 +58,8 @@ export function getLogsOverviewDataFetcher( getStartServices: InfraClientStartServicesAccessor ): FetchData { return async (params) => { - const [, { data }, { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); + const [, { data, logsShared }] = await getStartServices(); + const resolvedLogView = await logsShared.logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); const { stats, series } = await fetchLogsOverview( { diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts index dad3dedc4bb4..85d7f1458691 100644 --- a/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts +++ b/x-pack/plugins/infra/public/utils/logs_overview_fetches.test.ts @@ -6,10 +6,11 @@ */ import { CoreStart } from '@kbn/core/public'; -import { of } from 'rxjs'; import { coreMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { createResolvedLogViewMock } from '../../common/log_views/resolved_log_view.mock'; +import { createResolvedLogViewMock } from '@kbn/logs-shared-plugin/common/mocks'; +import { createLogsSharedPluginStartMock } from '@kbn/logs-shared-plugin/public/mocks'; +import { of } from 'rxjs'; import { createInfraPluginStartMock } from '../mocks'; import { InfraClientStartDeps, InfraClientStartExports } from '../types'; import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './logs_overview_fetchers'; @@ -24,10 +25,18 @@ const DEFAULT_PARAMS = { function setup() { const core = coreMock.createStart(); const data = dataPluginMock.createStartContract(); + const logsShared = createLogsSharedPluginStartMock(); const pluginStart = createInfraPluginStartMock(); - const pluginDeps = { data } as InfraClientStartDeps; + const pluginDeps = { data, logsShared } as unknown as InfraClientStartDeps; const dataSearch = data.search.search as jest.MockedFunction; + const getResolvedLogView = logsShared.logViews.client.getResolvedLogView as jest.MockedFunction< + typeof logsShared.logViews.client.getResolvedLogView + >; + const getResolvedLogViewStatus = logsShared.logViews.client + .getResolvedLogViewStatus as jest.MockedFunction< + typeof logsShared.logViews.client.getResolvedLogViewStatus + >; const mockedGetStartServices = jest.fn(() => Promise.resolve<[CoreStart, InfraClientStartDeps, InfraClientStartExports]>([ @@ -36,7 +45,15 @@ function setup() { pluginStart, ]) ); - return { core, dataSearch, mockedGetStartServices, pluginStart }; + return { + core, + dataSearch, + mockedGetStartServices, + pluginDeps, + pluginStart, + getResolvedLogView, + getResolvedLogViewStatus, + }; } describe('Logs UI Observability Homepage Functions', () => { @@ -46,62 +63,59 @@ describe('Logs UI Observability Homepage Functions', () => { describe('getLogsHasDataFetcher()', () => { it('should return true when non-empty indices exist', async () => { - const { mockedGetStartServices, pluginStart } = setup(); + const { mockedGetStartServices, pluginDeps, getResolvedLogView, getResolvedLogViewStatus } = + setup(); - pluginStart.logViews.client.getResolvedLogView.mockResolvedValue( - createResolvedLogViewMock({ indices: 'test-index' }) - ); - pluginStart.logViews.client.getResolvedLogViewStatus.mockResolvedValue({ - index: 'available', - }); + getResolvedLogView.mockResolvedValue(createResolvedLogViewMock({ indices: 'test-index' })); + getResolvedLogViewStatus.mockResolvedValue({ index: 'available' }); const hasData = getLogsHasDataFetcher(mockedGetStartServices); const response = await hasData(); - expect(pluginStart.logViews.client.getResolvedLogViewStatus).toHaveBeenCalledTimes(1); + expect(pluginDeps.logsShared.logViews.client.getResolvedLogViewStatus).toHaveBeenCalledTimes( + 1 + ); expect(response).toEqual({ hasData: true, indices: 'test-index' }); }); it('should return false when only empty indices exist', async () => { - const { mockedGetStartServices, pluginStart } = setup(); + const { mockedGetStartServices, pluginDeps, getResolvedLogView, getResolvedLogViewStatus } = + setup(); - pluginStart.logViews.client.getResolvedLogView.mockResolvedValue( - createResolvedLogViewMock({ indices: 'test-index' }) - ); - pluginStart.logViews.client.getResolvedLogViewStatus.mockResolvedValue({ - index: 'empty', - }); + getResolvedLogView.mockResolvedValue(createResolvedLogViewMock({ indices: 'test-index' })); + getResolvedLogViewStatus.mockResolvedValue({ index: 'empty' }); const hasData = getLogsHasDataFetcher(mockedGetStartServices); const response = await hasData(); - expect(pluginStart.logViews.client.getResolvedLogViewStatus).toHaveBeenCalledTimes(1); + expect(pluginDeps.logsShared.logViews.client.getResolvedLogViewStatus).toHaveBeenCalledTimes( + 1 + ); expect(response).toEqual({ hasData: false, indices: 'test-index' }); }); it('should return false when no index exists', async () => { - const { mockedGetStartServices, pluginStart } = setup(); + const { mockedGetStartServices, pluginDeps, getResolvedLogView, getResolvedLogViewStatus } = + setup(); - pluginStart.logViews.client.getResolvedLogView.mockResolvedValue( - createResolvedLogViewMock({ indices: 'test-index' }) - ); - pluginStart.logViews.client.getResolvedLogViewStatus.mockResolvedValue({ - index: 'missing', - }); + getResolvedLogView.mockResolvedValue(createResolvedLogViewMock({ indices: 'test-index' })); + getResolvedLogViewStatus.mockResolvedValue({ index: 'missing' }); const hasData = getLogsHasDataFetcher(mockedGetStartServices); const response = await hasData(); - expect(pluginStart.logViews.client.getResolvedLogViewStatus).toHaveBeenCalledTimes(1); + expect(pluginDeps.logsShared.logViews.client.getResolvedLogViewStatus).toHaveBeenCalledTimes( + 1 + ); expect(response).toEqual({ hasData: false, indices: 'test-index' }); }); }); describe('getLogsOverviewDataFetcher()', () => { it('should work', async () => { - const { mockedGetStartServices, dataSearch, pluginStart } = setup(); + const { mockedGetStartServices, dataSearch, getResolvedLogView } = setup(); - pluginStart.logViews.client.getResolvedLogView.mockResolvedValue(createResolvedLogViewMock()); + getResolvedLogView.mockResolvedValue(createResolvedLogViewMock()); dataSearch.mockReturnValue( of({ diff --git a/x-pack/plugins/infra/public/utils/url_state.tsx b/x-pack/plugins/infra/public/utils/url_state.tsx index 8fc4961bba22..a07b8afbc68f 100644 --- a/x-pack/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/plugins/infra/public/utils/url_state.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { Route } from '@kbn/shared-ux-router'; import { decode, RisonValue } from '@kbn/rison'; import { throttle } from 'lodash'; -import { replaceStateKeyInQueryString } from '../../common/log_views'; +import { replaceStateKeyInQueryString } from '../../common/url_state_storage_service'; interface UrlStateContainerProps { urlState: UrlState | undefined; diff --git a/x-pack/plugins/infra/public/utils/use_url_state.ts b/x-pack/plugins/infra/public/utils/use_url_state.ts index 3d269f9eb058..8fc03d2d9dda 100644 --- a/x-pack/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/plugins/infra/public/utils/use_url_state.ts @@ -10,7 +10,7 @@ import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { decode, RisonValue } from '@kbn/rison'; import { useHistory } from 'react-router-dom'; -import { replaceStateKeyInQueryString } from '../../common/log_views'; +import { replaceStateKeyInQueryString } from '../../common/url_state_storage_service'; export const useUrlState = ({ defaultState, diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts index a1b1a7b72919..e9fec4a5b4f5 100644 --- a/x-pack/plugins/infra/server/features.ts +++ b/x-pack/plugins/infra/server/features.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; +import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server'; import { LOG_DOCUMENT_COUNT_RULE_TYPE_ID } from '../common/alerting/logs/log_threshold/types'; import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, @@ -14,7 +15,6 @@ import { } from '../common/alerting/metrics'; import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; import { infraSourceConfigurationSavedObjectName } from './lib/sources/saved_object_type'; -import { logViewSavedObjectName } from './saved_objects'; export const METRICS_FEATURE = { id: METRICS_FEATURE_ID, diff --git a/x-pack/plugins/infra/server/infra_server.ts b/x-pack/plugins/infra/server/infra_server.ts index 4d29974ceb75..e54713faba76 100644 --- a/x-pack/plugins/infra/server/infra_server.ts +++ b/x-pack/plugins/infra/server/infra_server.ts @@ -22,12 +22,6 @@ import { initValidateLogAnalysisDatasetsRoute, initValidateLogAnalysisIndicesRoute, } from './routes/log_analysis'; -import { - initLogEntriesHighlightsRoute, - initLogEntriesSummaryHighlightsRoute, - initLogEntriesSummaryRoute, -} from './routes/log_entries'; -import { initLogViewRoutes } from './routes/log_views'; import { initMetadataRoute } from './routes/metadata'; import { initMetricsAPIRoute } from './routes/metrics_api'; import { initMetricExplorerRoute } from './routes/metrics_explorer'; @@ -55,10 +49,6 @@ export const initInfraServer = (libs: InfraBackendLibs) => { initValidateLogAnalysisDatasetsRoute(libs); initValidateLogAnalysisIndicesRoute(libs); initGetLogEntryExamplesRoute(libs); - initLogEntriesHighlightsRoute(libs); - initLogEntriesSummaryRoute(libs); - initLogEntriesSummaryHighlightsRoute(libs); - initLogViewRoutes(libs); initMetricExplorerRoute(libs); initMetricsExplorerViewRoutes(libs); initMetricsAPIRoute(libs); diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 22351750fbab..61c8b935806a 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -24,6 +24,7 @@ import { PluginSetupContract as AlertingPluginContract } from '@kbn/alerting-plu import { MlPluginSetup } from '@kbn/ml-plugin/server'; import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server'; +import { LogsSharedPluginSetup, LogsSharedPluginStart } from '@kbn/logs-shared-plugin/server'; import { VersionedRouteConfig } from '@kbn/core-http-server'; export interface InfraServerPluginSetupDeps { @@ -38,11 +39,13 @@ export interface InfraServerPluginSetupDeps { usageCollection: UsageCollectionSetup; visTypeTimeseries: VisTypeTimeseriesSetup; ml?: MlPluginSetup; + logsShared: LogsSharedPluginSetup; } export interface InfraServerPluginStartDeps { data: DataPluginStart; dataViews: DataViewsPluginStart; + logsShared: LogsSharedPluginStart; } export interface CallWithRequestParams extends estypes.RequestBase { diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts index 0860da5c7a18..d0a301726138 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -29,6 +29,7 @@ import { createInventoryMetricThresholdExecutor } from './inventory_metric_thres import { ConditionResult } from './evaluate_condition'; import { InfraBackendLibs } from '../../infra_types'; import { infraPluginMock } from '../../../mocks'; +import { logsSharedPluginMock } from '@kbn/logs-shared-plugin/server/mocks'; jest.mock('./evaluate_condition', () => ({ evaluateCondition: jest.fn() })); @@ -121,7 +122,7 @@ const mockLibs = { }, getStartServices: () => [ null, - infraPluginMock.createSetupContract(), + { logsShared: logsSharedPluginMock.createStartContract() }, infraPluginMock.createStartContract(), ], configuration: createMockStaticConfiguration({}), diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 5a1dba9faae1..0754c79a9968 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -162,8 +162,8 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = } const source = await libs.sources.getSourceConfiguration(savedObjectsClient, sourceId); - const [, , { logViews }] = await libs.getStartServices(); - const logQueryFields: LogQueryFields | undefined = await logViews + const [, { logsShared }] = await libs.getStartServices(); + const logQueryFields: LogQueryFields | undefined = await logsShared.logViews .getClient(savedObjectsClient, esClient) .getResolvedLogView({ type: 'log-view-reference', diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts index 97c43ea578e7..f54334ef22e5 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import { ResolvedLogView } from '@kbn/logs-shared-plugin/common'; import { ExecutionTimeRange, GroupedSearchQueryResponse, @@ -19,7 +20,6 @@ import { Point, Series, } from '../../../../common/http_api'; -import { ResolvedLogView } from '../../../../common/log_views'; import { decodeOrThrow } from '../../../../common/runtime_types'; import type { InfraPluginRequestHandlerContext } from '../../../types'; import { KibanaFramework } from '../../adapters/framework/kibana_framework_adapter'; diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index 7091fc62a2bb..9b93e021ae8f 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -201,12 +201,12 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => return alert; }; - const [, , { logViews }] = await libs.getStartServices(); + const [, { logsShared }] = await libs.getStartServices(); try { const validatedParams = decodeOrThrow(ruleParamsRT)(params); - const { indices, timestampField, runtimeMappings } = await logViews + const { indices, timestampField, runtimeMappings } = await logsShared.logViews .getClient(savedObjectsClient, scopedClusterClient.asCurrentUser) .getResolvedLogView(validatedParams.logView); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts index 7a44311b40fc..12b67b6f260e 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts @@ -6,9 +6,9 @@ */ import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; -import { logViewSavedObjectName } from '../../../saved_objects'; +import { logViewReferenceRT } from '@kbn/logs-shared-plugin/common'; +import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server'; import { RuleParams, ruleParamsRT } from '../../../../common/alerting/logs/log_threshold'; -import { logViewReferenceRT } from '../../../../common/log_views'; import { decodeOrThrow } from '../../../../common/runtime_types'; export const LOG_VIEW_REFERENCE_NAME = 'log-view-reference-0'; diff --git a/x-pack/plugins/infra/server/lib/infra_types.ts b/x-pack/plugins/infra/server/lib/infra_types.ts index f9fe976f692a..e31bad1b5ffe 100644 --- a/x-pack/plugins/infra/server/lib/infra_types.ts +++ b/x-pack/plugins/infra/server/lib/infra_types.ts @@ -10,18 +10,18 @@ import type { IBasePath } from '@kbn/core/server'; import type { handleEsError } from '@kbn/es-ui-shared-plugin/server'; import type { AlertsLocatorParams } from '@kbn/observability-plugin/common'; import type { LocatorPublic } from '@kbn/share-plugin/common'; +import type { ILogsSharedLogEntriesDomain } from '@kbn/logs-shared-plugin/server'; import { RulesServiceSetup } from '../services/rules'; import { InfraConfig, InfraPluginStartServicesAccessor } from '../types'; import { KibanaFramework } from './adapters/framework/kibana_framework_adapter'; import { InfraFieldsDomain } from './domains/fields_domain'; -import { InfraLogEntriesDomain } from './domains/log_entries_domain'; import { InfraMetricsDomain } from './domains/metrics_domain'; import { InfraSources } from './sources'; import { InfraSourceStatus } from './source_status'; export interface InfraDomainLibs { fields: InfraFieldsDomain; - logEntries: InfraLogEntriesDomain; + logEntries: ILogsSharedLogEntriesDomain; metrics: InfraMetricsDomain; } diff --git a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts index b17afa68d2d4..591376450be3 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts @@ -6,6 +6,7 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { PersistedLogViewReference, ResolvedLogView } from '@kbn/logs-shared-plugin/common'; import { AnomaliesSort, getJobId, @@ -16,7 +17,6 @@ import { logEntryRateJobTypes, Pagination, } from '../../../common/log_analysis'; -import { PersistedLogViewReference, ResolvedLogView } from '../../../common/log_views'; import { startTracingSpan, TracingSpan } from '../../../common/performance_tracing'; import { decodeOrThrow } from '../../../common/runtime_types'; import type { diff --git a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts index d8eb18e4890b..88678f4c79c5 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts @@ -7,6 +7,11 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core/server'; +import { + LogEntryContext, + PersistedLogViewReference, + ResolvedLogView, +} from '@kbn/logs-shared-plugin/common'; import { CategoriesSort, compareDatasetsByMaximumAnomalyScore, @@ -14,8 +19,6 @@ import { jobCustomSettingsRT, logEntryCategoriesJobTypes, } from '../../../common/log_analysis'; -import { LogEntryContext } from '../../../common/log_entry'; -import { PersistedLogViewReference, ResolvedLogView } from '../../../common/log_views'; import { startTracingSpan } from '../../../common/performance_tracing'; import { decodeOrThrow } from '../../../common/runtime_types'; import type { MlAnomalyDetectors, MlSystem } from '../../types'; diff --git a/x-pack/plugins/infra/server/mocks.ts b/x-pack/plugins/infra/server/mocks.ts index 7e5a349cb1e0..a15575572a07 100644 --- a/x-pack/plugins/infra/server/mocks.ts +++ b/x-pack/plugins/infra/server/mocks.ts @@ -5,18 +5,21 @@ * 2.0. */ -import { createInventoryViewsServiceStartMock } from './services/inventory_views/inventory_views_service.mock'; import { - createLogViewsServiceSetupMock, - createLogViewsServiceStartMock, -} from './services/log_views/log_views_service.mock'; -import { createMetricsExplorerViewsServiceStartMock } from './services/metrics_explorer_views/metrics_explorer_views_service.mock'; + createInventoryViewsServiceSetupMock, + createInventoryViewsServiceStartMock, +} from './services/inventory_views/inventory_views_service.mock'; +import { + createMetricsExplorerViewsServiceSetupMock, + createMetricsExplorerViewsServiceStartMock, +} from './services/metrics_explorer_views/metrics_explorer_views_service.mock'; import { InfraPluginSetup, InfraPluginStart } from './types'; const createInfraSetupMock = () => { const infraSetupMock: jest.Mocked = { defineInternalSourceConfiguration: jest.fn(), - logViews: createLogViewsServiceSetupMock(), + inventoryViews: createInventoryViewsServiceSetupMock(), + metricsExplorerViews: createMetricsExplorerViewsServiceSetupMock(), }; return infraSetupMock; @@ -26,7 +29,6 @@ const createInfraStartMock = () => { const infraStartMock: jest.Mocked = { getMetricIndices: jest.fn(), inventoryViews: createInventoryViewsServiceStartMock(), - logViews: createLogViewsServiceStartMock(), metricsExplorerViews: createMetricsExplorerViewsServiceStartMock(), }; return infraStartMock; diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 04fea926a208..2449293dfa3a 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -24,7 +24,6 @@ import { LOGS_FEATURE_ID, METRICS_FEATURE_ID, } from '../common/constants'; -import { defaultLogViewsStaticConfig } from '../common/log_views'; import { publicConfigKeys } from '../common/plugin_config_types'; import { configDeprecations, getInfraDeprecationsFactory } from './deprecations'; import { LOGS_FEATURE, METRICS_FEATURE } from './features'; @@ -32,7 +31,6 @@ import { initInfraServer } from './infra_server'; import { FrameworkFieldsAdapter } from './lib/adapters/fields/framework_fields_adapter'; import { InfraServerPluginSetupDeps, InfraServerPluginStartDeps } from './lib/adapters/framework'; import { KibanaFramework } from './lib/adapters/framework/kibana_framework_adapter'; -import { InfraKibanaLogEntriesAdapter } from './lib/adapters/log_entries/kibana_log_entries_adapter'; import { KibanaMetricsAdapter } from './lib/adapters/metrics/kibana_metrics_adapter'; import { InfraElasticsearchSourceStatusAdapter } from './lib/adapters/source_status'; import { registerRuleTypes } from './lib/alerting'; @@ -41,20 +39,13 @@ import { METRICS_RULES_ALERT_CONTEXT, } from './lib/alerting/register_rule_types'; import { InfraFieldsDomain } from './lib/domains/fields_domain'; -import { InfraLogEntriesDomain } from './lib/domains/log_entries_domain'; import { InfraMetricsDomain } from './lib/domains/metrics_domain'; import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types'; import { makeGetMetricIndices } from './lib/metrics/make_get_metric_indices'; import { infraSourceConfigurationSavedObjectType, InfraSources } from './lib/sources'; import { InfraSourceStatus } from './lib/source_status'; -import { - inventoryViewSavedObjectType, - logViewSavedObjectType, - metricsExplorerViewSavedObjectType, -} from './saved_objects'; +import { inventoryViewSavedObjectType, metricsExplorerViewSavedObjectType } from './saved_objects'; import { InventoryViewsService } from './services/inventory_views'; -import { LogEntriesService } from './services/log_entries'; -import { LogViewsService } from './services/log_views'; import { MetricsExplorerViewsService } from './services/metrics_explorer_views'; import { RulesService } from './services/rules'; import { @@ -65,6 +56,7 @@ import { InfraPluginStart, } from './types'; import { UsageCollector } from './usage/usage_collector'; +import { mapSourceToLogView } from './utils/map_source_to_log_view'; export const config: PluginConfigDescriptor = { schema: schema.object({ @@ -138,7 +130,6 @@ export class InfraServerPlugin private logsRules: RulesService; private metricsRules: RulesService; private inventoryViews: InventoryViewsService; - private logViews: LogViewsService; private metricsExplorerViews: MetricsExplorerViewsService; constructor(context: PluginInitializerContext) { @@ -157,7 +148,6 @@ export class InfraServerPlugin ); this.inventoryViews = new InventoryViewsService(this.logger.get('inventoryViews')); - this.logViews = new LogViewsService(this.logger.get('logViews')); this.metricsExplorerViews = new MetricsExplorerViewsService( this.logger.get('metricsExplorerViews') ); @@ -170,18 +160,16 @@ export class InfraServerPlugin }); const sourceStatus = new InfraSourceStatus( new InfraElasticsearchSourceStatusAdapter(framework), - { - sources, - } + { sources } ); + + // Setup infra services const inventoryViews = this.inventoryViews.setup(); - const logViews = this.logViews.setup(); const metricsExplorerViews = this.metricsExplorerViews.setup(); - // register saved object types + // Register saved object types core.savedObjects.registerType(infraSourceConfigurationSavedObjectType); core.savedObjects.registerType(inventoryViewSavedObjectType); - core.savedObjects.registerType(logViewSavedObjectType); core.savedObjects.registerType(metricsExplorerViewSavedObjectType); // TODO: separate these out individually and do away with "domains" as a temporary group @@ -191,10 +179,7 @@ export class InfraServerPlugin fields: new InfraFieldsDomain(new FrameworkFieldsAdapter(framework), { sources, }), - logEntries: new InfraLogEntriesDomain(new InfraKibanaLogEntriesAdapter(framework), { - framework, - getStartServices: () => core.getStartServices(), - }), + logEntries: plugins.logsShared.logEntries, metrics: new InfraMetricsDomain(new KibanaMetricsAdapter(framework)), }; @@ -216,6 +201,19 @@ export class InfraServerPlugin plugins.features.registerKibanaFeature(METRICS_FEATURE); plugins.features.registerKibanaFeature(LOGS_FEATURE); + // Register an handler to retrieve the fallback logView starting from a source configuration + plugins.logsShared.logViews.registerLogViewFallbackHandler(async (sourceId, { soClient }) => { + const sourceConfiguration = await sources.getSourceConfiguration(soClient, sourceId); + return mapSourceToLogView(sourceConfiguration); + }); + plugins.logsShared.logViews.setLogViewsStaticConfig({ + messageFields: this.config.sources?.default?.fields?.message, + }); + + plugins.logsShared.registerUsageCollectorActions({ + countLogs: () => UsageCollector.countLogs(), + }); + plugins.home.sampleData.addAppLinksToSampleDataset('logs', [ { sampleObject: null, // indicates that there is no sample object associated with this app link's path @@ -247,9 +245,6 @@ export class InfraServerPlugin // Telemetry UsageCollector.registerUsageCollector(plugins.usageCollection); - const logEntriesService = new LogEntriesService(); - logEntriesService.setup(core, plugins); - // register deprecated source configuration fields core.deprecations.registerDeprecations({ getDeprecations: getInfraDeprecationsFactory(sources), @@ -258,29 +253,16 @@ export class InfraServerPlugin return { defineInternalSourceConfiguration: sources.defineInternalSourceConfiguration.bind(sources), inventoryViews, - logViews, metricsExplorerViews, } as InfraPluginSetup; } - start(core: CoreStart, plugins: InfraServerPluginStartDeps) { + start(core: CoreStart) { const inventoryViews = this.inventoryViews.start({ infraSources: this.libs.sources, savedObjects: core.savedObjects, }); - const logViews = this.logViews.start({ - infraSources: this.libs.sources, - savedObjects: core.savedObjects, - dataViews: plugins.dataViews, - elasticsearch: core.elasticsearch, - config: { - messageFields: - this.config.sources?.default?.fields?.message ?? - defaultLogViewsStaticConfig.messageFields, - }, - }); - const metricsExplorerViews = this.metricsExplorerViews.start({ infraSources: this.libs.sources, savedObjects: core.savedObjects, @@ -288,7 +270,6 @@ export class InfraServerPlugin return { inventoryViews, - logViews, metricsExplorerViews, getMetricIndices: makeGetMetricIndices(this.libs.sources), }; diff --git a/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts b/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts index 9a963c3f84e7..1c6be6bf56c2 100644 --- a/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts +++ b/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts @@ -38,8 +38,10 @@ export const initGetLogAlertsChartPreviewDataRoute = ({ data: { logView, buckets, alertParams, executionTimeRange }, } = request.body; - const [, , { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.getScopedClient(request).getResolvedLogView(logView); + const [, { logsShared }] = await getStartServices(); + const resolvedLogView = await logsShared.logViews + .getScopedClient(request) + .getResolvedLogView(logView); try { const { series } = await getChartPreviewData( diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts index b17a50d23974..5e9a57768828 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts @@ -45,8 +45,10 @@ export const initGetLogEntryCategoryExamplesRoute = ({ }, } = request.body; - const [, , { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.getScopedClient(request).getResolvedLogView(logView); + const [, { logsShared }] = await getStartServices(); + const resolvedLogView = await logsShared.logViews + .getScopedClient(request) + .getResolvedLogView(logView); try { const infraMlContext = await assertHasInfraMlPlugins(requestContext); diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts index 2b413dce7f29..8b3b2f0449c5 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts @@ -46,8 +46,10 @@ export const initGetLogEntryExamplesRoute = ({ }, } = request.body; - const [, , { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.getScopedClient(request).getResolvedLogView(logView); + const [, { logsShared }] = await getStartServices(); + const resolvedLogView = await logsShared.logViews + .getScopedClient(request) + .getResolvedLogView(logView); try { const infraMlContext = await assertHasInfraMlPlugins(requestContext); diff --git a/x-pack/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts index 0c893171b5b6..085be4584e2a 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/index.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/index.ts @@ -40,8 +40,8 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => { const soClient = (await requestContext.core).savedObjects.client; const source = await libs.sources.getSourceConfiguration(soClient, snapshotRequest.sourceId); const compositeSize = libs.configuration.inventory.compositeSize; - const [, , { logViews }] = await libs.getStartServices(); - const logQueryFields: LogQueryFields | undefined = await logViews + const [, { logsShared }] = await libs.getStartServices(); + const logQueryFields: LogQueryFields | undefined = await logsShared.logViews .getScopedClient(request) .getResolvedLogView({ type: 'log-view-reference', diff --git a/x-pack/plugins/infra/server/saved_objects/index.ts b/x-pack/plugins/infra/server/saved_objects/index.ts index cf6906fc733f..c64f4b46808c 100644 --- a/x-pack/plugins/infra/server/saved_objects/index.ts +++ b/x-pack/plugins/infra/server/saved_objects/index.ts @@ -6,5 +6,4 @@ */ export * from './inventory_view'; -export * from './log_view'; export * from './metrics_explorer_view'; diff --git a/x-pack/plugins/infra/server/types.ts b/x-pack/plugins/infra/server/types.ts index 49dbca9b276b..0a4ad94c09d4 100644 --- a/x-pack/plugins/infra/server/types.ts +++ b/x-pack/plugins/infra/server/types.ts @@ -14,9 +14,11 @@ import type { SearchRequestHandlerContext } from '@kbn/data-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import type { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration'; import { InfraServerPluginStartDeps } from './lib/adapters/framework'; -import { InventoryViewsServiceStart } from './services/inventory_views'; -import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views/types'; -import { MetricsExplorerViewsServiceStart } from './services/metrics_explorer_views'; +import { InventoryViewsServiceSetup, InventoryViewsServiceStart } from './services/inventory_views'; +import { + MetricsExplorerViewsServiceSetup, + MetricsExplorerViewsServiceStart, +} from './services/metrics_explorer_views'; export type { InfraConfig } from '../common/plugin_config_types'; @@ -28,12 +30,12 @@ export interface InfraPluginSetup { sourceId: string, sourceProperties: InfraStaticSourceConfiguration ) => void; - logViews: LogViewsServiceSetup; + inventoryViews: InventoryViewsServiceSetup; + metricsExplorerViews: MetricsExplorerViewsServiceSetup; } export interface InfraPluginStart { inventoryViews: InventoryViewsServiceStart; - logViews: LogViewsServiceStart; metricsExplorerViews: MetricsExplorerViewsServiceStart; getMetricIndices: ( savedObjectsClient: SavedObjectsClientContract, diff --git a/x-pack/plugins/infra/server/utils/elasticsearch_runtime_types.ts b/x-pack/plugins/infra/server/utils/elasticsearch_runtime_types.ts index e2dbf02ae2d0..20f0aeb2e2f0 100644 --- a/x-pack/plugins/infra/server/utils/elasticsearch_runtime_types.ts +++ b/x-pack/plugins/infra/server/utils/elasticsearch_runtime_types.ts @@ -17,8 +17,6 @@ export const shardFailureRT = rt.partial({ shard: rt.number, }); -export type ShardFailure = rt.TypeOf; - export const commonSearchSuccessResponseFieldsRT = rt.type({ _shards: rt.intersection([ rt.type({ @@ -34,8 +32,3 @@ export const commonSearchSuccessResponseFieldsRT = rt.type({ timed_out: rt.boolean, took: rt.number, }); - -export const commonHitFieldsRT = rt.type({ - _index: rt.string, - _id: rt.string, -}); diff --git a/x-pack/plugins/infra/server/utils/map_source_to_log_view.test.ts b/x-pack/plugins/infra/server/utils/map_source_to_log_view.test.ts new file mode 100644 index 000000000000..38db985a2828 --- /dev/null +++ b/x-pack/plugins/infra/server/utils/map_source_to_log_view.test.ts @@ -0,0 +1,59 @@ +/* + * 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 { InfraSource } from '../lib/sources'; +import { getAttributesFromSourceConfiguration } from './map_source_to_log_view'; + +describe('getAttributesFromSourceConfiguration function', () => { + it('converts the index_pattern log indices type to data_view', () => { + const logViewAttributes = getAttributesFromSourceConfiguration(basicTestSourceConfiguration); + + expect(logViewAttributes.logIndices).toEqual({ + type: 'data_view', + dataViewId: 'INDEX_PATTERN_ID', + }); + }); + + it('preserves the index_name log indices type', () => { + const logViewAttributes = getAttributesFromSourceConfiguration({ + ...basicTestSourceConfiguration, + configuration: { + ...basicTestSourceConfiguration.configuration, + logIndices: { + type: 'index_name', + indexName: 'INDEX_NAME', + }, + }, + }); + + expect(logViewAttributes.logIndices).toEqual({ + type: 'index_name', + indexName: 'INDEX_NAME', + }); + }); +}); + +const basicTestSourceConfiguration: InfraSource = { + id: 'ID', + origin: 'stored', + configuration: { + name: 'NAME', + description: 'DESCRIPTION', + logIndices: { + type: 'index_pattern', + indexPatternId: 'INDEX_PATTERN_ID', + }, + logColumns: [], + fields: { + message: [], + }, + metricAlias: 'METRIC_ALIAS', + inventoryDefaultView: 'INVENTORY_DEFAULT_VIEW', + metricsExplorerDefaultView: 'METRICS_EXPLORER_DEFAULT_VIEW', + anomalyThreshold: 0, + }, +}; diff --git a/x-pack/plugins/infra/server/utils/map_source_to_log_view.ts b/x-pack/plugins/infra/server/utils/map_source_to_log_view.ts new file mode 100644 index 000000000000..5dd5d021ccd6 --- /dev/null +++ b/x-pack/plugins/infra/server/utils/map_source_to_log_view.ts @@ -0,0 +1,38 @@ +/* + * 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 { LogIndexReference, LogView, LogViewAttributes } from '@kbn/logs-shared-plugin/common'; +import { LogIndexReference as SourceConfigurationLogIndexReference } from '../../common/source_configuration/source_configuration'; +import { InfraSource } from '../lib/sources'; + +export const mapSourceToLogView = (sourceConfiguration: InfraSource): LogView => { + return { + id: sourceConfiguration.id, + version: sourceConfiguration.version, + updatedAt: sourceConfiguration.updatedAt, + origin: `infra-source-${sourceConfiguration.origin}`, + attributes: getAttributesFromSourceConfiguration(sourceConfiguration), + }; +}; + +export const getAttributesFromSourceConfiguration = ({ + configuration: { name, description, logIndices, logColumns }, +}: InfraSource): LogViewAttributes => ({ + name, + description, + logIndices: getLogIndicesFromSourceConfigurationLogIndices(logIndices), + logColumns, +}); + +const getLogIndicesFromSourceConfigurationLogIndices = ( + logIndices: SourceConfigurationLogIndexReference +): LogIndexReference => + logIndices.type === 'index_pattern' + ? { + type: 'data_view', + dataViewId: logIndices.indexPatternId, + } + : logIndices; diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index 997fbf24ea9c..3f1ece70501d 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -38,7 +38,6 @@ "@kbn/shared-ux-page-kibana-template", "@kbn/safer-lodash-set", "@kbn/test-jest-helpers", - "@kbn/test-subj-selector", "@kbn/controls-plugin", "@kbn/securitysolution-io-ts-types", "@kbn/config-schema", @@ -67,6 +66,7 @@ "@kbn/aiops-plugin", "@kbn/field-formats-plugin", "@kbn/core-http-server", + "@kbn/logs-shared-plugin", "@kbn/licensing-plugin", ], "exclude": ["target/**/*"] diff --git a/x-pack/plugins/logs_shared/README.md b/x-pack/plugins/logs_shared/README.md new file mode 100755 index 000000000000..16483a988ec4 --- /dev/null +++ b/x-pack/plugins/logs_shared/README.md @@ -0,0 +1,3 @@ +# Logs Shared + +Exposes the shared components and APIs to access and visualize logs. diff --git a/x-pack/plugins/logs_shared/common/constants.ts b/x-pack/plugins/logs_shared/common/constants.ts new file mode 100644 index 000000000000..7e49b14458a9 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/constants.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const TIMESTAMP_FIELD = '@timestamp'; +export const MESSAGE_FIELD = 'message'; +export const TIEBREAKER_FIELD = '_doc'; diff --git a/x-pack/plugins/logs_shared/common/dynamic.tsx b/x-pack/plugins/logs_shared/common/dynamic.tsx new file mode 100644 index 000000000000..a66dbaa10b5a --- /dev/null +++ b/x-pack/plugins/logs_shared/common/dynamic.tsx @@ -0,0 +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. + */ + +import React, { lazy, Suspense } from 'react'; + +type LoadableComponent = () => any; + +interface DynamicOptions { + fallback?: React.ReactNode; +} + +/** + * Lazy load and wrap with Suspense any component. + * + * @example + * const Header = dynamic(() => import('./components/header')) + */ +export function dynamic(loader: LoadableComponent, options: DynamicOptions = {}) { + const Component = lazy(loader); + + return (props: any) => ( + + + + ); +} diff --git a/x-pack/plugins/logs_shared/common/formatters/datetime.ts b/x-pack/plugins/logs_shared/common/formatters/datetime.ts new file mode 100644 index 000000000000..270dd7aa7380 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/formatters/datetime.ts @@ -0,0 +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. + */ + +import { i18n } from '@kbn/i18n'; + +export function localizedDate(dateTime: number | Date, locale: string = i18n.getLocale()) { + const formatter = new Intl.DateTimeFormat(locale, { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + + return formatter.format(dateTime); +} diff --git a/x-pack/plugins/logs_shared/common/http_api/index.ts b/x-pack/plugins/logs_shared/common/http_api/index.ts new file mode 100644 index 000000000000..939f72786183 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/http_api/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Exporting versioned APIs types + */ +export * from './latest'; +export * as logEntriesV1 from './log_entries/v1'; +export * as logViewsV1 from './log_views/v1'; diff --git a/x-pack/plugins/logs_shared/common/http_api/latest.ts b/x-pack/plugins/logs_shared/common/http_api/latest.ts new file mode 100644 index 000000000000..63f58bf4f92c --- /dev/null +++ b/x-pack/plugins/logs_shared/common/http_api/latest.ts @@ -0,0 +1,9 @@ +/* + * 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 './log_entries/v1'; +export * from './log_views/v1'; diff --git a/x-pack/plugins/infra/common/http_api/log_entries/v1/highlights.ts b/x-pack/plugins/logs_shared/common/http_api/log_entries/v1/highlights.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_entries/v1/highlights.ts rename to x-pack/plugins/logs_shared/common/http_api/log_entries/v1/highlights.ts diff --git a/x-pack/plugins/infra/common/http_api/log_entries/v1/index.ts b/x-pack/plugins/logs_shared/common/http_api/log_entries/v1/index.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_entries/v1/index.ts rename to x-pack/plugins/logs_shared/common/http_api/log_entries/v1/index.ts diff --git a/x-pack/plugins/infra/common/http_api/log_entries/v1/summary.ts b/x-pack/plugins/logs_shared/common/http_api/log_entries/v1/summary.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_entries/v1/summary.ts rename to x-pack/plugins/logs_shared/common/http_api/log_entries/v1/summary.ts diff --git a/x-pack/plugins/infra/common/http_api/log_entries/v1/summary_highlights.ts b/x-pack/plugins/logs_shared/common/http_api/log_entries/v1/summary_highlights.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_entries/v1/summary_highlights.ts rename to x-pack/plugins/logs_shared/common/http_api/log_entries/v1/summary_highlights.ts diff --git a/x-pack/plugins/infra/common/http_api/log_views/common.ts b/x-pack/plugins/logs_shared/common/http_api/log_views/common.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_views/common.ts rename to x-pack/plugins/logs_shared/common/http_api/log_views/common.ts diff --git a/x-pack/plugins/infra/common/http_api/log_views/index.ts b/x-pack/plugins/logs_shared/common/http_api/log_views/index.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_views/index.ts rename to x-pack/plugins/logs_shared/common/http_api/log_views/index.ts diff --git a/x-pack/plugins/infra/common/http_api/log_views/v1/get_log_view.ts b/x-pack/plugins/logs_shared/common/http_api/log_views/v1/get_log_view.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_views/v1/get_log_view.ts rename to x-pack/plugins/logs_shared/common/http_api/log_views/v1/get_log_view.ts diff --git a/x-pack/plugins/infra/common/http_api/log_views/v1/index.ts b/x-pack/plugins/logs_shared/common/http_api/log_views/v1/index.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_views/v1/index.ts rename to x-pack/plugins/logs_shared/common/http_api/log_views/v1/index.ts diff --git a/x-pack/plugins/infra/common/http_api/log_views/v1/put_log_view.ts b/x-pack/plugins/logs_shared/common/http_api/log_views/v1/put_log_view.ts similarity index 100% rename from x-pack/plugins/infra/common/http_api/log_views/v1/put_log_view.ts rename to x-pack/plugins/logs_shared/common/http_api/log_views/v1/put_log_view.ts diff --git a/x-pack/plugins/logs_shared/common/index.ts b/x-pack/plugins/logs_shared/common/index.ts new file mode 100644 index 000000000000..07f029868b22 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/index.ts @@ -0,0 +1,58 @@ +/* + * 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. + */ + +// LogView runtime +export { + defaultFilterStateKey, + defaultPositionStateKey, + DEFAULT_LOG_VIEW, + DEFAULT_REFRESH_INTERVAL, + logDataViewReferenceRT, + logIndexNameReferenceRT, + logViewColumnConfigurationRT, + logViewReferenceRT, + persistedLogViewReferenceRT, + defaultLogViewAttributes, +} from './log_views'; + +// LogView types +export type { + LogDataViewReference, + LogIndexNameReference, + LogIndexReference, + LogView, + LogViewAttributes, + LogViewColumnConfiguration, + LogViewReference, + LogViewStatus, + PersistedLogViewReference, + ResolvedLogView, + ResolvedLogViewField, +} from './log_views'; + +// LogView errors +export { + FetchLogViewError, + FetchLogViewStatusError, + ResolveLogViewError, +} from './log_views/errors'; + +// eslint-disable-next-line @kbn/eslint/no_export_all +export * from './log_entry'; + +// Http types +export type { LogEntriesSummaryBucket, LogEntriesSummaryHighlightsBucket } from './http_api'; + +// Http runtime +export { + LOG_ENTRIES_HIGHLIGHTS_PATH, + LOG_ENTRIES_SUMMARY_PATH, + logEntriesHighlightsRequestRT, + logEntriesHighlightsResponseRT, + logEntriesSummaryRequestRT, + logEntriesSummaryResponseRT, +} from './http_api'; diff --git a/x-pack/plugins/infra/common/log_entry/index.ts b/x-pack/plugins/logs_shared/common/log_entry/index.ts similarity index 100% rename from x-pack/plugins/infra/common/log_entry/index.ts rename to x-pack/plugins/logs_shared/common/log_entry/index.ts diff --git a/x-pack/plugins/infra/common/log_entry/log_entry.ts b/x-pack/plugins/logs_shared/common/log_entry/log_entry.ts similarity index 100% rename from x-pack/plugins/infra/common/log_entry/log_entry.ts rename to x-pack/plugins/logs_shared/common/log_entry/log_entry.ts diff --git a/x-pack/plugins/infra/common/log_entry/log_entry_cursor.ts b/x-pack/plugins/logs_shared/common/log_entry/log_entry_cursor.ts similarity index 100% rename from x-pack/plugins/infra/common/log_entry/log_entry_cursor.ts rename to x-pack/plugins/logs_shared/common/log_entry/log_entry_cursor.ts diff --git a/x-pack/plugins/logs_shared/common/log_text_scale/index.ts b/x-pack/plugins/logs_shared/common/log_text_scale/index.ts new file mode 100644 index 000000000000..11ae6d0adc7f --- /dev/null +++ b/x-pack/plugins/logs_shared/common/log_text_scale/index.ts @@ -0,0 +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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './log_text_scale'; diff --git a/x-pack/plugins/logs_shared/common/log_text_scale/log_text_scale.ts b/x-pack/plugins/logs_shared/common/log_text_scale/log_text_scale.ts new file mode 100644 index 000000000000..7fc774c4b59e --- /dev/null +++ b/x-pack/plugins/logs_shared/common/log_text_scale/log_text_scale.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type TextScale = 'small' | 'medium' | 'large'; + +export function isTextScale(maybeTextScale: string): maybeTextScale is TextScale { + return ['small', 'medium', 'large'].includes(maybeTextScale); +} diff --git a/x-pack/plugins/infra/common/log_views/defaults.ts b/x-pack/plugins/logs_shared/common/log_views/defaults.ts similarity index 82% rename from x-pack/plugins/infra/common/log_views/defaults.ts rename to x-pack/plugins/logs_shared/common/log_views/defaults.ts index 155ce6465b2f..2d7f26c6d940 100644 --- a/x-pack/plugins/infra/common/log_views/defaults.ts +++ b/x-pack/plugins/logs_shared/common/log_views/defaults.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { defaultSourceConfiguration } from '../source_configuration/defaults'; -import { LogViewAttributes, LogViewsStaticConfig } from './types'; +import { DefaultLogViewsStaticConfig, LogViewAttributes } from './types'; export const defaultLogViewId = 'default'; export const defaultFilterStateKey = 'logFilter'; @@ -41,8 +40,8 @@ export const defaultLogViewAttributes: LogViewAttributes = { ], }; -export const defaultLogViewsStaticConfig: LogViewsStaticConfig = { - messageFields: defaultSourceConfiguration.fields.message, +export const defaultLogViewsStaticConfig: DefaultLogViewsStaticConfig = { + messageFields: ['message', '@message'], }; export const DEFAULT_LOG_VIEW = { diff --git a/x-pack/plugins/infra/common/log_views/errors.ts b/x-pack/plugins/logs_shared/common/log_views/errors.ts similarity index 100% rename from x-pack/plugins/infra/common/log_views/errors.ts rename to x-pack/plugins/logs_shared/common/log_views/errors.ts diff --git a/x-pack/plugins/infra/common/log_views/index.ts b/x-pack/plugins/logs_shared/common/log_views/index.ts similarity index 89% rename from x-pack/plugins/infra/common/log_views/index.ts rename to x-pack/plugins/logs_shared/common/log_views/index.ts index 22176058622c..dd0cdaece431 100644 --- a/x-pack/plugins/infra/common/log_views/index.ts +++ b/x-pack/plugins/logs_shared/common/log_views/index.ts @@ -9,4 +9,3 @@ export * from './defaults'; export * from './errors'; export * from './resolved_log_view'; export * from './types'; -export * from './url_state_storage_service'; diff --git a/x-pack/plugins/infra/common/log_views/log_view.mock.ts b/x-pack/plugins/logs_shared/common/log_views/log_view.mock.ts similarity index 100% rename from x-pack/plugins/infra/common/log_views/log_view.mock.ts rename to x-pack/plugins/logs_shared/common/log_views/log_view.mock.ts diff --git a/x-pack/plugins/infra/common/log_views/resolved_log_view.mock.ts b/x-pack/plugins/logs_shared/common/log_views/resolved_log_view.mock.ts similarity index 100% rename from x-pack/plugins/infra/common/log_views/resolved_log_view.mock.ts rename to x-pack/plugins/logs_shared/common/log_views/resolved_log_view.mock.ts diff --git a/x-pack/plugins/infra/common/log_views/resolved_log_view.ts b/x-pack/plugins/logs_shared/common/log_views/resolved_log_view.ts similarity index 91% rename from x-pack/plugins/infra/common/log_views/resolved_log_view.ts rename to x-pack/plugins/logs_shared/common/log_views/resolved_log_view.ts index 391c6be18fb9..b9419fbd51f2 100644 --- a/x-pack/plugins/infra/common/log_views/resolved_log_view.ts +++ b/x-pack/plugins/logs_shared/common/log_views/resolved_log_view.ts @@ -8,6 +8,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DataView, DataViewsContract, FieldSpec } from '@kbn/data-views-plugin/common'; import { TIEBREAKER_FIELD, TIMESTAMP_FIELD } from '../constants'; +import { defaultLogViewsStaticConfig } from './defaults'; import { ResolveLogViewError } from './errors'; import { LogViewAttributes, LogViewColumnConfiguration, LogViewsStaticConfig } from './types'; @@ -26,16 +27,16 @@ export interface ResolvedLogView { dataViewReference: DataView; } -export const resolveLogView = async ( +export const resolveLogView = ( logViewId: string, logViewAttributes: LogViewAttributes, dataViewsService: DataViewsContract, config: LogViewsStaticConfig ): Promise => { if (logViewAttributes.logIndices.type === 'index_name') { - return await resolveLegacyReference(logViewId, logViewAttributes, dataViewsService, config); + return resolveLegacyReference(logViewId, logViewAttributes, dataViewsService, config); } else { - return await resolveDataViewReference(logViewAttributes, dataViewsService); + return resolveDataViewReference(logViewAttributes, dataViewsService); } }; @@ -71,7 +72,7 @@ const resolveLegacyReference = async ( indices, timestampField: TIMESTAMP_FIELD, tiebreakerField: TIEBREAKER_FIELD, - messageField: config.messageFields, + messageField: config.messageFields ?? defaultLogViewsStaticConfig.messageFields, fields: dataViewReference.fields, runtimeMappings: {}, columns: logViewAttributes.logColumns, diff --git a/x-pack/plugins/infra/common/log_views/types.ts b/x-pack/plugins/logs_shared/common/log_views/types.ts similarity index 96% rename from x-pack/plugins/infra/common/log_views/types.ts rename to x-pack/plugins/logs_shared/common/log_views/types.ts index ca6dd95330f8..f94601f9e0f8 100644 --- a/x-pack/plugins/infra/common/log_views/types.ts +++ b/x-pack/plugins/logs_shared/common/log_views/types.ts @@ -7,10 +7,12 @@ import * as rt from 'io-ts'; -export interface LogViewsStaticConfig { +export interface DefaultLogViewsStaticConfig { messageFields: string[]; } +export type LogViewsStaticConfig = Partial; + export const logViewOriginRT = rt.keyof({ stored: null, internal: null, diff --git a/x-pack/plugins/logs_shared/common/mocks.ts b/x-pack/plugins/logs_shared/common/mocks.ts new file mode 100644 index 000000000000..06ff3df3eb00 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/mocks.ts @@ -0,0 +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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { createResolvedLogViewMock } from './log_views/resolved_log_view.mock'; diff --git a/x-pack/plugins/logs_shared/common/runtime_types.ts b/x-pack/plugins/logs_shared/common/runtime_types.ts new file mode 100644 index 000000000000..89a6b4323b89 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/runtime_types.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RouteValidationFunction } from '@kbn/core/server'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { Context, Errors, IntersectionType, Type, UnionType, ValidationError } from 'io-ts'; + +type ErrorFactory = (message: string) => Error; + +const getErrorPath = ([first, ...rest]: Context): string[] => { + if (typeof first === 'undefined') { + return []; + } else if (first.type instanceof IntersectionType) { + const [, ...next] = rest; + return getErrorPath(next); + } else if (first.type instanceof UnionType) { + const [, ...next] = rest; + return [first.key, ...getErrorPath(next)]; + } + + return [first.key, ...getErrorPath(rest)]; +}; + +const getErrorType = ({ context }: ValidationError) => + context[context.length - 1]?.type?.name ?? 'unknown'; + +const formatError = (error: ValidationError) => + error.message ?? + `in ${getErrorPath(error.context).join('/')}: ${JSON.stringify( + error.value + )} does not match expected type ${getErrorType(error)}`; + +export const formatErrors = (errors: ValidationError[]) => + `Failed to validate: \n${errors.map((error) => ` ${formatError(error)}`).join('\n')}`; + +export const createPlainError = (message: string) => new Error(message); + +export const throwErrors = (createError: ErrorFactory) => (errors: Errors) => { + throw createError(formatErrors(errors)); +}; + +export const decodeOrThrow = + ( + runtimeType: Type, + createError: ErrorFactory = createPlainError + ) => + (inputValue: InputValue) => + pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity)); + +type ValdidationResult = ReturnType>; + +export const createValidationFunction = + ( + runtimeType: Type + ): RouteValidationFunction => + (inputValue, { badRequest, ok }) => + pipe( + runtimeType.decode(inputValue), + fold>( + (errors: Errors) => badRequest(formatErrors(errors)), + (result: DecodedValue) => ok(result) + ) + ); diff --git a/x-pack/plugins/logs_shared/common/search_strategies/common/errors.ts b/x-pack/plugins/logs_shared/common/search_strategies/common/errors.ts new file mode 100644 index 000000000000..4114af07619d --- /dev/null +++ b/x-pack/plugins/logs_shared/common/search_strategies/common/errors.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +const abortedRequestSearchStrategyErrorRT = rt.type({ + type: rt.literal('aborted'), +}); + +export type AbortedRequestSearchStrategyError = rt.TypeOf< + typeof abortedRequestSearchStrategyErrorRT +>; + +const genericSearchStrategyErrorRT = rt.type({ + type: rt.literal('generic'), + message: rt.string, +}); + +export type GenericSearchStrategyError = rt.TypeOf; + +const shardFailureSearchStrategyErrorRT = rt.type({ + type: rt.literal('shardFailure'), + shardInfo: rt.type({ + shard: rt.union([rt.number, rt.null]), + index: rt.union([rt.string, rt.null]), + node: rt.union([rt.string, rt.null]), + }), + message: rt.union([rt.string, rt.null]), +}); + +export type ShardFailureSearchStrategyError = rt.TypeOf; + +export const searchStrategyErrorRT = rt.union([ + abortedRequestSearchStrategyErrorRT, + genericSearchStrategyErrorRT, + shardFailureSearchStrategyErrorRT, +]); + +export type SearchStrategyError = rt.TypeOf; diff --git a/x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entries.ts b/x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entries.ts new file mode 100644 index 000000000000..f8daaa1b9227 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entries.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import * as rt from 'io-ts'; +import { + logEntryAfterCursorRT, + logEntryBeforeCursorRT, + logEntryCursorRT, + logEntryRT, +} from '../../log_entry'; +import { logViewColumnConfigurationRT, logViewReferenceRT } from '../../log_views'; +import { jsonObjectRT } from '../../typed_json'; +import { searchStrategyErrorRT } from '../common/errors'; + +export const LOG_ENTRIES_SEARCH_STRATEGY = 'infra-log-entries'; + +const logEntriesBaseSearchRequestParamsRT = rt.intersection([ + rt.type({ + logView: logViewReferenceRT, + startTimestamp: rt.number, + endTimestamp: rt.number, + size: rt.number, + }), + rt.partial({ + query: jsonObjectRT, + columns: rt.array(logViewColumnConfigurationRT), + highlightPhrase: rt.string, + }), +]); + +export const logEntriesBeforeSearchRequestParamsRT = rt.intersection([ + logEntriesBaseSearchRequestParamsRT, + logEntryBeforeCursorRT, +]); + +export const logEntriesAfterSearchRequestParamsRT = rt.intersection([ + logEntriesBaseSearchRequestParamsRT, + logEntryAfterCursorRT, +]); + +export const logEntriesSearchRequestParamsRT = rt.union([ + logEntriesBaseSearchRequestParamsRT, + logEntriesBeforeSearchRequestParamsRT, + logEntriesAfterSearchRequestParamsRT, +]); + +export type LogEntriesSearchRequestParams = rt.TypeOf; + +export type LogEntriesSearchRequestQuery = estypes.QueryDslQueryContainer; + +export const logEntriesSearchResponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.intersection([ + rt.type({ + entries: rt.array(logEntryRT), + topCursor: rt.union([logEntryCursorRT, rt.null]), + bottomCursor: rt.union([logEntryCursorRT, rt.null]), + }), + rt.partial({ + hasMoreBefore: rt.boolean, + hasMoreAfter: rt.boolean, + }), + ]), + }), + rt.partial({ + errors: rt.array(searchStrategyErrorRT), + }), +]); + +export type LogEntriesSearchResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entry.ts b/x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entry.ts new file mode 100644 index 000000000000..6d2a7891264d --- /dev/null +++ b/x-pack/plugins/logs_shared/common/search_strategies/log_entries/log_entry.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { logEntryCursorRT, logEntryFieldRT } from '../../log_entry'; +import { logViewReferenceRT } from '../../log_views'; +import { searchStrategyErrorRT } from '../common/errors'; + +export const LOG_ENTRY_SEARCH_STRATEGY = 'infra-log-entry'; + +export const logEntrySearchRequestParamsRT = rt.type({ + logView: logViewReferenceRT, + logEntryId: rt.string, +}); + +export type LogEntrySearchRequestParams = rt.TypeOf; + +export const logEntryRT = rt.type({ + id: rt.string, + index: rt.string, + fields: rt.array(logEntryFieldRT), + cursor: logEntryCursorRT, +}); + +export type LogEntry = rt.TypeOf; + +export const logEntrySearchResponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.union([logEntryRT, rt.null]), + }), + rt.partial({ + errors: rt.array(searchStrategyErrorRT), + }), +]); + +export type LogEntrySearchResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/logs_shared/common/time/index.ts b/x-pack/plugins/logs_shared/common/time/index.ts new file mode 100644 index 000000000000..c6a68995c024 --- /dev/null +++ b/x-pack/plugins/logs_shared/common/time/index.ts @@ -0,0 +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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './time_key'; diff --git a/x-pack/plugins/logs_shared/common/time/time_key.ts b/x-pack/plugins/logs_shared/common/time/time_key.ts new file mode 100644 index 000000000000..4c78158dd5bf --- /dev/null +++ b/x-pack/plugins/logs_shared/common/time/time_key.ts @@ -0,0 +1,80 @@ +/* + * 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 { ascending, bisector } from 'd3-array'; +import * as rt from 'io-ts'; + +export const minimalTimeKeyRT = rt.type({ + time: rt.number, + tiebreaker: rt.number, +}); + +export const timeKeyRT = rt.intersection([ + minimalTimeKeyRT, + rt.partial({ + gid: rt.string, + fromAutoReload: rt.boolean, + }), +]); +export type TimeKey = rt.TypeOf; + +export interface UniqueTimeKey extends TimeKey { + gid: string; +} + +export type Comparator = (firstValue: any, secondValue: any) => number; + +export function compareTimeKeys( + firstKey: TimeKey, + secondKey: TimeKey, + compareValues: Comparator = ascending +): number { + const timeComparison = compareValues(firstKey.time, secondKey.time); + + if (timeComparison === 0) { + const tiebreakerComparison = compareValues(firstKey.tiebreaker, secondKey.tiebreaker); + + if ( + tiebreakerComparison === 0 && + typeof firstKey.gid !== 'undefined' && + typeof secondKey.gid !== 'undefined' + ) { + return compareValues(firstKey.gid, secondKey.gid); + } + + return tiebreakerComparison; + } + + return timeComparison; +} + +export const compareToTimeKey = + (keyAccessor: (value: Value) => TimeKey, compareValues?: Comparator) => + (value: Value, key: TimeKey) => + compareTimeKeys(keyAccessor(value), key, compareValues); + +export const getIndexAtTimeKey = ( + keyAccessor: (value: Value) => TimeKey, + compareValues?: Comparator +) => { + const comparator = compareToTimeKey(keyAccessor, compareValues); + const collectionBisector = bisector(comparator); + + return (collection: Value[], key: TimeKey): number | null => { + const index = collectionBisector.left(collection, key); + + if (index >= collection.length) { + return null; + } + + if (comparator(collection[index], key) !== 0) { + return null; + } + + return index; + }; +}; diff --git a/x-pack/plugins/logs_shared/common/typed_json.ts b/x-pack/plugins/logs_shared/common/typed_json.ts new file mode 100644 index 000000000000..95d5d4274c3b --- /dev/null +++ b/x-pack/plugins/logs_shared/common/typed_json.ts @@ -0,0 +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. + */ + +import * as rt from 'io-ts'; +import { JsonArray, JsonObject, JsonValue } from '@kbn/utility-types'; + +export type { JsonArray, JsonObject, JsonValue }; + +export const jsonScalarRT = rt.union([rt.null, rt.boolean, rt.number, rt.string]); +export type JsonScalar = rt.TypeOf; + +export const jsonValueRT: rt.Type = rt.recursion('JsonValue', () => + rt.union([jsonScalarRT, jsonArrayRT, jsonObjectRT]) +); + +export const jsonArrayRT: rt.Type = rt.recursion('JsonArray', () => + rt.array(jsonValueRT) +); + +export const jsonObjectRT: rt.Type = rt.recursion('JsonObject', () => + rt.record(rt.string, jsonValueRT) +); diff --git a/x-pack/plugins/logs_shared/jest.config.js b/x-pack/plugins/logs_shared/jest.config.js new file mode 100644 index 000000000000..4d4168f2dbda --- /dev/null +++ b/x-pack/plugins/logs_shared/jest.config.js @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/logs_shared'], + coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/logs_shared', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/logs_shared/{common,public,server}/**/*.{ts,tsx}', + ], +}; diff --git a/x-pack/plugins/logs_shared/kibana.jsonc b/x-pack/plugins/logs_shared/kibana.jsonc new file mode 100644 index 000000000000..fea2ccc87848 --- /dev/null +++ b/x-pack/plugins/logs_shared/kibana.jsonc @@ -0,0 +1,22 @@ +{ + "type": "plugin", + "id": "@kbn/logs-shared-plugin", + "owner": "@elastic/infra-monitoring-ui", + "description": "Exposes the shared components and APIs to access and visualize logs.", + "plugin": { + "id": "logsShared", + "server": true, + "browser": true, + "configPath": ["xpack", "logs_shared"], + "requiredPlugins": ["data", "dataViews", "usageCollection", "observabilityShared"], + "optionalPlugins": ["observability"], + "requiredBundles": [ + "observability", + "kibanaUtils", + "kibanaReact", + ], + "extraPublicDirs": [ + "common", + ] + } +} diff --git a/x-pack/plugins/logs_shared/public/components/auto_sizer.tsx b/x-pack/plugins/logs_shared/public/components/auto_sizer.tsx new file mode 100644 index 000000000000..a983502fa85b --- /dev/null +++ b/x-pack/plugins/logs_shared/public/components/auto_sizer.tsx @@ -0,0 +1,188 @@ +/* + * 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 { isEqual } from 'lodash'; +import React from 'react'; + +interface Measurement { + width?: number; + height?: number; +} + +interface Measurements { + bounds: Measurement; + content: Measurement; +} + +interface AutoSizerProps { + detectAnyWindowResize?: boolean | 'height' | 'width'; + bounds?: boolean; + content?: boolean; + onResize?: (size: Measurements) => void; + children: ( + args: { measureRef: (instance: HTMLElement | null) => any } & Measurements + ) => React.ReactNode; +} + +interface AutoSizerState { + boundsMeasurement: Measurement; + contentMeasurement: Measurement; +} + +export class AutoSizer extends React.PureComponent { + public element: HTMLElement | null = null; + public resizeObserver: ResizeObserver | null = null; + public windowWidth: number = -1; + public windowHeight: number = -1; + + public readonly state = { + boundsMeasurement: { + height: void 0, + width: void 0, + }, + contentMeasurement: { + height: void 0, + width: void 0, + }, + }; + + constructor(props: AutoSizerProps) { + super(props); + if (this.props.detectAnyWindowResize) { + window.addEventListener('resize', this.updateMeasurement); + } + this.resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => { + entries.forEach((entry) => { + if (entry.target === this.element) { + this.measure(entry); + } + }); + }); + } + + public componentWillUnmount() { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + if (this.props.detectAnyWindowResize) { + window.removeEventListener('resize', this.updateMeasurement); + } + } + + public measure = (entry: ResizeObserverEntry | null) => { + if (!this.element) { + return; + } + + const { content = true, bounds = false } = this.props; + const { + boundsMeasurement: previousBoundsMeasurement, + contentMeasurement: previousContentMeasurement, + } = this.state; + + const boundsRect = bounds ? this.element.getBoundingClientRect() : null; + const boundsMeasurement = boundsRect + ? { + height: boundsRect.height, + width: boundsRect.width, + } + : previousBoundsMeasurement; + + if (this.props.detectAnyWindowResize && boundsMeasurement) { + if ( + boundsMeasurement.width && + this.windowWidth !== -1 && + this.windowWidth > window.innerWidth + ) { + const gap = this.windowWidth - window.innerWidth; + boundsMeasurement.width = boundsMeasurement.width - gap; + } + if ( + boundsMeasurement.height && + this.windowHeight !== -1 && + this.windowHeight > window.innerHeight + ) { + const gap = this.windowHeight - window.innerHeight; + boundsMeasurement.height = boundsMeasurement.height - gap; + } + } + this.windowWidth = window.innerWidth; + this.windowHeight = window.innerHeight; + const contentRect = content && entry ? entry.contentRect : null; + const contentMeasurement = + contentRect && entry + ? { + height: entry.contentRect.height, + width: entry.contentRect.width, + } + : previousContentMeasurement; + if ( + isEqual(boundsMeasurement, previousBoundsMeasurement) && + isEqual(contentMeasurement, previousContentMeasurement) + ) { + return; + } + + requestAnimationFrame(() => { + if (!this.resizeObserver) { + return; + } + + this.setState({ boundsMeasurement, contentMeasurement }); + + if (this.props.onResize) { + this.props.onResize({ + bounds: boundsMeasurement, + content: contentMeasurement, + }); + } + }); + }; + + public render() { + const { children } = this.props; + const { boundsMeasurement, contentMeasurement } = this.state; + return children({ + bounds: boundsMeasurement, + content: contentMeasurement, + measureRef: this.storeRef, + }); + } + + private updateMeasurement = () => + requestAnimationFrame(() => { + const { detectAnyWindowResize } = this.props; + if (!detectAnyWindowResize) return; + switch (detectAnyWindowResize) { + case 'height': + if (this.windowHeight !== window.innerHeight) { + this.measure(null); + } + break; + case 'width': + if (this.windowWidth !== window.innerWidth) { + this.measure(null); + } + break; + default: + this.measure(null); + } + }); + + private storeRef = (element: HTMLElement | null) => { + if (this.element && this.resizeObserver) { + this.resizeObserver.unobserve(this.element); + } + + if (element && this.resizeObserver) { + this.resizeObserver.observe(element); + } + + this.element = element; + }; +} diff --git a/x-pack/plugins/infra/public/components/centered_flyout_body.tsx b/x-pack/plugins/logs_shared/public/components/centered_flyout_body.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/centered_flyout_body.tsx rename to x-pack/plugins/logs_shared/public/components/centered_flyout_body.tsx diff --git a/x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx b/x-pack/plugins/logs_shared/public/components/data_search_error_callout.stories.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/data_search_error_callout.stories.tsx rename to x-pack/plugins/logs_shared/public/components/data_search_error_callout.stories.tsx diff --git a/x-pack/plugins/infra/public/components/data_search_error_callout.tsx b/x-pack/plugins/logs_shared/public/components/data_search_error_callout.tsx similarity index 92% rename from x-pack/plugins/infra/public/components/data_search_error_callout.tsx rename to x-pack/plugins/logs_shared/public/components/data_search_error_callout.tsx index dd63b22db683..52e36002de73 100644 --- a/x-pack/plugins/infra/public/components/data_search_error_callout.tsx +++ b/x-pack/plugins/logs_shared/public/components/data_search_error_callout.tsx @@ -35,7 +35,7 @@ export const DataSearchErrorCallout: React.FC<{ onClick={onRetry} > @@ -59,7 +59,7 @@ const AbortedRequestErrorMessage: React.FC<{ }> = ({}) => ( ); @@ -73,7 +73,7 @@ const ShardFailureErrorMessage: React.FC<{ error: ShardFailureSearchStrategyErro }) => ( void; + testString?: string; +} + +export const NoData: React.FC = ({ + titleText, + bodyText, + refetchText, + onRefetch, + testString, +}) => ( + {titleText}} + titleSize="m" + body={

{bodyText}

} + actions={ + + {refetchText} + + } + data-test-subj={testString} + /> +); + +const CenteredEmptyPrompt = euiStyled(EuiEmptyPrompt)` + align-self: center; +`; diff --git a/x-pack/plugins/infra/public/components/formatted_time.tsx b/x-pack/plugins/logs_shared/public/components/formatted_time.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/formatted_time.tsx rename to x-pack/plugins/logs_shared/public/components/formatted_time.tsx diff --git a/x-pack/plugins/logs_shared/public/components/loading/__examples__/index.stories.tsx b/x-pack/plugins/logs_shared/public/components/loading/__examples__/index.stories.tsx new file mode 100644 index 000000000000..879d8a2328eb --- /dev/null +++ b/x-pack/plugins/logs_shared/public/components/loading/__examples__/index.stories.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 { Meta, Story } from '@storybook/react/types-6-0'; +import React from 'react'; +import { LogsSharedLoadingPanel } from '..'; +import { decorateWithGlobalStorybookThemeProviders } from '../../../test_utils/use_global_storybook_theme'; + +export default { + title: 'logs_shared/LogsSharedLoadingPanel', + decorators: [ + (wrappedStory) =>
{wrappedStory()}
, + decorateWithGlobalStorybookThemeProviders, + ], +} as Meta; + +export const LoadingPanel: Story = () => ( + +); diff --git a/x-pack/plugins/logs_shared/public/components/loading/index.tsx b/x-pack/plugins/logs_shared/public/components/loading/index.tsx new file mode 100644 index 000000000000..ca032885a895 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/components/loading/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiLoadingChart, EuiPanel, EuiText } from '@elastic/eui'; +import * as React from 'react'; + +import { euiStyled } from '@kbn/kibana-react-plugin/common'; + +interface LogsSharedLoadingProps { + text: string | JSX.Element; + height: number | string; + width: number | string; +} + +export class LogsSharedLoadingPanel extends React.PureComponent { + public render() { + const { height, text, width } = this.props; + return ( + + + + + +

{text}

+
+
+
+
+ ); + } +} + +export const LogsSharedLoadingStaticPanel = euiStyled.div` + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: center; +`; + +export const LogsSharedLoadingStaticContentPanel = euiStyled.div` + flex: 0 0 auto; + align-self: center; + text-align: center; +`; diff --git a/x-pack/plugins/infra/public/components/log_stream/index.ts b/x-pack/plugins/logs_shared/public/components/log_stream/index.ts similarity index 100% rename from x-pack/plugins/infra/public/components/log_stream/index.ts rename to x-pack/plugins/logs_shared/public/components/log_stream/index.ts diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.mdx similarity index 91% rename from x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx rename to x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.mdx index b6e1d5f7fe0d..36bddaf0a11d 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.mdx +++ b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.mdx @@ -1,15 +1,15 @@ import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs/blocks'; - + # Embeddable `` component The purpose of this component is to allow you, the developer, to have your very own Log Stream in your plugin. -The component is exposed through `infra/public`. Since Kibana uses relative paths, it is up to you to find how to import it (sorry). +The component is exposed through `logs_shared/public`. Since Kibana uses relative paths, it is up to you to find how to import it (sorry). ```tsx -import { LogStream } from '../../../../../../infra/public'; +import { LogStream } from '../../../../../../logs_shared/public'; // ^^ Modify appropriately ``` @@ -17,7 +17,7 @@ import { LogStream } from '../../../../../../infra/public'; To use the component your plugin needs to follow certain criteria: -- Ensure `"infra"` and `"data"` are specified as a `requiredPlugins` in your plugin's `kibana.json`. +- Ensure `"logsShared"` and `"data"` are specified as a `requiredPlugins` in your plugin's `kibana.json`. - Ensure the `` component is mounted inside the hierachy of a [`kibana-react` provider](https://github.com/elastic/kibana/blob/b2d0aa7b7fae1c89c8f9e8854ae73e71be64e765/src/plugins/kibana_react/README.md#L45). At a minimum, the kibana-react provider must pass `http` (from core start services) and `data` (from core plugin start dependencies). - Ensure the `` component is mounted inside the hierachy of a [`KibanaThemeProvider`](https://github.com/elastic/kibana/blob/31d2db035c905fb5819fa6dc2354f3be795a34cf/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx#L27). - Ensure the `` component is mounted inside the hierachy of a [`EuiThemeProvider`](https://github.com/elastic/kibana/blob/main/src/plugins/kibana_react/common/eui_styled_components.tsx). This is not the same as the provider exported by EUI. It bridges the gap between EUI and styled components and predates the css-in-js support in EUI. @@ -35,7 +35,7 @@ const startTimestamp = endTimestamp - 15 * 60 * 1000; // 15 minutes This will show a list of log entries between the specified timestamps. - + ## Query log entries @@ -80,7 +80,7 @@ The component also has a `filters` prop that accepts valid es-query `filters`. T ## Center the view on a specific entry -By default the component will load at the bottom of the list, showing the newest entries. You can change the rendering point with the `center` prop. The prop takes a [`LogEntriesCursor`](https://github.com/elastic/kibana/blob/0a6c748cc837c016901f69ff05d81395aa2d41c8/x-pack/plugins/infra/common/http_api/log_entries/common.ts#L9-L13). +By default the component will load at the bottom of the list, showing the newest entries. You can change the rendering point with the `center` prop. The prop takes a [`LogEntriesCursor`](https://github.com/elastic/kibana/blob/0a6c748cc837c016901f69ff05d81395aa2d41c8/x-pack/plugins/logs_shared/common/http_api/log_entries/common.ts#L9-L13). ```tsx ``` - + ## Highlight a specific entry @@ -100,7 +100,7 @@ The component can highlight a specific line via the `highlight` prop. It takes t ``` - + ## Column configuration @@ -131,7 +131,7 @@ The easiest way is to specify what columns you want with the `columns` prop. /> ``` - + The rendering of the column headers and the cell contents can also be customized with the following properties: @@ -206,7 +206,7 @@ The rendering of the column headers and the cell contents can also be customized /> ``` - + ### With a static log view configuration @@ -262,4 +262,4 @@ The component can render a button on the row that opens a flyout, via the `showF showFlyoutAction /> ``` - + diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.stories.tsx b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/log_stream/log_stream.stories.tsx rename to x-pack/plugins/logs_shared/public/components/log_stream/log_stream.stories.tsx diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.story_decorators.tsx b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.story_decorators.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/log_stream/log_stream.story_decorators.tsx rename to x-pack/plugins/logs_shared/public/components/log_stream/log_stream.story_decorators.tsx diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/log_stream/log_stream.tsx rename to x-pack/plugins/logs_shared/public/components/log_stream/log_stream.tsx diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream_error_boundary.tsx similarity index 93% rename from x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx rename to x-pack/plugins/logs_shared/public/components/log_stream/log_stream_error_boundary.tsx index c2e025dcd5e7..cf60b902e3b3 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx +++ b/x-pack/plugins/logs_shared/public/components/log_stream/log_stream_error_boundary.tsx @@ -33,7 +33,7 @@ const LogStreamErrorContent: React.FC<{ @@ -46,7 +46,7 @@ const LogStreamErrorContent: React.FC<{ diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/index.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/index.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx similarity index 90% rename from x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx index e06149dd90d5..e99304a61b45 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx @@ -8,7 +8,6 @@ import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useMemo } from 'react'; -import { getApmTraceUrl } from '@kbn/observability-plugin/public'; import { useLinkProps, LinkDescriptor } from '@kbn/observability-shared-plugin/public'; import { useVisibilityState } from '../../../utils/use_visibility_state'; import { LogEntry } from '../../../../common/search_strategies/log_entries/log_entry'; @@ -45,7 +44,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { {...uptimeLinkProps} > , @@ -57,7 +56,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { {...apmLinkProps} > , @@ -78,7 +77,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { onClick={toggle} > @@ -142,3 +141,15 @@ const getAPMLink = (logEntry: LogEntry): LinkDescriptor | undefined => { pathname: getApmTraceUrl({ traceId, rangeFrom, rangeTo }), }; }; + +function getApmTraceUrl({ + traceId, + rangeFrom, + rangeTo, +}: { + traceId: string; + rangeFrom: string; + rangeTo: string; +}) { + return `/link-to/trace/${traceId}?` + new URLSearchParams({ rangeFrom, rangeTo }).toString(); +} diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx similarity index 90% rename from x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx index a503c0501124..09b838185935 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx @@ -47,7 +47,7 @@ export const LogEntryFieldsTable: React.FC<{ () => [ { field: 'field', - name: i18n.translate('xpack.infra.logFlyout.fieldColumnLabel', { + name: i18n.translate('xpack.logsShared.logFlyout.fieldColumnLabel', { defaultMessage: 'Field', }), sortable: true, @@ -66,7 +66,7 @@ export const LogEntryFieldsTable: React.FC<{ }, { field: 'value', - name: i18n.translate('xpack.infra.logFlyout.valueColumnLabel', { + name: i18n.translate('xpack.logsShared.logFlyout.valueColumnLabel', { defaultMessage: 'Value', }), render: (_name: string, item: LogEntryField) => ( @@ -107,11 +107,11 @@ const searchOptions = { }, }; -const setFilterButtonLabel = i18n.translate('xpack.infra.logFlyout.filterAriaLabel', { +const setFilterButtonLabel = i18n.translate('xpack.logsShared.logFlyout.filterAriaLabel', { defaultMessage: 'Filter', }); -const setFilterButtonDescription = i18n.translate('xpack.infra.logFlyout.setFilterTooltip', { +const setFilterButtonDescription = i18n.translate('xpack.logsShared.logFlyout.setFilterTooltip', { defaultMessage: 'View event with filter', }); diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx similarity index 91% rename from x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx index 8237a36f5557..e06064c676a6 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx @@ -146,7 +146,7 @@ export const LogEntryFlyout = ({

{logEntryId} : '', }} @@ -158,7 +158,7 @@ export const LogEntryFlyout = ({ {logEntry.index}, @@ -237,18 +237,24 @@ export const LogEntryFlyout = ({ ); }; -const explainLogMessageTitle = i18n.translate('xpack.infra.logFlyout.explainLogMessageTitle', { +const explainLogMessageTitle = i18n.translate('xpack.logsShared.logFlyout.explainLogMessageTitle', { defaultMessage: "What's this message?", }); -const similarLogMessagesTitle = i18n.translate('xpack.infra.logFlyout.similarLogMessagesTitle', { - defaultMessage: 'How do I find similar log messages?', -}); +const similarLogMessagesTitle = i18n.translate( + 'xpack.logsShared.logFlyout.similarLogMessagesTitle', + { + defaultMessage: 'How do I find similar log messages?', + } +); -const loadingProgressMessage = i18n.translate('xpack.infra.logFlyout.loadingMessage', { +const loadingProgressMessage = i18n.translate('xpack.logsShared.logFlyout.loadingMessage', { defaultMessage: 'Searching log entry in shards', }); -const loadingErrorCalloutTitle = i18n.translate('xpack.infra.logFlyout.loadingErrorCalloutTitle', { - defaultMessage: 'Error while searching the log entry', -}); +const loadingErrorCalloutTitle = i18n.translate( + 'xpack.logsShared.logFlyout.loadingErrorCalloutTitle', + { + defaultMessage: 'Error while searching the log entry', + } +); diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/column_headers.tsx similarity index 96% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/column_headers.tsx index 30355ea1a9a2..9e1d9330f7b8 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/column_headers.tsx @@ -43,7 +43,7 @@ export const LogColumnHeaders: React.FunctionComponent<{ } else { columnHeader = firstVisiblePosition ? localizedDate(firstVisiblePosition.time) - : i18n.translate('xpack.infra.logs.stream.timestampColumnTitle', { + : i18n.translate('xpack.logsShared.logs.stream.timestampColumnTitle', { defaultMessage: 'Timestamp', }); } @@ -64,7 +64,7 @@ export const LogColumnHeaders: React.FunctionComponent<{ } else if (typeof columnConfiguration.messageColumn.header === 'string') { columnHeader = columnConfiguration.messageColumn.header; } else { - columnHeader = i18n.translate('xpack.infra.logs.stream.messageColumnTitle', { + columnHeader = i18n.translate('xpack.logsShared.logs.stream.messageColumnTitle', { defaultMessage: 'Message', }); } diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/field_value.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/field_value.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/highlighting.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/highlighting.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/index.ts b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/index.ts similarity index 79% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/index.ts rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/index.ts index dbd5bd49d024..dfccaae6dd8c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/index.ts +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/index.ts @@ -5,11 +5,14 @@ * 2.0. */ +export type { LogEntryStreamItem } from './item'; export type { LogEntryColumnWidths } from './log_entry_column'; -export { LogEntryColumn, useColumnWidths, iconColumnId } from './log_entry_column'; + +export { LogColumnHeader, LogColumnHeadersWrapper } from './column_headers'; +export { iconColumnId, LogEntryColumn, useColumnWidths } from './log_entry_column'; +export { LogEntryContextMenu } from './log_entry_context_menu'; export { LogEntryFieldColumn } from './log_entry_field_column'; export { LogEntryMessageColumn } from './log_entry_message_column'; export { LogEntryRowWrapper } from './log_entry_row'; export { LogEntryTimestampColumn } from './log_entry_timestamp_column'; export { ScrollableLogTextStreamView } from './scrollable_log_text_stream_view'; -export { LogEntryContextMenu } from './log_entry_context_menu'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/item.ts b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/item.ts similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/item.ts rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/item.ts diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/jump_to_tail.tsx similarity index 93% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/jump_to_tail.tsx index 81bb3f299db5..1f3f35d0d1a1 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/jump_to_tail.tsx @@ -24,7 +24,7 @@ export class LogTextStreamJumpToTail extends React.PureComponent @@ -36,7 +36,7 @@ export class LogTextStreamJumpToTail extends React.PureComponent diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/loading_item_view.tsx similarity index 89% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/loading_item_view.tsx index f0b0fc4236bf..9cd7c9125052 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/loading_item_view.tsx @@ -118,19 +118,19 @@ const ProgressMessage: React.FC = ({ timestamp, position, const message = position === 'start' ? ( ) : isStreaming ? ( ) : ( @@ -152,12 +152,12 @@ const ProgressSpinner: React.FC<{ kind: 'streaming' | 'loading' }> = ({ kind }) {kind === 'streaming' ? ( ) : ( )} @@ -182,7 +182,7 @@ const ProgressCta: React.FC = ({ if (rangeEdge === 'now' && position === 'end') { return ( - + ); } @@ -217,7 +217,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'ms': return ( @@ -225,7 +225,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 's': return ( @@ -233,7 +233,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'm': return ( @@ -241,7 +241,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'h': return ( @@ -249,7 +249,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'd': return ( @@ -257,7 +257,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'w': return ( @@ -265,7 +265,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'M': return ( @@ -273,7 +273,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'y': return ( diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_date_row.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_date_row.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_column.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_column.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_context_menu.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_context_menu.tsx index ba87fa8c09bc..17c7fb9dd7e9 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_context_menu.tsx @@ -35,7 +35,7 @@ interface LogEntryContextMenuProps { } const DEFAULT_MENU_LABEL = i18n.translate( - 'xpack.infra.logEntryItemView.logEntryActionsMenuToolTip', + 'xpack.logsShared.logEntryItemView.logEntryActionsMenuToolTip', { defaultMessage: 'View actions for line', } diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_field_column.test.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_field_column.test.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_field_column.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_field_column.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_message_column.test.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.test.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_message_column.test.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_message_column.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_message_column.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx index 4a137ca361a4..2963a3e1aac1 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx @@ -32,16 +32,16 @@ import { LogEntryMessageColumn } from './log_entry_message_column'; import { LogEntryTimestampColumn } from './log_entry_timestamp_column'; import { highlightedContentStyle, hoveredContentStyle, monospaceTextStyle } from './text_styles'; -const MENU_LABEL = i18n.translate('xpack.infra.logEntryItemView.logEntryActionsMenuToolTip', { +const MENU_LABEL = i18n.translate('xpack.logsShared.logEntryItemView.logEntryActionsMenuToolTip', { defaultMessage: 'View actions for line', }); -const LOG_DETAILS_LABEL = i18n.translate('xpack.infra.logs.logEntryActionsDetailsButton', { +const LOG_DETAILS_LABEL = i18n.translate('xpack.logsShared.logs.logEntryActionsDetailsButton', { defaultMessage: 'View details', }); const LOG_VIEW_IN_CONTEXT_LABEL = i18n.translate( - 'xpack.infra.lobs.logEntryActionsViewInContextButton', + 'xpack.logsShared.lobs.logEntryActionsViewInContextButton', { defaultMessage: 'View in context', } diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_text_separator.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_text_separator.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/log_text_separator.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/log_text_separator.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/measurable_item_view.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/measurable_item_view.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 4c11bd21daa1..8a0a120a3a7d 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -16,7 +16,7 @@ import { TimeKey, UniqueTimeKey } from '../../../../common/time'; import { callWithoutRepeats } from '../../../utils/handlers'; import { AutoSizer } from '../../auto_sizer'; import { NoData } from '../../empty_states'; -import { InfraLoadingPanel } from '../../loading'; +import { LogsSharedLoadingPanel } from '../../loading'; import { getStreamItemBeforeTimeKey, getStreamItemId, parseStreamItemId, StreamItem } from './item'; import { LogColumnHeaders } from './column_headers'; import { LogTextStreamLoadingItemView } from './loading_item_view'; @@ -160,27 +160,30 @@ export class ScrollableLogTextStreamView extends React.PureComponent< return ( {isReloading && (!isStreaming || !hasItems) ? ( - } /> ) : !hasItems ? ( @@ -368,6 +371,9 @@ export class ScrollableLogTextStreamView extends React.PureComponent< }; } +// eslint-disable-next-line import/no-default-export +export default ScrollableLogTextStreamView; + /** * If the above component wasn't a class component, this wouldn't be necessary * since the `useColumnWidths` hook could have been used directly. diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/text_styles.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/text_styles.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_text_stream/vertical_scroll_panel.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx rename to x-pack/plugins/logs_shared/public/components/logging/log_text_stream/vertical_scroll_panel.tsx diff --git a/x-pack/plugins/infra/public/components/resettable_error_boundary.tsx b/x-pack/plugins/logs_shared/public/components/resettable_error_boundary.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/resettable_error_boundary.tsx rename to x-pack/plugins/logs_shared/public/components/resettable_error_boundary.tsx diff --git a/x-pack/plugins/infra/public/containers/logs/log_entry.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_entry.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_entry.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_entry.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/index.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/index.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/index.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/index.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_entry_highlights.tsx similarity index 98% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_entry_highlights.tsx index ea0e1fa326c7..6e13ff542add 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -6,7 +6,7 @@ */ import { useEffect, useMemo, useState } from 'react'; -import { LogViewReference } from '../../../../common/log_views'; +import { LogViewReference } from '../../../../common'; import { LogEntriesHighlightsResponse } from '../../../../common/http_api'; import { LogEntry } from '../../../../common/log_entry'; import { TimeKey } from '../../../../common/time'; diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_highlights.tsx similarity index 96% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_highlights.tsx index 0a6710731bcb..aa963925d7ac 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_highlights.tsx @@ -8,12 +8,13 @@ import createContainer from 'constate'; import { useState } from 'react'; import useThrottle from 'react-use/lib/useThrottle'; -import { LogViewReference } from '../../../../common/log_views'; + +import { LogViewReference } from '../../../../common'; import { useLogEntryHighlights } from './log_entry_highlights'; import { useLogSummaryHighlights } from './log_summary_highlights'; import { useNextAndPrevious } from './next_and_previous'; -import { useLogPositionStateContext } from '../log_position'; import { TimeKey } from '../../../../common/time'; +import { useLogPositionStateContext } from '../log_position'; const FETCH_THROTTLE_INTERVAL = 3000; @@ -25,7 +26,7 @@ interface UseLogHighlightsStateProps { filterQuery: string | null; } -export const useLogHighlightsState = ({ +const useLogHighlightsState = ({ logViewReference, sourceVersion, centerCursor, diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_summary_highlights.ts similarity index 97% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_summary_highlights.ts index 8c5f7fb7ae77..61a1a02618e7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -8,7 +8,7 @@ import { useEffect, useMemo, useState } from 'react'; import { debounce } from 'lodash'; -import { LogViewReference } from '../../../../common/log_views'; +import { LogViewReference } from '../../../../common'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogSummaryHighlights } from './api/fetch_log_summary_highlights'; import { LogEntriesSummaryHighlightsResponse } from '../../../../common/http_api'; diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx b/x-pack/plugins/logs_shared/public/containers/logs/log_highlights/next_and_previous.tsx similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx rename to x-pack/plugins/logs_shared/public/containers/logs/log_highlights/next_and_previous.tsx diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/index.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_position/index.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_position/index.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_position/index.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/use_log_position.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_position/use_log_position.ts similarity index 80% rename from x-pack/plugins/infra/public/containers/logs/log_position/use_log_position.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_position/use_log_position.ts index 2471acf6e928..236e59093d45 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/use_log_position.ts +++ b/x-pack/plugins/logs_shared/public/containers/logs/log_position/use_log_position.ts @@ -5,15 +5,28 @@ * 2.0. */ +import { TimeRange } from '@kbn/es-query'; import createContainer from 'constate'; import { useMemo } from 'react'; -import { VisiblePositions } from '../../../observability_logs/log_stream_position_state/src/types'; -import { - LogStreamPageActorRef, - LogStreamPageCallbacks, -} from '../../../observability_logs/log_stream_page/state'; -import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; +import { ActorRefWithDeprecatedState } from 'xstate'; import { TimeKey } from '../../../../common/time'; +import { + MatchedStateFromActor, + OmitDeprecatedState, +} from '../../../observability_logs/xstate_helpers'; + +type LogStreamPageState = MatchedStateFromActor< + OmitDeprecatedState>, + { hasLogViewIndices: 'initialized' } +>; + +interface VisiblePositions { + startKey: TimeKey | null; + middleKey: TimeKey | null; + endKey: TimeKey | null; + pagesAfterEnd: number; + pagesBeforeStart: number; +} type TimeKeyOrNull = TimeKey | null; @@ -46,6 +59,15 @@ export interface LogPositionCallbacks { updateDateRange: UpdateDateRangeFn; } +export interface LogStreamPageCallbacks { + updateTimeRange: (timeRange: Partial) => void; + jumpToTargetPosition: (targetPosition: TimeKey | null) => void; + jumpToTargetPositionTime: (time: number) => void; + reportVisiblePositions: (visiblePositions: VisiblePositions) => void; + startLiveStreaming: () => void; + stopLiveStreaming: () => void; +} + type UpdateDateRangeFn = ( newDateRange: Partial> ) => void; @@ -54,7 +76,7 @@ export const useLogPositionState = ({ logStreamPageState, logStreamPageCallbacks, }: { - logStreamPageState: InitializedLogStreamPageState; + logStreamPageState: LogStreamPageState; logStreamPageCallbacks: LogStreamPageCallbacks; }): LogPositionStateParams & LogPositionCallbacks => { const dateRange = useMemo(() => getLegacyDateRange(logStreamPageState), [logStreamPageState]); @@ -119,7 +141,7 @@ export const useLogPositionState = ({ export const [LogPositionStateProvider, useLogPositionStateContext] = createContainer(useLogPositionState); -const getLegacyDateRange = (logStreamPageState: InitializedLogStreamPageState): DateRange => { +const getLegacyDateRange = (logStreamPageState: LogStreamPageState): DateRange => { return { startDateExpression: logStreamPageState.context.timeRange.from, endDateExpression: logStreamPageState.context.timeRange.to, @@ -130,8 +152,3 @@ const getLegacyDateRange = (logStreamPageState: InitializedLogStreamPageState): timestampsLastUpdate: logStreamPageState.context.timestamps.lastChangedTimestamp, }; }; - -type InitializedLogStreamPageState = MatchedStateFromActor< - LogStreamPageActorRef, - { hasLogViewIndices: 'initialized' } ->; diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_stream/index.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_stream/index.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_stream/index.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_stream/use_fetch_log_entries_after.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_stream/use_fetch_log_entries_after.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_stream/use_fetch_log_entries_around.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_stream/use_fetch_log_entries_around.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_stream/use_fetch_log_entries_before.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_stream/use_fetch_log_entries_before.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/api/fetch_log_summary.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_summary/api/fetch_log_summary.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/bucket_size.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/bucket_size.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_summary/bucket_size.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_summary/bucket_size.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/index.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/index.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_summary/index.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_summary/index.ts diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/log_summary.test.tsx similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx rename to x-pack/plugins/logs_shared/public/containers/logs/log_summary/log_summary.test.tsx diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/log_summary.tsx similarity index 97% rename from x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx rename to x-pack/plugins/logs_shared/public/containers/logs/log_summary/log_summary.tsx index c2792300c5f3..0360d6d381ad 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/log_summary.tsx @@ -8,7 +8,7 @@ import { useEffect } from 'react'; import { exhaustMap, map, Observable } from 'rxjs'; import { HttpHandler } from '@kbn/core-http-browser'; -import { LogViewReference } from '../../../../common/log_views'; +import { LogViewReference } from '../../../../common'; import { useObservableState, useReplaySubject } from '../../../utils/use_observable'; import { fetchLogSummary } from './api/fetch_log_summary'; import { LogEntriesSummaryRequest, LogEntriesSummaryResponse } from '../../../../common/http_api'; diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/with_summary.ts similarity index 66% rename from x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts rename to x-pack/plugins/logs_shared/public/containers/logs/log_summary/with_summary.ts index 1dc5c7021d25..5d18926cca29 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/logs_shared/public/containers/logs/log_summary/with_summary.ts @@ -5,32 +5,24 @@ * 2.0. */ -import { useSelector } from '@xstate/react'; -import stringify from 'json-stable-stringify'; import useThrottle from 'react-use/lib/useThrottle'; -import { useLogViewContext } from '../../../hooks/use_log_view'; -import { useLogStreamPageStateContext } from '../../../observability_logs/log_stream_page/state'; +import { useLogPositionStateContext, useLogViewContext } from '../../..'; import { RendererFunction } from '../../../utils/typed_react'; -import { useLogPositionStateContext } from '../log_position'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; const FETCH_THROTTLE_INTERVAL = 3000; -export const WithSummary = ({ - children, -}: { +export interface WithSummaryProps { + serializedParsedQuery: string | null; children: RendererFunction<{ buckets: LogSummaryBuckets; start: number | null; end: number | null; }>; -}) => { +} + +export const WithSummary = ({ serializedParsedQuery, children }: WithSummaryProps) => { const { logViewReference } = useLogViewContext(); - const serializedParsedQuery = useSelector(useLogStreamPageStateContext(), (logStreamPageState) => - logStreamPageState.matches({ hasLogViewIndices: 'initialized' }) - ? stringify(logStreamPageState.context.parsedQuery) - : null - ); const { startTimestamp, endTimestamp } = useLogPositionStateContext(); // Keep it reasonably updated for the `now` case, but don't reload all the time when the user scrolls diff --git a/x-pack/plugins/logs_shared/public/hooks/use_kibana.tsx b/x-pack/plugins/logs_shared/public/hooks/use_kibana.tsx new file mode 100644 index 000000000000..09032b4b644a --- /dev/null +++ b/x-pack/plugins/logs_shared/public/hooks/use_kibana.tsx @@ -0,0 +1,71 @@ +/* + * 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 { PropsOf } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { CoreStart } from '@kbn/core/public'; +import { + createKibanaReactContext, + KibanaReactContextValue, + useKibana, +} from '@kbn/kibana-react-plugin/public'; +import { + LogsSharedClientCoreSetup, + LogsSharedClientStartDeps, + LogsSharedClientStartExports, +} from '../types'; + +export type PluginKibanaContextValue = CoreStart & + LogsSharedClientStartDeps & + LogsSharedClientStartExports; + +export const createKibanaContextForPlugin = ( + core: CoreStart, + plugins: LogsSharedClientStartDeps, + pluginStart: LogsSharedClientStartExports +) => + createKibanaReactContext({ + ...core, + ...plugins, + ...pluginStart, + }); + +export const useKibanaContextForPlugin = + useKibana as () => KibanaReactContextValue; + +export const useKibanaContextForPluginProvider = ( + core: CoreStart, + plugins: LogsSharedClientStartDeps, + pluginStart: LogsSharedClientStartExports +) => { + const { Provider } = useMemo( + () => createKibanaContextForPlugin(core, plugins, pluginStart), + [core, pluginStart, plugins] + ); + + return Provider; +}; + +export const createLazyComponentWithKibanaContext = >( + coreSetup: LogsSharedClientCoreSetup, + lazyComponentFactory: () => Promise<{ default: T }> +) => + React.lazy(() => + Promise.all([lazyComponentFactory(), coreSetup.getStartServices()]).then( + ([{ default: LazilyLoadedComponent }, [core, plugins, pluginStart]]) => { + const { Provider } = createKibanaContextForPlugin(core, plugins, pluginStart); + + return { + default: (props: PropsOf) => ( + + + + ), + }; + } + ) + ); diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.ts b/x-pack/plugins/logs_shared/public/hooks/use_log_view.ts similarity index 100% rename from x-pack/plugins/infra/public/hooks/use_log_view.ts rename to x-pack/plugins/logs_shared/public/hooks/use_log_view.ts diff --git a/x-pack/plugins/logs_shared/public/index.ts b/x-pack/plugins/logs_shared/public/index.ts new file mode 100644 index 000000000000..63692bbdeae5 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/index.ts @@ -0,0 +1,92 @@ +/* + * 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 { dynamic } from '../common/dynamic'; +import { LogsSharedPlugin } from './plugin'; + +export type { + LogsSharedClientSetupExports, + LogsSharedClientStartExports, + LogsSharedClientSetupDeps, + LogsSharedClientStartDeps, +} from './types'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new LogsSharedPlugin(); +} + +// Containers & Hook +export { LogViewProvider, useLogViewContext, useLogView } from './hooks/use_log_view'; +export { LogStreamProvider, useLogStreamContext } from './containers/logs/log_stream'; +export { + LogPositionStateProvider, + useLogPositionStateContext, +} from './containers/logs/log_position'; +export { + LogHighlightsStateProvider, + useLogHighlightsStateContext, +} from './containers/logs/log_highlights'; +export type { LogSummaryBuckets, WithSummaryProps } from './containers/logs/log_summary'; +export { useLogSummary, WithSummary } from './containers/logs/log_summary'; +export { useLogEntryFlyout } from './components/logging/log_entry_flyout'; + +// Shared components +export type { + LogEntryStreamItem, + LogEntryColumnWidths, +} from './components/logging/log_text_stream'; +export { + iconColumnId, + useColumnWidths, +} from './components/logging/log_text_stream/log_entry_column'; +export { LogEntryFlyout } from './components/logging/log_entry_flyout'; +export type { LogStreamProps } from './components/log_stream/log_stream'; + +export const LogStream = dynamic(() => import('./components/log_stream/log_stream')); +export const LogColumnHeader = dynamic( + () => import('./components/logging/log_text_stream/column_headers') +); +export const LogColumnHeadersWrapper = dynamic( + () => import('./components/logging/log_text_stream/column_headers') +); +export const LogEntryColumn = dynamic( + () => import('./components/logging/log_text_stream/log_entry_column') +); +export const LogEntryContextMenu = dynamic( + () => import('./components/logging/log_text_stream/log_entry_context_menu') +); +export const LogEntryFieldColumn = dynamic( + () => import('./components/logging/log_text_stream/log_entry_field_column') +); +export const LogEntryMessageColumn = dynamic( + () => import('./components/logging/log_text_stream/log_entry_message_column') +); +export const LogEntryRowWrapper = dynamic( + () => import('./components/logging/log_text_stream/log_entry_row') +); +export const LogEntryTimestampColumn = dynamic( + () => import('./components/logging/log_text_stream/log_entry_timestamp_column') +); +export const ScrollableLogTextStreamView = dynamic( + () => import('./components/logging/log_text_stream/scrollable_log_text_stream_view') +); + +// State machine utils +export { + getLogViewReferenceFromUrl, + initializeFromUrl, + listenForUrlChanges, + updateContextInUrl, +} from './observability_logs/log_view_state'; +export type { + LogViewContextWithError, + LogViewContextWithResolvedLogView, + LogViewNotificationChannel, + LogViewNotificationEvent, +} from './observability_logs/log_view_state'; diff --git a/x-pack/plugins/logs_shared/public/mocks.tsx b/x-pack/plugins/logs_shared/public/mocks.tsx new file mode 100644 index 000000000000..963480d8fd90 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/mocks.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createLogViewsServiceStartMock } from './services/log_views/log_views_service.mock'; +import { LogsSharedClientStartExports } from './types'; + +export const createLogsSharedPluginStartMock = (): jest.Mocked => ({ + logViews: createLogViewsServiceStartMock(), +}); + +export const _ensureTypeCompatibility = (): LogsSharedClientStartExports => + createLogsSharedPluginStartMock(); diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/README.md b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/README.md similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/README.md rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/README.md diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/index.ts b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/index.ts similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/index.ts rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/index.ts diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/index.ts similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/index.ts diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/notifications.ts similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/notifications.ts diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/state_machine.ts similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/state_machine.ts diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/types.ts similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/types.ts diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts b/x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/url_state_storage_service.ts similarity index 100% rename from x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts rename to x-pack/plugins/logs_shared/public/observability_logs/log_view_state/src/url_state_storage_service.ts diff --git a/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/README.md b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/README.md new file mode 100644 index 000000000000..337f65add422 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/README.md @@ -0,0 +1,3 @@ +# @kbn/observability-logs-xstate-helpers + +Helpers to design well-typed state machines with XState diff --git a/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/index.ts b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/index.ts new file mode 100644 index 000000000000..3b2a320ae181 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/index.ts @@ -0,0 +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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src'; diff --git a/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/index.ts b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/index.ts new file mode 100644 index 000000000000..340087bb31fc --- /dev/null +++ b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/index.ts @@ -0,0 +1,9 @@ +/* + * 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 './notification_channel'; +export * from './types'; diff --git a/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/notification_channel.ts b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/notification_channel.ts new file mode 100644 index 000000000000..0108ab022517 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/notification_channel.ts @@ -0,0 +1,41 @@ +/* + * 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 { ReplaySubject } from 'rxjs'; +import { ActionFunction, EventObject, Expr, Subscribable } from 'xstate'; + +export interface NotificationChannel { + createService: () => Subscribable; + notify: ( + eventExpr: Expr + ) => ActionFunction; +} + +export const createNotificationChannel = < + TContext, + TEvent extends EventObject, + TSentEvent +>(): NotificationChannel => { + const eventsSubject = new ReplaySubject(1); + + const createService = () => eventsSubject.asObservable(); + + const notify = + (eventExpr: Expr) => + (context: TContext, event: TEvent) => { + const eventToSend = eventExpr(context, event); + + if (eventToSend != null) { + eventsSubject.next(eventToSend); + } + }; + + return { + createService, + notify, + }; +}; diff --git a/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/types.ts b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/types.ts new file mode 100644 index 000000000000..05e75c5fe6e4 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/observability_logs/xstate_helpers/src/types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActorRef, ActorRefWithDeprecatedState, EmittedFrom, State, StateValue } from 'xstate'; + +export type OmitDeprecatedState> = Omit< + T, + 'state' +>; + +export type MatchedState< + TState extends State, + TStateValue extends StateValue +> = TState extends State< + any, + infer TEvent, + infer TStateSchema, + infer TTypestate, + infer TResolvedTypesMeta +> + ? State< + (TTypestate extends any + ? { value: TStateValue; context: any } extends TTypestate + ? TTypestate + : never + : never)['context'], + TEvent, + TStateSchema, + TTypestate, + TResolvedTypesMeta + > & { + value: TStateValue; + } + : never; + +export type MatchedStateFromActor< + TActorRef extends ActorRef, + TStateValue extends StateValue +> = MatchedState, TStateValue>; diff --git a/x-pack/plugins/logs_shared/public/plugin.ts b/x-pack/plugins/logs_shared/public/plugin.ts new file mode 100644 index 000000000000..47ded2e3df83 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/plugin.ts @@ -0,0 +1,38 @@ +/* + * 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 { CoreStart } from '@kbn/core/public'; +import { LogViewsService } from './services/log_views'; +import { LogsSharedClientPluginClass, LogsSharedClientStartDeps } from './types'; + +export class LogsSharedPlugin implements LogsSharedClientPluginClass { + private logViews: LogViewsService; + + constructor() { + this.logViews = new LogViewsService(); + } + + public setup() { + const logViews = this.logViews.setup(); + + return { logViews }; + } + + public start(core: CoreStart, plugins: LogsSharedClientStartDeps) { + const logViews = this.logViews.start({ + http: core.http, + dataViews: plugins.dataViews, + search: plugins.data.search, + }); + + return { + logViews, + }; + } + + public stop() {} +} diff --git a/x-pack/plugins/infra/public/services/log_views/index.ts b/x-pack/plugins/logs_shared/public/services/log_views/index.ts similarity index 100% rename from x-pack/plugins/infra/public/services/log_views/index.ts rename to x-pack/plugins/logs_shared/public/services/log_views/index.ts diff --git a/x-pack/plugins/infra/public/services/log_views/log_views_client.mock.ts b/x-pack/plugins/logs_shared/public/services/log_views/log_views_client.mock.ts similarity index 100% rename from x-pack/plugins/infra/public/services/log_views/log_views_client.mock.ts rename to x-pack/plugins/logs_shared/public/services/log_views/log_views_client.mock.ts diff --git a/x-pack/plugins/infra/public/services/log_views/log_views_client.ts b/x-pack/plugins/logs_shared/public/services/log_views/log_views_client.ts similarity index 100% rename from x-pack/plugins/infra/public/services/log_views/log_views_client.ts rename to x-pack/plugins/logs_shared/public/services/log_views/log_views_client.ts diff --git a/x-pack/plugins/infra/public/services/log_views/log_views_service.mock.ts b/x-pack/plugins/logs_shared/public/services/log_views/log_views_service.mock.ts similarity index 100% rename from x-pack/plugins/infra/public/services/log_views/log_views_service.mock.ts rename to x-pack/plugins/logs_shared/public/services/log_views/log_views_service.mock.ts diff --git a/x-pack/plugins/infra/public/services/log_views/log_views_service.ts b/x-pack/plugins/logs_shared/public/services/log_views/log_views_service.ts similarity index 61% rename from x-pack/plugins/infra/public/services/log_views/log_views_service.ts rename to x-pack/plugins/logs_shared/public/services/log_views/log_views_service.ts index 9e081a8df502..712196c95205 100644 --- a/x-pack/plugins/infra/public/services/log_views/log_views_service.ts +++ b/x-pack/plugins/logs_shared/public/services/log_views/log_views_service.ts @@ -5,17 +5,23 @@ * 2.0. */ -import { LogViewsStaticConfig } from '../../../common/log_views'; +import { defaultLogViewsStaticConfig, LogViewsStaticConfig } from '../../../common/log_views'; import { LogViewsClient } from './log_views_client'; import { LogViewsServiceStartDeps, LogViewsServiceSetup, LogViewsServiceStart } from './types'; export class LogViewsService { - constructor(private readonly config: LogViewsStaticConfig) {} + private logViewsStaticConfig: LogViewsStaticConfig = defaultLogViewsStaticConfig; - public setup(): LogViewsServiceSetup {} + public setup(): LogViewsServiceSetup { + return { + setLogViewsStaticConfig: (config: LogViewsStaticConfig) => { + this.logViewsStaticConfig = config; + }, + }; + } public start({ dataViews, http, search }: LogViewsServiceStartDeps): LogViewsServiceStart { - const client = new LogViewsClient(dataViews, http, search.search, this.config); + const client = new LogViewsClient(dataViews, http, search.search, this.logViewsStaticConfig); return { client, diff --git a/x-pack/plugins/infra/public/services/log_views/types.ts b/x-pack/plugins/logs_shared/public/services/log_views/types.ts similarity index 90% rename from x-pack/plugins/infra/public/services/log_views/types.ts rename to x-pack/plugins/logs_shared/public/services/log_views/types.ts index 6fd7a4d208b3..58a504be789b 100644 --- a/x-pack/plugins/infra/public/services/log_views/types.ts +++ b/x-pack/plugins/logs_shared/public/services/log_views/types.ts @@ -12,11 +12,14 @@ import { LogView, LogViewAttributes, LogViewReference, + LogViewsStaticConfig, LogViewStatus, ResolvedLogView, } from '../../../common/log_views'; -export type LogViewsServiceSetup = void; +export interface LogViewsServiceSetup { + setLogViewsStaticConfig: (config: LogViewsStaticConfig) => void; +} export interface LogViewsServiceStart { client: ILogViewsClient; diff --git a/x-pack/plugins/logs_shared/public/test_utils/entries.ts b/x-pack/plugins/logs_shared/public/test_utils/entries.ts new file mode 100644 index 000000000000..4dc3732fd49d --- /dev/null +++ b/x-pack/plugins/logs_shared/public/test_utils/entries.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import faker from 'faker'; +import { LogEntry } from '../../common/log_entry'; +import { LogViewColumnConfiguration } from '../../common/log_views'; + +export const ENTRIES_EMPTY = { + data: { + entries: [], + topCursor: null, + bottomCursor: null, + }, +}; + +export function generateFakeEntries( + count: number, + startTimestamp: number, + endTimestamp: number, + columns: LogViewColumnConfiguration[] +): LogEntry[] { + const entries: LogEntry[] = []; + const timestampStep = Math.floor((endTimestamp - startTimestamp) / count); + for (let i = 0; i < count; i++) { + const timestamp = i === count - 1 ? endTimestamp : startTimestamp + timestampStep * i; + entries.push({ + id: `entry-${i}`, + index: 'logs-fake', + context: {}, + cursor: { time: timestamp, tiebreaker: i }, + columns: columns.map((column) => { + if ('timestampColumn' in column) { + return { columnId: column.timestampColumn.id, timestamp }; + } else if ('messageColumn' in column) { + return { + columnId: column.messageColumn.id, + message: [{ field: 'message', value: [fakeColumnValue('message')], highlights: [] }], + }; + } else { + return { + columnId: column.fieldColumn.id, + field: column.fieldColumn.field, + value: [fakeColumnValue(column.fieldColumn.field)], + highlights: [], + }; + } + }), + }); + } + + return entries; +} + +function fakeColumnValue(field: string): string { + switch (field) { + case 'message': + return faker.fake( + '{{internet.ip}} - [{{date.past}}] "GET {{internet.url}} HTTP/1.1" 200 {{random.number}} "-" "{{internet.userAgent}}"' + ); + case 'event.dataset': + return faker.fake('{{hacker.noun}}.{{hacker.noun}}'); + case 'log.file.path': + return faker.system.filePath(); + case 'log.level': + return faker.random.arrayElement(['debug', 'info', 'warn', 'error']); + case 'host.name': + return faker.hacker.noun(); + default: + return faker.lorem.sentence(); + } +} diff --git a/x-pack/plugins/logs_shared/public/test_utils/use_global_storybook_theme.tsx b/x-pack/plugins/logs_shared/public/test_utils/use_global_storybook_theme.tsx new file mode 100644 index 000000000000..4d1feb4617dc --- /dev/null +++ b/x-pack/plugins/logs_shared/public/test_utils/use_global_storybook_theme.tsx @@ -0,0 +1,59 @@ +/* + * 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 { DecoratorFn } from '@storybook/react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { BehaviorSubject } from 'rxjs'; +import type { CoreTheme } from '@kbn/core/public'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; + +type StoryContext = Parameters[1]; + +export const useGlobalStorybookTheme = ({ globals: { euiTheme } }: StoryContext) => { + const theme = useMemo(() => euiThemeFromId(euiTheme), [euiTheme]); + const [theme$] = useState(() => new BehaviorSubject(theme)); + + useEffect(() => { + theme$.next(theme); + }, [theme$, theme]); + + return { + theme, + theme$, + }; +}; + +export const GlobalStorybookThemeProviders: React.FC<{ storyContext: StoryContext }> = ({ + children, + storyContext, +}) => { + const { theme, theme$ } = useGlobalStorybookTheme(storyContext); + return ( + + {children} + + ); +}; + +export const decorateWithGlobalStorybookThemeProviders: DecoratorFn = ( + wrappedStory, + storyContext +) => ( + + {wrappedStory()} + +); + +const euiThemeFromId = (themeId: string): CoreTheme => { + switch (themeId) { + case 'v8.dark': + return { darkMode: true }; + default: + return { darkMode: false }; + } +}; diff --git a/x-pack/plugins/logs_shared/public/types.ts b/x-pack/plugins/logs_shared/public/types.ts new file mode 100644 index 000000000000..f2563ccb3433 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/types.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; you may not use this file except in compliance with the Elastic License + * 2.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 { CoreSetup, CoreStart, Plugin as PluginClass } from '@kbn/core/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +// import type { OsqueryPluginStart } from '../../osquery/public'; +import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views'; + +// Our own setup and start contract values +export interface LogsSharedClientSetupExports { + logViews: LogViewsServiceSetup; +} + +export interface LogsSharedClientStartExports { + logViews: LogViewsServiceStart; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface LogsSharedClientSetupDeps {} + +export interface LogsSharedClientStartDeps { + data: DataPublicPluginStart; + dataViews: DataViewsPublicPluginStart; + uiActions: UiActionsStart; +} + +export type LogsSharedClientCoreSetup = CoreSetup< + LogsSharedClientStartDeps, + LogsSharedClientStartExports +>; +export type LogsSharedClientCoreStart = CoreStart; +export type LogsSharedClientPluginClass = PluginClass< + LogsSharedClientSetupExports, + LogsSharedClientStartExports, + LogsSharedClientSetupDeps, + LogsSharedClientStartDeps +>; + +export type UnwrapPromise> = T extends Promise ? Value : never; + +export type LogsSharedClientStartServicesAccessor = LogsSharedClientCoreSetup['getStartServices']; +export type LogsSharedClientStartServices = UnwrapPromise< + ReturnType +>; diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/data_search.stories.mdx b/x-pack/plugins/logs_shared/public/utils/data_search/data_search.stories.mdx new file mode 100644 index 000000000000..a8854692caa3 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/data_search.stories.mdx @@ -0,0 +1,152 @@ +import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks'; + + + +# The `data` plugin and `SearchStrategies` + +The search functionality abstraction provided by the `search` service of the +`data` plugin is pretty powerful: + +- The execution of the request is delegated to a search strategy, which is + executed on the Kibana server side. +- Any plugin can register custom search strategies with custom parameters and + response shapes. +- Search requests can be cancelled via an `AbortSignal`. +- Search requests are decoupled from the transport layer. The service will poll + for new results transparently. +- Partial responses can be returned as they become available if the search + takes longer. + +# Working with `data.search.search()` in the Browser + +The following chapters describe a set of React components and hooks that aim to +make it easy to take advantage of these characteristics from client-side React +code. They implement a producer/consumer pattern that decouples the craeation +of search requests from the consumption of the responses. This keeps each +code-path small and encourages the use of reactive processing, which in turn +reduces the risk of race conditions and incorrect assumptions about the +response timing. + +## Issuing new requests + +The main API to issue new requests is the `data.search.search()` function. It +returns an `Observable` representing the stream of partial and final results +without the consumer having to know the underlying transport mechanisms. +Besides receiving a search-strategy-specific parameter object, it supports +selection of the search strategy as well an `AbortSignal` used for request +cancellation. + +The hook `useDataSearch()` is designed to ease the integration between the +`Observable` world and the React world. It uses the function it is given to +derive the parameters to use for the next search request. The request can then +be issued by calling the returned `search()` function. For each new request the +hook emits an object describing the request and its state in the `requests$` +`Observable`. + +Since the specific response shape depends on the data strategy used, the hook +takes a projection function, that is responsible for decoding the response in +an appropriate way. Because most response projections follow a similar pattern +there's a helper `normalizeDataSearchResponses(initialResponse, +parseRawResponse)`, which generates an RxJS operator, that... + +- emits an initial response containing the given `initialResponse` value +- applies `parseRawResponse` to the `rawResponse` property of each emitted response +- transforms transport layer errors as well as parsing errors into + `SearchStrategyError`s + +```typescript +const parseMyCustomSearchResponse = normalizeDataSearchResponses( + 'initial value', + decodeOrThrow(myCustomSearchResponsePayloadRT) +); + +const { search, requests$ } = useDataSearch({ + getRequest: useCallback((searchTerm: string) => ({ + request: { + params: { + searchTerm + }, + options: { + strategy: 'my-custom-search-strategy', + }, + }, + }), []), + parseResponses: parseMyCustomSearchResponse, +}); +``` + +## Executing requests and consuming the responses + +The response `Observable`s emitted by `data.search.search()` is "cold", so it +won't be executed unless a subscriber subscribes to it. And in order to cleanly +cancel and garbage collect the subscription it should be integrated with the +React component life-cycle. + +The `useLatestPartialDataSearchResponse()` does that in such a way that the +newest response observable is subscribed to and that any previous response +observables are unsubscribed from for proper cancellation if a new request has +been created. This uses RxJS's `switchMap()` operator under the hood. The hook +also makes sure that all observables are unsubscribed from on unmount. + +A request can fail due to various reasons that include servers-side errors, +Elasticsearch shard failures and network failures. The intention is to map all +of them to a common `SearchStrategyError` interface. While the +`useLatestPartialDataSearchResponse()` hook does that for errors emitted +natively by the response `Observable`, it's the responsibility of the +projection function to handle errors that are encoded in the response body, +which includes most server-side errors. Note that errors and partial results in +a response are not mutually exclusive. + +The request status (running, partial, etc), the response +and the errors are turned in to React component state so they can be used in +the usual rendering cycle: + +```typescript +const { + cancelRequest, + isRequestRunning, + isResponsePartial, + latestResponseData, + latestResponseErrors, + loaded, + total, +} = useLatestPartialDataSearchResponse(requests$); +``` + +## Representing the request state to the user + +After the values have been made available to the React rendering process using +the `useLatestPartialDataSearchResponse()` hook, normal component hierarchies +can be used to make the request state and result available to the user. The +following utility components can make that even easier. + +### Undetermined progress + +If `total` and `loaded` are not (yet) known, we can show an undetermined +progress bar. + + + + + +### Known progress + +If `total` and `loaded` are returned by the search strategy, they can be used +to show a progress bar with the option to cancel the request if it takes too +long. + + + + + +### Failed requests + +Assuming the errors are represented as an array of `SearchStrategyError`s in +the `latestResponseErrors` return value, they can be rendered as appropriate +for the respective part of the UI. For many cases a `EuiCallout` is suitable, +so the `DataSearchErrorCallout` can serve as a starting point: + + + + + diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/flatten_data_search_response.ts b/x-pack/plugins/logs_shared/public/utils/data_search/flatten_data_search_response.ts new file mode 100644 index 000000000000..52f08f39bc2d --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/flatten_data_search_response.ts @@ -0,0 +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. + */ + +import { map } from 'rxjs/operators'; +import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; +import { ParsedDataSearchRequestDescriptor } from './types'; + +export const flattenDataSearchResponseDescriptor = < + Request extends IKibanaSearchRequest, + Response +>({ + abortController, + options, + request, + response$, +}: ParsedDataSearchRequestDescriptor) => + response$.pipe( + map((response) => { + return { + abortController, + options, + request, + response, + }; + }) + ); diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/index.ts b/x-pack/plugins/logs_shared/public/utils/data_search/index.ts new file mode 100644 index 000000000000..bd1881a6935e --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './flatten_data_search_response'; +export * from './normalize_data_search_responses'; +export * from './types'; +export * from './use_data_search_request'; +export * from './use_data_search_response_state'; +export * from './use_latest_partial_data_search_response'; diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/normalize_data_search_responses.ts b/x-pack/plugins/logs_shared/public/utils/data_search/normalize_data_search_responses.ts new file mode 100644 index 000000000000..0056b7466759 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/normalize_data_search_responses.ts @@ -0,0 +1,81 @@ +/* + * 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 { Observable, of } from 'rxjs'; +import { catchError, map, startWith } from 'rxjs/operators'; +import { IKibanaSearchResponse } from '@kbn/data-plugin/public'; +import { AbortError } from '@kbn/kibana-utils-plugin/public'; +import { SearchStrategyError } from '../../../common/search_strategies/common/errors'; +import { ParsedKibanaSearchResponse } from './types'; + +export type RawResponseParser = (rawResponse: RawResponse) => { + data: Response; + errors?: SearchStrategyError[]; +}; + +/** + * An operator factory that normalizes each {@link IKibanaSearchResponse} by + * parsing it into a {@link ParsedKibanaSearchResponse} and adding initial + * responses and error handling. + * + * @param initialResponse - The initial value to emit when a new request is + * handled. + * @param projectResponse - The projection function to apply to each response + * payload. It should validate that the response payload is of the type {@link + * RawResponse} and decode it to a {@link Response}. + * + * @return An operator that adds parsing and error handling transformations to + * each response payload using the arguments given above. + */ +export const normalizeDataSearchResponses = + ( + initialResponse: InitialResponse, + parseRawResponse: RawResponseParser + ) => + ( + response$: Observable> + ): Observable> => + response$.pipe( + map((response) => { + const { data, errors = [] } = parseRawResponse(response.rawResponse); + return { + data, + errors, + isPartial: response.isPartial ?? false, + isRunning: response.isRunning ?? false, + loaded: response.loaded, + total: response.total, + }; + }), + startWith({ + data: initialResponse, + errors: [], + isPartial: true, + isRunning: true, + loaded: 0, + total: undefined, + }), + catchError((error) => + of({ + data: initialResponse, + errors: [ + error instanceof AbortError + ? { + type: 'aborted' as const, + } + : { + type: 'generic' as const, + message: `${error.message ?? error}`, + }, + ], + isPartial: true, + isRunning: false, + loaded: 0, + total: undefined, + }) + ) + ); diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/types.ts b/x-pack/plugins/logs_shared/public/utils/data_search/types.ts new file mode 100644 index 000000000000..3e2c6a15487b --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/types.ts @@ -0,0 +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. + */ + +import { Observable } from 'rxjs'; +import { + IKibanaSearchRequest, + IKibanaSearchResponse, + ISearchOptions, +} from '@kbn/data-plugin/public'; +import { SearchStrategyError } from '../../../common/search_strategies/common/errors'; + +export interface DataSearchRequestDescriptor { + request: Request; + options: ISearchOptions; + response$: Observable>; + abortController: AbortController; +} + +export interface ParsedDataSearchRequestDescriptor< + Request extends IKibanaSearchRequest, + ResponseData +> { + request: Request; + options: ISearchOptions; + response$: Observable>; + abortController: AbortController; +} + +export interface ParsedKibanaSearchResponse { + total?: number; + loaded?: number; + isRunning: boolean; + isPartial: boolean; + data: ResponseData; + errors: SearchStrategyError[]; +} + +export interface ParsedDataSearchResponseDescriptor< + Request extends IKibanaSearchRequest, + Response +> { + request: Request; + options: ISearchOptions; + response: ParsedKibanaSearchResponse; + abortController: AbortController; +} diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.test.tsx b/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.test.tsx new file mode 100644 index 000000000000..06f8ca1b82eb --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.test.tsx @@ -0,0 +1,198 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks'; +import React from 'react'; +import { firstValueFrom, Observable, of, Subject } from 'rxjs'; +import { + DataPublicPluginStart, + IKibanaSearchResponse, + ISearchGeneric, + ISearchStart, +} from '@kbn/data-plugin/public'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { PluginKibanaContextValue } from '../../hooks/use_kibana'; +import { normalizeDataSearchResponses } from './normalize_data_search_responses'; +import { useDataSearch } from './use_data_search_request'; + +describe('useDataSearch hook', () => { + it('forwards the search function arguments to the getRequest function', async () => { + const dataMock = createDataPluginMock(); + const { Provider: KibanaContextProvider } = createKibanaReactContext< + Partial + >({ + data: dataMock, + }); + + const getRequest = jest.fn((_firstArgument: string, _secondArgument: string) => null); + + const { result } = renderHook( + () => + useDataSearch({ + getRequest, + parseResponses: noopParseResponse, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + act(() => { + result.current.search('first', 'second'); + }); + + expect(getRequest).toHaveBeenLastCalledWith('first', 'second'); + expect(dataMock.search.search).not.toHaveBeenCalled(); + }); + + it('creates search requests with the given params and options and parses the responses', async () => { + const dataMock = createDataPluginMock(); + const searchResponseMock$ = of({ + rawResponse: { + firstKey: 'firstValue', + }, + }); + dataMock.search.search.mockReturnValue(searchResponseMock$); + const { Provider: KibanaContextProvider } = createKibanaReactContext< + Partial + >({ + data: dataMock, + }); + + const getRequest = jest.fn((firstArgument: string, secondArgument: string) => ({ + request: { + params: { + firstArgument, + secondArgument, + }, + }, + options: { + strategy: 'test-search-strategy', + }, + })); + + const { result } = renderHook( + () => + useDataSearch({ + getRequest, + parseResponses: noopParseResponse, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + // the request execution is lazy + expect(dataMock.search.search).not.toHaveBeenCalled(); + + // execute requests$ observable + const firstRequestPromise = firstValueFrom(result.current.requests$); + + act(() => { + result.current.search('first', 'second'); + }); + + const firstRequest = await firstRequestPromise; + + expect(dataMock.search.search).toHaveBeenLastCalledWith( + { + params: { firstArgument: 'first', secondArgument: 'second' }, + }, + { + abortSignal: expect.any(Object), + strategy: 'test-search-strategy', + } + ); + expect(firstRequest).toHaveProperty('abortController', expect.any(Object)); + expect(firstRequest).toHaveProperty('request.params', { + firstArgument: 'first', + secondArgument: 'second', + }); + expect(firstRequest).toHaveProperty('options.strategy', 'test-search-strategy'); + expect(firstRequest).toHaveProperty('response$', expect.any(Observable)); + await expect(firstRequest.response$.toPromise()).resolves.toMatchObject({ + data: { + firstKey: 'firstValue', // because this specific response parser just copies the raw response + }, + errors: [], + }); + }); + + it('aborts the request when the response observable looses the last subscriber', async () => { + const dataMock = createDataPluginMock(); + const searchResponseMock$ = new Subject(); + dataMock.search.search.mockReturnValue(searchResponseMock$); + const { Provider: KibanaContextProvider } = createKibanaReactContext< + Partial + >({ + data: dataMock, + }); + + const getRequest = jest.fn((firstArgument: string, secondArgument: string) => ({ + request: { + params: { + firstArgument, + secondArgument, + }, + }, + options: { + strategy: 'test-search-strategy', + }, + })); + + const { result } = renderHook( + () => + useDataSearch({ + getRequest, + parseResponses: noopParseResponse, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + // the request execution is lazy + expect(dataMock.search.search).not.toHaveBeenCalled(); + + // execute requests$ observable + const firstRequestPromise = firstValueFrom(result.current.requests$); + + act(() => { + result.current.search('first', 'second'); + }); + + const firstRequest = await firstRequestPromise; + + // execute requests$ observable + const firstResponseSubscription = firstRequest.response$.subscribe({ + next: jest.fn(), + }); + + // get the abort signal + const [, firstRequestOptions] = dataMock.search.search.mock.calls[0]; + + expect(firstRequestOptions?.abortSignal?.aborted).toBe(false); + + // unsubscribe + firstResponseSubscription.unsubscribe(); + + expect(firstRequestOptions?.abortSignal?.aborted).toBe(true); + }); +}); + +const createDataPluginMock = () => { + const dataMock = dataPluginMock.createStartContract() as DataPublicPluginStart & { + search: ISearchStart & { search: jest.MockedFunction }; + }; + return dataMock; +}; + +const noopParseResponse = normalizeDataSearchResponses( + null, + (response: Response) => ({ data: response }) +); diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.ts b/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.ts new file mode 100644 index 000000000000..230c2d87e065 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_request.ts @@ -0,0 +1,104 @@ +/* + * 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 } from 'react'; +import { OperatorFunction, ReplaySubject } from 'rxjs'; +import { share, tap } from 'rxjs/operators'; +import { + IKibanaSearchRequest, + IKibanaSearchResponse, + ISearchOptions, +} from '@kbn/data-plugin/public'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; +import { tapUnsubscribe, useObservable } from '../use_observable'; +import { ParsedDataSearchRequestDescriptor, ParsedKibanaSearchResponse } from './types'; + +export type DataSearchRequestFactory = ( + ...args: Args +) => + | { + request: Request; + options: ISearchOptions; + } + | null + | undefined; + +type ParseResponsesOperator = OperatorFunction< + IKibanaSearchResponse, + ParsedKibanaSearchResponse +>; + +export const useDataSearch = < + RequestFactoryArgs extends any[], + RequestParams, + Request extends IKibanaSearchRequest, + RawResponse, + Response +>({ + getRequest, + parseResponses, +}: { + getRequest: DataSearchRequestFactory; + parseResponses: ParseResponsesOperator; +}) => { + const { services } = useKibanaContextForPlugin(); + const requests$ = useObservable( + () => new ReplaySubject>(1), + [] + ); + + const search = useCallback( + (...args: RequestFactoryArgs) => { + const requestArgs = getRequest(...args); + + if (requestArgs == null) { + return; + } + + const abortController = new AbortController(); + let isAbortable = true; + + const newRequestDescriptor = { + ...requestArgs, + abortController, + response$: services.data.search + .search>(requestArgs.request, { + abortSignal: abortController.signal, + ...requestArgs.options, + }) + .pipe( + // avoid aborting failed or completed requests + tap({ + error: () => { + isAbortable = false; + }, + complete: () => { + isAbortable = false; + }, + }), + tapUnsubscribe(() => { + if (isAbortable) { + abortController.abort(); + } + }), + parseResponses, + share() + ), + }; + + requests$.next(newRequestDescriptor); + + return newRequestDescriptor; + }, + [getRequest, services.data.search, parseResponses, requests$] + ); + + return { + requests$, + search, + }; +}; diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_response_state.ts b/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_response_state.ts new file mode 100644 index 000000000000..c8ec672beb84 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/use_data_search_response_state.ts @@ -0,0 +1,36 @@ +/* + * 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 } from 'react'; +import { Observable } from 'rxjs'; +import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; +import { useObservableState } from '../use_observable'; +import { ParsedDataSearchResponseDescriptor } from './types'; + +export const useDataSearchResponseState = < + Request extends IKibanaSearchRequest, + Response, + InitialResponse +>( + response$: Observable> +) => { + const { latestValue } = useObservableState(response$, undefined); + + const cancelRequest = useCallback(() => { + latestValue?.abortController.abort(); + }, [latestValue]); + + return { + cancelRequest, + isRequestRunning: latestValue?.response.isRunning ?? false, + isResponsePartial: latestValue?.response.isPartial ?? false, + latestResponseData: latestValue?.response.data, + latestResponseErrors: latestValue?.response.errors, + loaded: latestValue?.response.loaded, + total: latestValue?.response.total, + }; +}; diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.test.tsx b/x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.test.tsx new file mode 100644 index 000000000000..a48cf2c7ccdc --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.test.tsx @@ -0,0 +1,121 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks'; +import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; +import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; +import { ParsedDataSearchRequestDescriptor, ParsedKibanaSearchResponse } from './types'; +import { useLatestPartialDataSearchResponse } from './use_latest_partial_data_search_response'; + +describe('useLatestPartialDataSearchResponse hook', () => { + it("subscribes to the latest request's response observable", () => { + const firstRequest = { + abortController: new AbortController(), + options: {}, + request: { params: 'firstRequestParam' }, + response$: new BehaviorSubject>({ + data: 'initial', + isRunning: true, + isPartial: true, + errors: [], + }), + }; + + const secondRequest = { + abortController: new AbortController(), + options: {}, + request: { params: 'secondRequestParam' }, + response$: new BehaviorSubject>({ + data: 'initial', + isRunning: true, + isPartial: true, + errors: [], + }), + }; + + const requests$ = new Subject< + ParsedDataSearchRequestDescriptor, string> + >(); + + const { result } = renderHook(() => useLatestPartialDataSearchResponse(requests$)); + + expect(result).toHaveProperty('current.isRequestRunning', false); + expect(result).toHaveProperty('current.latestResponseData', undefined); + + // first request is started + act(() => { + requests$.next(firstRequest); + }); + + expect(result).toHaveProperty('current.isRequestRunning', true); + expect(result).toHaveProperty('current.latestResponseData', 'initial'); + + // first response of the first request arrives + act(() => { + firstRequest.response$.next({ + data: 'request-1-response-1', + isRunning: true, + isPartial: true, + errors: [], + }); + }); + + expect(result).toHaveProperty('current.isRequestRunning', true); + expect(result).toHaveProperty('current.latestResponseData', 'request-1-response-1'); + + // second request is started before the second response of the first request arrives + act(() => { + requests$.next(secondRequest); + secondRequest.response$.next({ + data: 'request-2-response-1', + isRunning: true, + isPartial: true, + errors: [], + }); + }); + + expect(result).toHaveProperty('current.isRequestRunning', true); + expect(result).toHaveProperty('current.latestResponseData', 'request-2-response-1'); + + // second response of the second request arrives + act(() => { + secondRequest.response$.next({ + data: 'request-2-response-2', + isRunning: false, + isPartial: false, + errors: [], + }); + }); + + expect(result).toHaveProperty('current.isRequestRunning', false); + expect(result).toHaveProperty('current.latestResponseData', 'request-2-response-2'); + }); + + it("unsubscribes from the latest request's response observable on unmount", () => { + const onUnsubscribe = jest.fn(); + + const firstRequest = { + abortController: new AbortController(), + options: {}, + request: { params: 'firstRequestParam' }, + response$: new Observable>(() => { + return onUnsubscribe; + }), + }; + + const requests$ = + of, string>>(firstRequest); + + const { unmount } = renderHook(() => useLatestPartialDataSearchResponse(requests$)); + + expect(onUnsubscribe).not.toHaveBeenCalled(); + + unmount(); + + expect(onUnsubscribe).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.ts b/x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.ts new file mode 100644 index 000000000000..48ff61eb676b --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/data_search/use_latest_partial_data_search_response.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; +import { useOperator } from '../use_observable'; +import { flattenDataSearchResponseDescriptor } from './flatten_data_search_response'; +import { ParsedDataSearchRequestDescriptor, ParsedDataSearchResponseDescriptor } from './types'; +import { useDataSearchResponseState } from './use_data_search_response_state'; + +export const useLatestPartialDataSearchResponse = ( + requests$: Observable> +) => { + const latestResponse$: Observable> = + useOperator(requests$, flattenLatestDataSearchResponse); + + const { + cancelRequest, + isRequestRunning, + isResponsePartial, + latestResponseData, + latestResponseErrors, + loaded, + total, + } = useDataSearchResponseState(latestResponse$); + + return { + cancelRequest, + isRequestRunning, + isResponsePartial, + latestResponseData, + latestResponseErrors, + loaded, + total, + }; +}; + +const flattenLatestDataSearchResponse = switchMap(flattenDataSearchResponseDescriptor); diff --git a/x-pack/plugins/logs_shared/public/utils/datemath.ts b/x-pack/plugins/logs_shared/public/utils/datemath.ts new file mode 100644 index 000000000000..ab07ace52d05 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/datemath.ts @@ -0,0 +1,293 @@ +/* + * 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 dateMath, { Unit } from '@kbn/datemath'; + +const JS_MAX_DATE = 8640000000000000; + +export function isValidDatemath(value: string): boolean { + const parsedValue = dateMath.parse(value); + return !!(parsedValue && parsedValue.isValid()); +} + +export function datemathToEpochMillis( + value: string, + round: 'down' | 'up' = 'down', + forceNow?: Date +): number | null { + const parsedValue = dateMath.parse(value, { roundUp: round === 'up', forceNow }); + if (!parsedValue || !parsedValue.isValid()) { + return null; + } + return parsedValue.valueOf(); +} + +type DatemathExtension = + | { + value: string; + diffUnit: Unit; + diffAmount: number; + } + | { value: 'now' }; + +const datemathNowExpression = /(\+|\-)(\d+)(ms|s|m|h|d|w|M|y)$/; + +/** + * Extend a datemath value + * @param value The value to extend + * @param {'before' | 'after'} direction Should the value move before or after in time + * @param oppositeEdge For absolute values, the value of the other edge of the range + */ +export function extendDatemath( + value: string, + direction: 'before' | 'after' = 'before', + oppositeEdge?: string +): DatemathExtension | undefined { + if (!isValidDatemath(value)) { + return undefined; + } + + // `now` cannot be extended + if (value === 'now') { + return { value: 'now' }; + } + + // The unit is relative + if (value.startsWith('now')) { + return extendRelativeDatemath(value, direction); + } else if (oppositeEdge && isValidDatemath(oppositeEdge)) { + return extendAbsoluteDatemath(value, direction, oppositeEdge); + } + + return undefined; +} + +function extendRelativeDatemath( + value: string, + direction: 'before' | 'after' +): DatemathExtension | undefined { + const [, operator, amount, unit] = datemathNowExpression.exec(value) || []; + if (!operator || !amount || !unit) { + return undefined; + } + + const mustIncreaseAmount = + (operator === '-' && direction === 'before') || (operator === '+' && direction === 'after'); + const parsedAmount = parseInt(amount, 10); + let newUnit: Unit = unit as Unit; + let newAmount: number; + + // Extend the amount + switch (unit) { + // For small units, always double or halve the amount + case 'ms': + case 's': + newAmount = mustIncreaseAmount ? parsedAmount * 2 : Math.floor(parsedAmount / 2); + break; + // For minutes, increase or decrease in doubles or halves, depending on + // the amount of minutes + case 'm': + let ratio; + const MINUTES_LARGE = 10; + if (mustIncreaseAmount) { + ratio = parsedAmount >= MINUTES_LARGE ? 0.5 : 1; + newAmount = parsedAmount + Math.floor(parsedAmount * ratio); + } else { + newAmount = + parsedAmount >= MINUTES_LARGE + ? Math.floor(parsedAmount / 1.5) + : parsedAmount - Math.floor(parsedAmount * 0.5); + } + break; + + // For hours, increase or decrease half an hour for 1 hour. Otherwise + // increase full hours + case 'h': + if (parsedAmount === 1) { + newAmount = mustIncreaseAmount ? 90 : 30; + newUnit = 'm'; + } else { + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + // For the rest of units, increase or decrease one smaller unit for + // amounts of 1. Otherwise increase or decrease the unit + case 'd': + case 'w': + case 'M': + case 'y': + if (parsedAmount === 1) { + newUnit = dateMath.unitsDesc[dateMath.unitsDesc.indexOf(unit) + 1]; + newAmount = mustIncreaseAmount + ? convertDate(1, unit, newUnit) + 1 + : convertDate(1, unit, newUnit) - 1; + } else { + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + default: + throw new TypeError('Unhandled datemath unit'); + } + + // normalize amount and unit (i.e. 120s -> 2m) + const { unit: normalizedUnit, amount: normalizedAmount } = normalizeDate(newAmount, newUnit); + + // How much have we changed the time? + const diffAmount = Math.abs(normalizedAmount - convertDate(parsedAmount, unit, normalizedUnit)); + // if `diffAmount` is not an integer after normalization, express the difference in the original unit + const shouldKeepDiffUnit = diffAmount % 1 !== 0; + + const nextValue = `now${operator}${normalizedAmount}${normalizedUnit}`; + + if (isDateInRange(nextValue)) { + return { + value: nextValue, + diffUnit: shouldKeepDiffUnit ? unit : newUnit, + diffAmount: shouldKeepDiffUnit ? Math.abs(newAmount - parsedAmount) : diffAmount, + }; + } else { + return undefined; + } +} + +function extendAbsoluteDatemath( + value: string, + direction: 'before' | 'after', + oppositeEdge: string +): DatemathExtension | undefined { + const valueTimestamp = datemathToEpochMillis(value)!; + const oppositeEdgeTimestamp = datemathToEpochMillis(oppositeEdge)!; + const actualTimestampDiff = Math.abs(valueTimestamp - oppositeEdgeTimestamp); + const normalizedDiff = normalizeDate(actualTimestampDiff, 'ms'); + const normalizedTimestampDiff = convertDate(normalizedDiff.amount, normalizedDiff.unit, 'ms'); + + const newValue = + direction === 'before' + ? valueTimestamp - normalizedTimestampDiff + : valueTimestamp + normalizedTimestampDiff; + + if (isDateInRange(newValue)) { + return { + value: new Date(newValue).toISOString(), + diffUnit: normalizedDiff.unit, + diffAmount: normalizedDiff.amount, + }; + } else { + return undefined; + } +} + +const CONVERSION_RATIOS: Record> = { + wy: [ + ['w', 52], // 1 year = 52 weeks + ['y', 1], + ], + w: [ + ['ms', 1000], + ['s', 60], + ['m', 60], + ['h', 24], + ['d', 7], // 1 week = 7 days + ['w', 4], // 1 month = 4 weeks = 28 days + ['M', 12], // 1 year = 12 months = 52 weeks = 364 days + ['y', 1], + ], + M: [ + ['ms', 1000], + ['s', 60], + ['m', 60], + ['h', 24], + ['d', 30], // 1 month = 30 days + ['M', 12], // 1 year = 12 months = 360 days + ['y', 1], + ], + default: [ + ['ms', 1000], + ['s', 60], + ['m', 60], + ['h', 24], + ['d', 365], // 1 year = 365 days + ['y', 1], + ], +}; + +function getRatioScale(from: Unit, to?: Unit) { + if ((from === 'y' && to === 'w') || (from === 'w' && to === 'y')) { + return CONVERSION_RATIOS.wy; + } else if (from === 'w' || to === 'w') { + return CONVERSION_RATIOS.w; + } else if (from === 'M' || to === 'M') { + return CONVERSION_RATIOS.M; + } else { + return CONVERSION_RATIOS.default; + } +} + +export function convertDate(value: number, from: Unit, to: Unit): number { + if (from === to) { + return value; + } + + const ratioScale = getRatioScale(from, to); + const fromIdx = ratioScale.findIndex((ratio) => ratio[0] === from); + const toIdx = ratioScale.findIndex((ratio) => ratio[0] === to); + + let convertedValue = value; + + if (fromIdx > toIdx) { + // `from` is the bigger unit. Multiply the value + for (let i = toIdx; i < fromIdx; i++) { + convertedValue *= ratioScale[i][1]; + } + } else { + // `from` is the smaller unit. Divide the value + for (let i = fromIdx; i < toIdx; i++) { + convertedValue /= ratioScale[i][1]; + } + } + + return convertedValue; +} + +export function normalizeDate(amount: number, unit: Unit): { amount: number; unit: Unit } { + // There is nothing after years + if (unit === 'y') { + return { amount, unit }; + } + + const nextUnit = dateMath.unitsAsc[dateMath.unitsAsc.indexOf(unit) + 1]; + const ratioScale = getRatioScale(unit, nextUnit); + const ratio = ratioScale.find((r) => r[0] === unit)![1]; + + const newAmount = amount / ratio; + + // Exact conversion + if (newAmount === 1) { + return { amount: newAmount, unit: nextUnit }; + } + + // Might be able to go one unit more, so try again, rounding the value + // 7200s => 120m => 2h + // 7249s ~> 120m ~> 2h + if (newAmount >= 2) { + return normalizeDate(Math.round(newAmount), nextUnit); + } + + // Cannot go one one unit above. Return as it is + return { amount, unit }; +} + +function isDateInRange(date: string | number): boolean { + try { + const epoch = typeof date === 'string' ? datemathToEpochMillis(date) ?? -1 : date; + return epoch >= 0 && epoch <= JS_MAX_DATE; + } catch { + return false; + } +} diff --git a/x-pack/plugins/logs_shared/public/utils/dev_mode.ts b/x-pack/plugins/logs_shared/public/utils/dev_mode.ts new file mode 100644 index 000000000000..60571501b419 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/dev_mode.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const getReduxDevtools = () => (window as any).__REDUX_DEVTOOLS_EXTENSION__; + +export const hasReduxDevtools = () => getReduxDevtools() != null; + +export const isDevMode = () => process.env.NODE_ENV !== 'production'; diff --git a/x-pack/plugins/logs_shared/public/utils/handlers.ts b/x-pack/plugins/logs_shared/public/utils/handlers.ts new file mode 100644 index 000000000000..f79e2fa4766a --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/handlers.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEqual } from 'lodash'; + +export function callWithoutRepeats( + func: (...args: any[]) => T, + isArgsEqual: (firstArgs: any, secondArgs: any) => boolean = isEqual +) { + let previousArgs: any[]; + let previousResult: T; + + return (...args: any[]) => { + if (!isArgsEqual(args, previousArgs)) { + previousArgs = args; + previousResult = func(...args); + } + + return previousResult; + }; +} diff --git a/x-pack/plugins/logs_shared/public/utils/log_column_render_configuration.tsx b/x-pack/plugins/logs_shared/public/utils/log_column_render_configuration.tsx new file mode 100644 index 000000000000..ff4a24f1498a --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/log_column_render_configuration.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ReactNode } from 'react'; +import { JsonValue } from '@kbn/utility-types'; + +/** + * Interface for common configuration properties, regardless of the column type. + */ +interface CommonRenderConfiguration { + id: string; + width?: number | string; + header?: boolean | string; +} + +interface TimestampColumnRenderConfiguration { + timestampColumn: CommonRenderConfiguration & { + render?: (timestamp: number) => ReactNode; + }; +} + +interface MessageColumnRenderConfiguration { + messageColumn: CommonRenderConfiguration & { + render?: (message: string) => ReactNode; + }; +} + +interface FieldColumnRenderConfiguration { + fieldColumn: CommonRenderConfiguration & { + field: string; + render?: (value: JsonValue) => ReactNode; + }; +} + +export type LogColumnRenderConfiguration = + | TimestampColumnRenderConfiguration + | MessageColumnRenderConfiguration + | FieldColumnRenderConfiguration; + +export function isTimestampColumnRenderConfiguration( + column: LogColumnRenderConfiguration +): column is TimestampColumnRenderConfiguration { + return 'timestampColumn' in column; +} + +export function isMessageColumnRenderConfiguration( + column: LogColumnRenderConfiguration +): column is MessageColumnRenderConfiguration { + return 'messageColumn' in column; +} + +export function isFieldColumnRenderConfiguration( + column: LogColumnRenderConfiguration +): column is FieldColumnRenderConfiguration { + return 'fieldColumn' in column; +} + +export function columnWidthToCSS(width: number | string) { + return typeof width === 'number' ? `${width}px` : width; +} diff --git a/x-pack/plugins/infra/public/utils/log_entry/index.ts b/x-pack/plugins/logs_shared/public/utils/log_entry/index.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/log_entry/index.ts rename to x-pack/plugins/logs_shared/public/utils/log_entry/index.ts diff --git a/x-pack/plugins/infra/public/utils/log_entry/log_entry.ts b/x-pack/plugins/logs_shared/public/utils/log_entry/log_entry.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/log_entry/log_entry.ts rename to x-pack/plugins/logs_shared/public/utils/log_entry/log_entry.ts diff --git a/x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts b/x-pack/plugins/logs_shared/public/utils/log_entry/log_entry_highlight.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts rename to x-pack/plugins/logs_shared/public/utils/log_entry/log_entry_highlight.ts diff --git a/x-pack/plugins/infra/public/utils/styles.ts b/x-pack/plugins/logs_shared/public/utils/styles.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/styles.ts rename to x-pack/plugins/logs_shared/public/utils/styles.ts diff --git a/x-pack/plugins/infra/public/components/log_stream/lazy_log_stream_wrapper.tsx b/x-pack/plugins/logs_shared/public/utils/typed_react.tsx similarity index 50% rename from x-pack/plugins/infra/public/components/log_stream/lazy_log_stream_wrapper.tsx rename to x-pack/plugins/logs_shared/public/utils/typed_react.tsx index 064e944378ca..664894e1bf05 100644 --- a/x-pack/plugins/infra/public/components/log_stream/lazy_log_stream_wrapper.tsx +++ b/x-pack/plugins/logs_shared/public/utils/typed_react.tsx @@ -6,12 +6,6 @@ */ import React from 'react'; -import type { LogStreamProps } from './log_stream'; -const LazyLogStream = React.lazy(() => import('./log_stream')); - -export const LazyLogStreamWrapper = (props: LogStreamProps) => ( - }> - - -); +export type RendererResult = React.ReactElement | null; +export type RendererFunction = (args: RenderArgs) => Result; diff --git a/x-pack/plugins/logs_shared/public/utils/use_kibana_query_settings.ts b/x-pack/plugins/logs_shared/public/utils/use_kibana_query_settings.ts new file mode 100644 index 000000000000..521cd0142303 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/use_kibana_query_settings.ts @@ -0,0 +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. + */ + +import type { EsQueryConfig } from '@kbn/es-query'; +import { SerializableRecord } from '@kbn/utility-types'; +import { useMemo } from 'react'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; + +export const useKibanaQuerySettings = (): EsQueryConfig => { + const [allowLeadingWildcards] = useUiSetting$(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS); + const [queryStringOptions] = useUiSetting$(UI_SETTINGS.QUERY_STRING_OPTIONS); + const [dateFormatTZ] = useUiSetting$(UI_SETTINGS.DATEFORMAT_TZ); + const [ignoreFilterIfFieldNotInIndex] = useUiSetting$( + UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX + ); + + return useMemo( + () => ({ + allowLeadingWildcards, + queryStringOptions, + dateFormatTZ, + ignoreFilterIfFieldNotInIndex, + }), + [allowLeadingWildcards, dateFormatTZ, ignoreFilterIfFieldNotInIndex, queryStringOptions] + ); +}; diff --git a/x-pack/plugins/logs_shared/public/utils/use_kibana_ui_setting.ts b/x-pack/plugins/logs_shared/public/utils/use_kibana_ui_setting.ts new file mode 100644 index 000000000000..e53e2b28cbdd --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/use_kibana_ui_setting.ts @@ -0,0 +1,53 @@ +/* + * 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 { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; + +/** + * This hook behaves like a `useState` hook in that it provides a requested + * setting value (with an optional default) from the Kibana UI settings (also + * known as "advanced settings") and a setter to change that setting: + * + * ``` + * const [quickRanges, setQuickRanges] = useKibanaUiSetting('timepicker:quickRanges'); + * ``` + * + * This is not just a static consumption of the value, but will reactively + * update when the underlying setting subscription of the `UiSettingsClient` + * notifies of a change. + * + * Unlike the `useState`, it doesn't give type guarantees for the value, + * because the underlying `UiSettingsClient` doesn't support that. + */ + +export interface TimePickerQuickRange { + from: string; + to: string; + display: string; +} + +export function useKibanaUiSetting( + key: 'timepicker:quickRanges', + defaultValue?: TimePickerQuickRange[] +): [ + TimePickerQuickRange[], + (key: 'timepicker:quickRanges', value: TimePickerQuickRange[]) => Promise +]; + +export function useKibanaUiSetting( + key: string, + defaultValue?: any +): [any, (key: string, value: any) => Promise]; + +export function useKibanaUiSetting(key: string, defaultValue?: any) { + const [uiSetting, setUiSetting] = useUiSetting$(key); + const uiSettingValue = useMemo(() => { + return uiSetting ? uiSetting : defaultValue; + }, [uiSetting, defaultValue]); + return [uiSettingValue, setUiSetting]; +} diff --git a/x-pack/plugins/logs_shared/public/utils/use_observable.ts b/x-pack/plugins/logs_shared/public/utils/use_observable.ts new file mode 100644 index 000000000000..00efc4900b82 --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/use_observable.ts @@ -0,0 +1,152 @@ +/* + * 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 { useEffect, useRef, useState } from 'react'; +import { + BehaviorSubject, + Observable, + OperatorFunction, + PartialObserver, + ReplaySubject, + Subscription, +} from 'rxjs'; +import { switchMap } from 'rxjs/operators'; + +export const useLatest = (value: Value) => { + const valueRef = useRef(value); + valueRef.current = value; + return valueRef; +}; + +export const useObservable = < + OutputValue, + OutputObservable extends Observable, + InputValues extends Readonly +>( + createObservableOnce: (inputValues: Observable) => OutputObservable, + inputValues: InputValues +) => { + const [output$, next] = useBehaviorSubject(createObservableOnce, () => inputValues); + + useEffect(() => { + next(inputValues); + // `inputValues` can't be statically analyzed + // eslint-disable-next-line react-hooks/exhaustive-deps + }, inputValues); + + return output$; +}; + +export const useBehaviorSubject = < + InputValue, + OutputValue, + OutputObservable extends Observable +>( + deriveObservableOnce: (input$: Observable) => OutputObservable, + createInitialValue: () => InputValue +) => { + const [[subject$, next], _] = useState(() => { + const newSubject$ = new BehaviorSubject(createInitialValue()); + const newNext = newSubject$.next.bind(newSubject$); + return [newSubject$, newNext] as const; + }); + const [output$] = useState(() => deriveObservableOnce(subject$)); + return [output$, next] as const; +}; + +export const useReplaySubject = < + InputValue, + OutputValue, + OutputObservable extends Observable +>( + deriveObservableOnce: (input$: Observable) => OutputObservable +) => { + const [[subject$, next], _] = useState(() => { + const newSubject$ = new ReplaySubject(); + const newNext = newSubject$.next.bind(newSubject$); + return [newSubject$, newNext] as const; + }); + const [output$] = useState(() => deriveObservableOnce(subject$)); + return [output$, next] as const; +}; + +export const useObservableState = ( + state$: Observable, + initialState: InitialState | (() => InitialState) +) => { + const [latestValue, setLatestValue] = useState(initialState); + const [latestError, setLatestError] = useState(); + + useSubscription(state$, { + next: setLatestValue, + error: setLatestError, + }); + + return { latestValue, latestError }; +}; + +export const useSubscription = ( + input$: Observable, + { next, error, complete, unsubscribe }: PartialObserver & { unsubscribe?: () => void } +) => { + const latestSubscription = useRef(); + const latestNext = useLatest(next); + const latestError = useLatest(error); + const latestComplete = useLatest(complete); + const latestUnsubscribe = useLatest(unsubscribe); + + useEffect(() => { + const fixedUnsubscribe = latestUnsubscribe.current; + + const subscription = input$.subscribe({ + next: (value) => { + return latestNext.current?.(value); + }, + error: (value) => latestError.current?.(value), + complete: () => latestComplete.current?.(), + }); + + latestSubscription.current = subscription; + + return () => { + subscription.unsubscribe(); + fixedUnsubscribe?.(); + }; + }, [input$, latestNext, latestError, latestComplete, latestUnsubscribe]); + + return latestSubscription.current; +}; + +export const useOperator = ( + input$: Observable, + operator: OperatorFunction +) => { + const latestOperator = useLatest(operator); + + return useObservable( + (inputs$) => + inputs$.pipe(switchMap(([currentInput$]) => latestOperator.current(currentInput$))), + [input$] as const + ); +}; + +export const tapUnsubscribe = + (onUnsubscribe: () => void) => + (source$: Observable) => { + return new Observable((subscriber) => { + const subscription = source$.subscribe({ + next: (value) => subscriber.next(value), + error: (error) => subscriber.error(error), + complete: () => subscriber.complete(), + }); + + return () => { + onUnsubscribe(); + subscription.unsubscribe(); + }; + }); + }; diff --git a/x-pack/plugins/logs_shared/public/utils/use_tracked_promise.ts b/x-pack/plugins/logs_shared/public/utils/use_tracked_promise.ts new file mode 100644 index 000000000000..d12749ea69fd --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/use_tracked_promise.ts @@ -0,0 +1,299 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +import { DependencyList, useEffect, useMemo, useRef, useState, useCallback } from 'react'; +import useMountedState from 'react-use/lib/useMountedState'; + +interface UseTrackedPromiseArgs { + createPromise: (...args: Arguments) => Promise; + onResolve?: (result: Result) => void; + onReject?: (value: unknown) => void; + cancelPreviousOn?: 'creation' | 'settlement' | 'resolution' | 'rejection' | 'never'; + triggerOrThrow?: 'always' | 'whenMounted'; +} + +/** + * This hook manages a Promise factory and can create new Promises from it. The + * state of these Promises is tracked and they can be canceled when superseded + * to avoid race conditions. + * + * ``` + * const [requestState, performRequest] = useTrackedPromise( + * { + * cancelPreviousOn: 'resolution', + * createPromise: async (url: string) => { + * return await fetchSomething(url) + * }, + * onResolve: response => { + * setSomeState(response.data); + * }, + * onReject: response => { + * setSomeError(response); + * }, + * }, + * [fetchSomething] + * ); + * ``` + * + * The `onResolve` and `onReject` handlers are registered separately, because + * the hook will inject a rejection when in case of a canellation. The + * `cancelPreviousOn` attribute can be used to indicate when the preceding + * pending promises should be canceled: + * + * 'never': No preceding promises will be canceled. + * + * 'creation': Any preceding promises will be canceled as soon as a new one is + * created. + * + * 'settlement': Any preceding promise will be canceled when a newer promise is + * resolved or rejected. + * + * 'resolution': Any preceding promise will be canceled when a newer promise is + * resolved. + * + * 'rejection': Any preceding promise will be canceled when a newer promise is + * rejected. + * + * Any pending promises will be canceled when the component using the hook is + * unmounted, but their status will not be tracked to avoid React warnings + * about memory leaks. + * + * The last argument is a normal React hook dependency list that indicates + * under which conditions a new reference to the configuration object should be + * used. + * + * The `onResolve`, `onReject` and possible uncatched errors are only triggered + * if the underlying component is mounted. To ensure they always trigger (i.e. + * if the promise is called in a `useLayoutEffect`) use the `triggerOrThrow` + * attribute: + * + * 'whenMounted': (default) they are called only if the component is mounted. + * + * 'always': they always call. The consumer is then responsible of ensuring no + * side effects happen if the underlying component is not mounted. + */ +export const useTrackedPromise = ( + { + createPromise, + onResolve = noOp, + onReject = noOp, + cancelPreviousOn = 'never', + triggerOrThrow = 'whenMounted', + }: UseTrackedPromiseArgs, + dependencies: DependencyList +) => { + const isComponentMounted = useMountedState(); + const shouldTriggerOrThrow = useCallback(() => { + switch (triggerOrThrow) { + case 'always': + return true; + case 'whenMounted': + return isComponentMounted(); + } + }, [isComponentMounted, triggerOrThrow]); + + /** + * If a promise is currently pending, this holds a reference to it and its + * cancellation function. + */ + const pendingPromises = useRef>>([]); + + /** + * The state of the promise most recently created by the `createPromise` + * factory. It could be uninitialized, pending, resolved or rejected. + */ + const [promiseState, setPromiseState] = useState>({ + state: 'uninitialized', + }); + + const reset = useCallback(() => { + setPromiseState({ + state: 'uninitialized', + }); + }, []); + + const execute = useMemo( + () => + (...args: Arguments) => { + let rejectCancellationPromise!: (value: any) => void; + const cancellationPromise = new Promise((_, reject) => { + rejectCancellationPromise = reject; + }); + + // remember the list of prior pending promises for cancellation + const previousPendingPromises = pendingPromises.current; + + const cancelPreviousPendingPromises = () => { + previousPendingPromises.forEach((promise) => promise.cancel()); + }; + + const newPromise = createPromise(...args); + const newCancelablePromise = Promise.race([newPromise, cancellationPromise]); + + // track this new state + setPromiseState({ + state: 'pending', + promise: newCancelablePromise, + }); + + if (cancelPreviousOn === 'creation') { + cancelPreviousPendingPromises(); + } + + const newPendingPromise: CancelablePromise = { + cancel: () => { + rejectCancellationPromise(new CanceledPromiseError()); + }, + cancelSilently: () => { + rejectCancellationPromise(new SilentCanceledPromiseError()); + }, + promise: newCancelablePromise.then( + (value) => { + if (['settlement', 'resolution'].includes(cancelPreviousOn)) { + cancelPreviousPendingPromises(); + } + + // remove itself from the list of pending promises + pendingPromises.current = pendingPromises.current.filter( + (pendingPromise) => pendingPromise.promise !== newPendingPromise.promise + ); + + if (onResolve && shouldTriggerOrThrow()) { + onResolve(value); + } + + setPromiseState((previousPromiseState) => + previousPromiseState.state === 'pending' && + previousPromiseState.promise === newCancelablePromise + ? { + state: 'resolved', + promise: newPendingPromise.promise, + value, + } + : previousPromiseState + ); + + return value; + }, + (value) => { + if (!(value instanceof SilentCanceledPromiseError)) { + if (['settlement', 'rejection'].includes(cancelPreviousOn)) { + cancelPreviousPendingPromises(); + } + + // remove itself from the list of pending promises + pendingPromises.current = pendingPromises.current.filter( + (pendingPromise) => pendingPromise.promise !== newPendingPromise.promise + ); + + if (shouldTriggerOrThrow()) { + if (onReject) { + onReject(value); + } else { + throw value; + } + } + + setPromiseState((previousPromiseState) => + previousPromiseState.state === 'pending' && + previousPromiseState.promise === newCancelablePromise + ? { + state: 'rejected', + promise: newCancelablePromise, + value, + } + : previousPromiseState + ); + } + } + ), + }; + + // add the new promise to the list of pending promises + pendingPromises.current = [...pendingPromises.current, newPendingPromise]; + + // silence "unhandled rejection" warnings + newPendingPromise.promise.catch(noOp); + + return newPendingPromise.promise; + }, + // the dependencies are managed by the caller + // eslint-disable-next-line react-hooks/exhaustive-deps + dependencies + ); + + /** + * Cancel any pending promises silently to avoid memory leaks and race + * conditions. + */ + useEffect( + () => () => { + pendingPromises.current.forEach((promise) => promise.cancelSilently()); + }, + [] + ); + + return [promiseState, execute, reset] as [typeof promiseState, typeof execute, typeof reset]; +}; + +export interface UninitializedPromiseState { + state: 'uninitialized'; +} + +export interface PendingPromiseState { + state: 'pending'; + promise: Promise; +} + +export interface ResolvedPromiseState { + state: 'resolved'; + promise: Promise; + value: ResolvedValue; +} + +export interface RejectedPromiseState { + state: 'rejected'; + promise: Promise; + value: RejectedValue; +} + +export type SettledPromiseState = + | ResolvedPromiseState + | RejectedPromiseState; + +export type PromiseState = + | UninitializedPromiseState + | PendingPromiseState + | SettledPromiseState; + +export const isRejectedPromiseState = ( + promiseState: PromiseState +): promiseState is RejectedPromiseState => promiseState.state === 'rejected'; + +interface CancelablePromise { + // reject the promise prematurely with a CanceledPromiseError + cancel: () => void; + // reject the promise prematurely with a SilentCanceledPromiseError + cancelSilently: () => void; + // the tracked promise + promise: Promise; +} + +export class CanceledPromiseError extends Error { + public isCanceled = true; + + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class SilentCanceledPromiseError extends CanceledPromiseError {} + +const noOp = () => undefined; diff --git a/x-pack/plugins/logs_shared/public/utils/use_visibility_state.ts b/x-pack/plugins/logs_shared/public/utils/use_visibility_state.ts new file mode 100644 index 000000000000..cff9a7190d2b --- /dev/null +++ b/x-pack/plugins/logs_shared/public/utils/use_visibility_state.ts @@ -0,0 +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. + */ + +import { useCallback, useMemo, useState } from 'react'; + +export const useVisibilityState = (initialState: boolean) => { + const [isVisible, setIsVisible] = useState(initialState); + + const hide = useCallback(() => setIsVisible(false), []); + const show = useCallback(() => setIsVisible(true), []); + const toggle = useCallback(() => setIsVisible((state) => !state), []); + + return useMemo( + () => ({ + hide, + isVisible, + show, + toggle, + }), + [hide, isVisible, show, toggle] + ); +}; diff --git a/x-pack/plugins/logs_shared/server/index.ts b/x-pack/plugins/logs_shared/server/index.ts new file mode 100644 index 000000000000..95f6741bb54b --- /dev/null +++ b/x-pack/plugins/logs_shared/server/index.ts @@ -0,0 +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. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; +import { LogsSharedPlugin } from './plugin'; + +export type { LogsSharedPluginSetup, LogsSharedPluginStart } from './types'; +export type { + LogsSharedLogEntriesDomain, + ILogsSharedLogEntriesDomain, +} from './lib/domains/log_entries_domain'; + +export { logViewSavedObjectName } from './saved_objects'; + +export function plugin(context: PluginInitializerContext) { + return new LogsSharedPlugin(context); +} diff --git a/x-pack/plugins/logs_shared/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/logs_shared/server/lib/adapters/framework/adapter_types.ts new file mode 100644 index 000000000000..46e77f48d3f4 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/lib/adapters/framework/adapter_types.ts @@ -0,0 +1,96 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { JsonArray, JsonValue } from '@kbn/utility-types'; +import { RouteMethod } from '@kbn/core/server'; +import { VersionedRouteConfig } from '@kbn/core-http-server'; + +export interface CallWithRequestParams extends estypes.RequestBase { + max_concurrent_shard_requests?: number; + name?: string; + index?: string | string[]; + ignore_unavailable?: boolean; + allow_no_indices?: boolean; + size?: number; + terminate_after?: number; + fields?: estypes.Fields; + path?: string; + query?: string | object; + track_total_hits?: boolean | number; + body?: any; +} + +export interface LogsSharedDatabaseResponse { + took: number; + timeout: boolean; +} + +export interface LogsSharedDatabaseSearchResponse + extends LogsSharedDatabaseResponse { + _shards: { + total: number; + successful: number; + skipped: number; + failed: number; + }; + timed_out: boolean; + aggregations?: Aggregations; + hits: { + total: { + value: number; + relation: string; + }; + hits: Hit[]; + }; +} + +export interface LogsSharedDatabaseMultiResponse + extends LogsSharedDatabaseResponse { + responses: Array>; +} + +export interface LogsSharedDatabaseGetIndicesAliasResponse { + [indexName: string]: { + aliases: { + [aliasName: string]: any; + }; + }; +} + +export interface LogsSharedDatabaseGetIndicesResponse { + [indexName: string]: { + aliases: { + [aliasName: string]: any; + }; + mappings: { + _meta: object; + dynamic_templates: any[]; + date_detection: boolean; + properties: { + [fieldName: string]: any; + }; + }; + settings: { index: object }; + }; +} + +export type SearchHit = estypes.SearchHit; + +export interface SortedSearchHit extends SearchHit { + sort: any[]; + _source: { + [field: string]: JsonValue; + }; + fields: { + [field: string]: JsonArray; + }; +} + +export type LogsSharedVersionedRouteConfig = { + method: RouteMethod; +} & VersionedRouteConfig; diff --git a/x-pack/plugins/logs_shared/server/lib/adapters/framework/index.ts b/x-pack/plugins/logs_shared/server/lib/adapters/framework/index.ts new file mode 100644 index 000000000000..5d7c09c54b8c --- /dev/null +++ b/x-pack/plugins/logs_shared/server/lib/adapters/framework/index.ts @@ -0,0 +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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './adapter_types'; diff --git a/x-pack/plugins/logs_shared/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/logs_shared/server/lib/adapters/framework/kibana_framework_adapter.ts new file mode 100644 index 000000000000..413ab3b333e8 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -0,0 +1,179 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { TransportRequestParams } from '@elastic/elasticsearch'; +import { CoreSetup, IRouter, RouteMethod } from '@kbn/core/server'; +import { UI_SETTINGS } from '@kbn/data-plugin/server'; +import type { + LogsSharedPluginRequestHandlerContext, + LogsSharedServerPluginSetupDeps, + LogsSharedServerPluginStartDeps, +} from '../../../types'; +import { + CallWithRequestParams, + LogsSharedDatabaseGetIndicesAliasResponse, + LogsSharedDatabaseGetIndicesResponse, + LogsSharedDatabaseMultiResponse, + LogsSharedDatabaseSearchResponse, + LogsSharedVersionedRouteConfig, +} from './adapter_types'; + +interface FrozenIndexParams { + ignore_throttled?: boolean; +} + +export class KibanaFramework { + public router: IRouter; + public plugins: LogsSharedServerPluginSetupDeps; + + constructor( + core: CoreSetup, + plugins: LogsSharedServerPluginSetupDeps + ) { + this.router = core.http.createRouter(); + this.plugins = plugins; + } + + public registerVersionedRoute( + config: LogsSharedVersionedRouteConfig + ) { + const defaultOptions = { + tags: ['access:infra'], + }; + const routeConfig = { + access: config.access, + path: config.path, + // Currently we have no use of custom options beyond tags, this can be extended + // beyond defaultOptions if it's needed. + options: defaultOptions, + }; + switch (config.method) { + case 'get': + return this.router.versioned.get(routeConfig); + case 'post': + return this.router.versioned.post(routeConfig); + case 'delete': + return this.router.versioned.delete(routeConfig); + case 'put': + return this.router.versioned.put(routeConfig); + case 'patch': + return this.router.versioned.patch(routeConfig); + default: + throw new RangeError( + `#registerVersionedRoute: "${config.method}" is not an accepted method` + ); + } + } + + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + endpoint: 'search', + options?: CallWithRequestParams + ): Promise>; + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + endpoint: 'msearch', + options?: CallWithRequestParams + ): Promise>; + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + endpoint: 'indices.existsAlias', + options?: CallWithRequestParams + ): Promise; + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + method: 'indices.getAlias', + options?: object + ): Promise; + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + method: 'indices.get' | 'ml.getBuckets', + options?: object + ): Promise; + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + method: 'transport.request', + options?: CallWithRequestParams + ): Promise; + callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + endpoint: string, + options?: CallWithRequestParams + ): Promise; + public async callWithRequest( + requestContext: LogsSharedPluginRequestHandlerContext, + endpoint: string, + params: CallWithRequestParams + ) { + const { elasticsearch, uiSettings } = await requestContext.core; + + const includeFrozen = await uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); + if (endpoint === 'msearch') { + const maxConcurrentShardRequests = await uiSettings.client.get( + UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS + ); + if (maxConcurrentShardRequests > 0) { + params = { ...params, max_concurrent_shard_requests: maxConcurrentShardRequests }; + } + } + + // Only set the "ignore_throttled" value (to false) if the Kibana setting + // for "search:includeFrozen" is true (i.e. don't ignore throttled indices, a triple negative!) + // More information: + // - https://github.com/elastic/kibana/issues/113197 + // - https://github.com/elastic/elasticsearch/pull/77479 + // + // NOTE: these params only need to be spread onto the search and msearch calls below + const frozenIndicesParams: FrozenIndexParams = {}; + if (includeFrozen) { + frozenIndicesParams.ignore_throttled = false; + } + + let apiResult; + switch (endpoint) { + case 'search': + apiResult = elasticsearch.client.asCurrentUser.search({ + ...params, + ...frozenIndicesParams, + }); + break; + case 'msearch': + apiResult = elasticsearch.client.asCurrentUser.msearch({ + ...params, + ...frozenIndicesParams, + } as estypes.MsearchRequest); + break; + case 'indices.existsAlias': + apiResult = elasticsearch.client.asCurrentUser.indices.existsAlias({ + ...params, + } as estypes.IndicesExistsAliasRequest); + break; + case 'indices.getAlias': + apiResult = elasticsearch.client.asCurrentUser.indices.getAlias({ + ...params, + }); + break; + case 'indices.get': + apiResult = elasticsearch.client.asCurrentUser.indices.get({ + ...params, + } as estypes.IndicesGetRequest); + break; + case 'transport.request': + apiResult = elasticsearch.client.asCurrentUser.transport.request({ + ...params, + } as TransportRequestParams); + break; + case 'ml.getBuckets': + apiResult = elasticsearch.client.asCurrentUser.ml.getBuckets({ + ...params, + } as estypes.MlGetBucketsRequest); + break; + } + return apiResult ? await apiResult : undefined; + } +} diff --git a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/plugins/logs_shared/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts similarity index 96% rename from x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts rename to x-pack/plugins/logs_shared/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts index 97ef51ded269..66de5699cfb3 100644 --- a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts +++ b/x-pack/plugins/logs_shared/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts @@ -12,7 +12,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import * as runtimeTypes from 'io-ts'; import { JsonArray } from '@kbn/utility-types'; import { compact } from 'lodash'; -import type { InfraPluginRequestHandlerContext } from '../../../types'; +import type { LogsSharedPluginRequestHandlerContext } from '../../../types'; import { LogEntriesAdapter, LogEntriesParams, @@ -28,11 +28,11 @@ import { TIMESTAMP_FIELD, TIEBREAKER_FIELD } from '../../../../common/constants' const TIMESTAMP_FORMAT = 'epoch_millis'; -export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { +export class LogsSharedKibanaLogEntriesAdapter implements LogEntriesAdapter { constructor(private readonly framework: KibanaFramework) {} public async getLogEntries( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, resolvedLogView: ResolvedLogView, fields: string[], params: LogEntriesParams @@ -123,7 +123,7 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { } public async getContainedLogSummaryBuckets( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, resolvedLogView: ResolvedLogView, startTimestamp: number, endTimestamp: number, @@ -318,5 +318,3 @@ const LogSummaryResponseRuntimeType = runtimeTypes.type({ }), }), }); - -export type LogSummaryResponse = runtimeTypes.TypeOf; diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/index.ts b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/index.ts similarity index 100% rename from x-pack/plugins/infra/server/lib/domains/log_entries_domain/index.ts rename to x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/index.ts diff --git a/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.mock.ts b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.mock.ts new file mode 100644 index 000000000000..74509f11ae4a --- /dev/null +++ b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.mock.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ILogsSharedLogEntriesDomain } from './log_entries_domain'; + +export const createLogsSharedLogEntriesDomainMock = + (): jest.Mocked => { + return { + getLogEntriesAround: jest.fn(), + getLogEntries: jest.fn(), + getLogSummaryBucketsBetween: jest.fn(), + getLogSummaryHighlightBucketsBetween: jest.fn(), + getLogEntryDatasets: jest.fn(), + }; + }; diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts similarity index 84% rename from x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts rename to x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts index fcda9b30b0da..92829c676b93 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -26,8 +26,8 @@ import { Fields, Highlights, } from '../../../services/log_entries/message/message'; -import type { InfraPluginRequestHandlerContext } from '../../../types'; -import { InfraBackendLibs } from '../../infra_types'; +import type { LogsSharedPluginRequestHandlerContext } from '../../../types'; +import { LogsSharedBackendLibs } from '../../logs_shared_types'; import { CompositeDatasetKey, createLogEntryDatasetsQuery, @@ -59,14 +59,54 @@ const FIELDS_FROM_CONTEXT = ['log.file.path', 'host.name', 'container.id'] as co const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000; -export class InfraLogEntriesDomain { +export interface ILogsSharedLogEntriesDomain { + getLogEntriesAround( + requestContext: LogsSharedPluginRequestHandlerContext, + logView: LogViewReference, + params: LogEntriesAroundParams, + columnOverrides?: LogViewColumnConfiguration[] + ): Promise<{ entries: LogEntry[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }>; + getLogEntries( + requestContext: LogsSharedPluginRequestHandlerContext, + logView: LogViewReference, + params: LogEntriesParams, + columnOverrides?: LogViewColumnConfiguration[] + ): Promise<{ entries: LogEntry[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }>; + getLogSummaryBucketsBetween( + requestContext: LogsSharedPluginRequestHandlerContext, + logView: LogViewReference, + start: number, + end: number, + bucketSize: number, + filterQuery?: LogEntryQuery + ): Promise; + getLogSummaryHighlightBucketsBetween( + requestContext: LogsSharedPluginRequestHandlerContext, + logView: LogViewReference, + startTimestamp: number, + endTimestamp: number, + bucketSize: number, + highlightQueries: string[], + filterQuery?: LogEntryQuery + ): Promise; + getLogEntryDatasets( + requestContext: LogsSharedPluginRequestHandlerContext, + timestampField: string, + indexName: string, + startTime: number, + endTime: number, + runtimeMappings: estypes.MappingRuntimeFields + ): Promise; +} + +export class LogsSharedLogEntriesDomain implements ILogsSharedLogEntriesDomain { constructor( private readonly adapter: LogEntriesAdapter, - private readonly libs: Pick + private readonly libs: Pick ) {} public async getLogEntriesAround( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, logView: LogViewReference, params: LogEntriesAroundParams, columnOverrides?: LogViewColumnConfiguration[] @@ -126,7 +166,7 @@ export class InfraLogEntriesDomain { } public async getLogEntries( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, logView: LogViewReference, params: LogEntriesParams, columnOverrides?: LogViewColumnConfiguration[] @@ -184,7 +224,7 @@ export class InfraLogEntriesDomain { } public async getLogSummaryBucketsBetween( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, logView: LogViewReference, start: number, end: number, @@ -208,7 +248,7 @@ export class InfraLogEntriesDomain { } public async getLogSummaryHighlightBucketsBetween( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, logView: LogViewReference, startTimestamp: number, endTimestamp: number, @@ -255,7 +295,7 @@ export class InfraLogEntriesDomain { } public async getLogEntryDatasets( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, timestampField: string, indexName: string, startTime: number, @@ -298,14 +338,14 @@ export class InfraLogEntriesDomain { export interface LogEntriesAdapter { getLogEntries( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, resolvedLogView: ResolvedLogView, fields: string[], params: LogEntriesParams ): Promise<{ documents: LogEntryDocument[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }>; getContainedLogSummaryBuckets( - requestContext: InfraPluginRequestHandlerContext, + requestContext: LogsSharedPluginRequestHandlerContext, resolvedLogView: ResolvedLogView, startTimestamp: number, endTimestamp: number, diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts similarity index 96% rename from x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts rename to x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts index a658c7deb317..c2b966e86983 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts +++ b/x-pack/plugins/logs_shared/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts @@ -99,5 +99,3 @@ export const logEntryDatasetsResponseRT = rt.intersection([ }), }), ]); - -export type LogEntryDatasetsResponse = rt.TypeOf; diff --git a/x-pack/plugins/logs_shared/server/lib/logs_shared_types.ts b/x-pack/plugins/logs_shared/server/lib/logs_shared_types.ts new file mode 100644 index 000000000000..9d7ef50443c7 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/lib/logs_shared_types.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Logger } from '@kbn/logging'; +import type { IBasePath } from '@kbn/core/server'; +import type { LogsSharedPluginStartServicesAccessor, UsageCollector } from '../types'; +import type { KibanaFramework } from './adapters/framework/kibana_framework_adapter'; +import type { ILogsSharedLogEntriesDomain } from './domains/log_entries_domain'; + +export interface LogsSharedDomainLibs { + logEntries: ILogsSharedLogEntriesDomain; +} + +export interface LogsSharedBackendLibs extends LogsSharedDomainLibs { + basePath: IBasePath; + framework: KibanaFramework; + getStartServices: LogsSharedPluginStartServicesAccessor; + logger: Logger; + getUsageCollector: () => UsageCollector; +} diff --git a/x-pack/plugins/logs_shared/server/logs_shared_server.ts b/x-pack/plugins/logs_shared/server/logs_shared_server.ts new file mode 100644 index 000000000000..60dc17be61d2 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/logs_shared_server.ts @@ -0,0 +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. + */ + +import { LogsSharedBackendLibs } from './lib/logs_shared_types'; +import { + initLogEntriesHighlightsRoute, + initLogEntriesSummaryHighlightsRoute, + initLogEntriesSummaryRoute, +} from './routes/log_entries'; +import { initLogViewRoutes } from './routes/log_views'; + +export const initLogsSharedServer = (libs: LogsSharedBackendLibs) => { + initLogEntriesHighlightsRoute(libs); + initLogEntriesSummaryRoute(libs); + initLogEntriesSummaryHighlightsRoute(libs); + initLogViewRoutes(libs); +}; diff --git a/x-pack/plugins/logs_shared/server/mocks.ts b/x-pack/plugins/logs_shared/server/mocks.ts new file mode 100644 index 000000000000..a8b16381d32f --- /dev/null +++ b/x-pack/plugins/logs_shared/server/mocks.ts @@ -0,0 +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. + */ + +import { createLogsSharedLogEntriesDomainMock } from './lib/domains/log_entries_domain/log_entries_domain.mock'; +import { + createLogViewsServiceSetupMock, + createLogViewsServiceStartMock, +} from './services/log_views/log_views_service.mock'; +import { LogsSharedPluginSetup, LogsSharedPluginStart } from './types'; + +const createLogsSharedSetupMock = () => { + const logsSharedSetupMock: jest.Mocked = { + logViews: createLogViewsServiceSetupMock(), + logEntries: createLogsSharedLogEntriesDomainMock(), + registerUsageCollectorActions: jest.fn(), + }; + + return logsSharedSetupMock; +}; + +const createLogsSharedStartMock = () => { + const logsSharedStartMock: jest.Mocked = { + logViews: createLogViewsServiceStartMock(), + }; + return logsSharedStartMock; +}; + +export const logsSharedPluginMock = { + createSetupContract: createLogsSharedSetupMock, + createStartContract: createLogsSharedStartMock, +}; diff --git a/x-pack/plugins/logs_shared/server/plugin.ts b/x-pack/plugins/logs_shared/server/plugin.ts new file mode 100644 index 000000000000..6ccf743dba7f --- /dev/null +++ b/x-pack/plugins/logs_shared/server/plugin.ts @@ -0,0 +1,98 @@ +/* + * 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 { PluginInitializerContext, CoreStart, Plugin, Logger } from '@kbn/core/server'; + +import { + LogsSharedPluginCoreSetup, + LogsSharedPluginSetup, + LogsSharedPluginStart, + LogsSharedServerPluginSetupDeps, + LogsSharedServerPluginStartDeps, + UsageCollector, +} from './types'; +import { logViewSavedObjectType } from './saved_objects'; +import { initLogsSharedServer } from './logs_shared_server'; +import { LogViewsService } from './services/log_views'; +import { KibanaFramework } from './lib/adapters/framework/kibana_framework_adapter'; +import { LogsSharedBackendLibs, LogsSharedDomainLibs } from './lib/logs_shared_types'; +import { LogsSharedLogEntriesDomain } from './lib/domains/log_entries_domain'; +import { LogsSharedKibanaLogEntriesAdapter } from './lib/adapters/log_entries/kibana_log_entries_adapter'; +import { LogEntriesService } from './services/log_entries'; + +export class LogsSharedPlugin + implements + Plugin< + LogsSharedPluginSetup, + LogsSharedPluginStart, + LogsSharedServerPluginSetupDeps, + LogsSharedServerPluginStartDeps + > +{ + private readonly logger: Logger; + private libs!: LogsSharedBackendLibs; + private logViews: LogViewsService; + private usageCollector: UsageCollector; + + constructor(context: PluginInitializerContext) { + this.logger = context.logger.get(); + this.usageCollector = {}; + + this.logViews = new LogViewsService(this.logger.get('logViews')); + } + + public setup(core: LogsSharedPluginCoreSetup, plugins: LogsSharedServerPluginSetupDeps) { + const framework = new KibanaFramework(core, plugins); + + const logViews = this.logViews.setup(); + + // Register saved objects + core.savedObjects.registerType(logViewSavedObjectType); + + const domainLibs: LogsSharedDomainLibs = { + logEntries: new LogsSharedLogEntriesDomain(new LogsSharedKibanaLogEntriesAdapter(framework), { + framework, + getStartServices: () => core.getStartServices(), + }), + }; + + this.libs = { + ...domainLibs, + basePath: core.http.basePath, + framework, + getStartServices: () => core.getStartServices(), + logger: this.logger, + getUsageCollector: () => this.usageCollector, + }; + + // Register server side APIs + initLogsSharedServer(this.libs); + + const logEntriesService = new LogEntriesService(); + logEntriesService.setup(core, plugins); + + return { + ...domainLibs, + logViews, + registerUsageCollectorActions: (usageCollector: UsageCollector) => { + Object.assign(this.usageCollector, usageCollector); + }, + }; + } + + public start(core: CoreStart, plugins: LogsSharedServerPluginStartDeps) { + const logViews = this.logViews.start({ + savedObjects: core.savedObjects, + dataViews: plugins.dataViews, + elasticsearch: core.elasticsearch, + }); + + return { logViews }; + } + + public stop() {} +} diff --git a/x-pack/plugins/infra/server/routes/log_entries/highlights.ts b/x-pack/plugins/logs_shared/server/routes/log_entries/highlights.ts similarity index 96% rename from x-pack/plugins/infra/server/routes/log_entries/highlights.ts rename to x-pack/plugins/logs_shared/server/routes/log_entries/highlights.ts index 9d81e0d349f4..e8019c58d600 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/highlights.ts +++ b/x-pack/plugins/logs_shared/server/routes/log_entries/highlights.ts @@ -15,14 +15,14 @@ import { schema } from '@kbn/config-schema'; import { logEntriesV1 } from '../../../common/http_api'; import { throwErrors } from '../../../common/runtime_types'; -import { InfraBackendLibs } from '../../lib/infra_types'; +import { LogsSharedBackendLibs } from '../../lib/logs_shared_types'; import { parseFilterQuery } from '../../utils/serialized_query'; import { LogEntriesParams } from '../../lib/domains/log_entries_domain'; const escapeHatch = schema.object({}, { unknowns: 'allow' }); -export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: InfraBackendLibs) => { +export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: LogsSharedBackendLibs) => { framework .registerVersionedRoute({ access: 'internal', diff --git a/x-pack/plugins/infra/server/routes/log_entries/index.ts b/x-pack/plugins/logs_shared/server/routes/log_entries/index.ts similarity index 100% rename from x-pack/plugins/infra/server/routes/log_entries/index.ts rename to x-pack/plugins/logs_shared/server/routes/log_entries/index.ts diff --git a/x-pack/plugins/infra/server/routes/log_entries/summary.ts b/x-pack/plugins/logs_shared/server/routes/log_entries/summary.ts similarity index 83% rename from x-pack/plugins/infra/server/routes/log_entries/summary.ts rename to x-pack/plugins/logs_shared/server/routes/log_entries/summary.ts index ee042f51d1dc..2ac889ab9ffd 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/summary.ts +++ b/x-pack/plugins/logs_shared/server/routes/log_entries/summary.ts @@ -15,14 +15,17 @@ import { schema } from '@kbn/config-schema'; import { logEntriesV1 } from '../../../common/http_api'; import { throwErrors } from '../../../common/runtime_types'; -import { InfraBackendLibs } from '../../lib/infra_types'; +import { LogsSharedBackendLibs } from '../../lib/logs_shared_types'; import { parseFilterQuery } from '../../utils/serialized_query'; -import { UsageCollector } from '../../usage/usage_collector'; const escapeHatch = schema.object({}, { unknowns: 'allow' }); -export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBackendLibs) => { +export const initLogEntriesSummaryRoute = ({ + framework, + logEntries, + getUsageCollector, +}: LogsSharedBackendLibs) => { framework .registerVersionedRoute({ access: 'internal', @@ -41,6 +44,8 @@ export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBacke ); const { logView, startTimestamp, endTimestamp, bucketSize, query } = payload; + const usageCollector = getUsageCollector(); + const buckets = await logEntries.getLogSummaryBucketsBetween( requestContext, logView, @@ -50,7 +55,9 @@ export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBacke parseFilterQuery(query) ); - UsageCollector.countLogs(); + if (typeof usageCollector.countLogs === 'function') { + usageCollector.countLogs(); + } return response.ok({ body: logEntriesV1.logEntriesSummaryResponseRT.encode({ diff --git a/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts b/x-pack/plugins/logs_shared/server/routes/log_entries/summary_highlights.ts similarity index 95% rename from x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts rename to x-pack/plugins/logs_shared/server/routes/log_entries/summary_highlights.ts index e831ec51c414..b4093f1d6543 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts +++ b/x-pack/plugins/logs_shared/server/routes/log_entries/summary_highlights.ts @@ -15,7 +15,7 @@ import { schema } from '@kbn/config-schema'; import { logEntriesV1 } from '../../../common/http_api'; import { throwErrors } from '../../../common/runtime_types'; -import { InfraBackendLibs } from '../../lib/infra_types'; +import { LogsSharedBackendLibs } from '../../lib/logs_shared_types'; import { parseFilterQuery } from '../../utils/serialized_query'; @@ -24,7 +24,7 @@ const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesSummaryHighlightsRoute = ({ framework, logEntries, -}: InfraBackendLibs) => { +}: LogsSharedBackendLibs) => { framework .registerVersionedRoute({ access: 'internal', diff --git a/x-pack/plugins/infra/server/routes/log_views/get_log_view.ts b/x-pack/plugins/logs_shared/server/routes/log_views/get_log_view.ts similarity index 92% rename from x-pack/plugins/infra/server/routes/log_views/get_log_view.ts rename to x-pack/plugins/logs_shared/server/routes/log_views/get_log_view.ts index 0c3cb7cbac2a..ef6e69f07d0e 100644 --- a/x-pack/plugins/infra/server/routes/log_views/get_log_view.ts +++ b/x-pack/plugins/logs_shared/server/routes/log_views/get_log_view.ts @@ -9,14 +9,14 @@ import { logViewsV1 } from '../../../common/http_api'; import { LOG_VIEW_URL } from '../../../common/http_api/log_views'; import { createValidationFunction } from '../../../common/runtime_types'; import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter'; -import type { InfraPluginStartServicesAccessor } from '../../types'; +import type { LogsSharedPluginStartServicesAccessor } from '../../types'; export const initGetLogViewRoute = ({ framework, getStartServices, }: { framework: KibanaFramework; - getStartServices: InfraPluginStartServicesAccessor; + getStartServices: LogsSharedPluginStartServicesAccessor; }) => { framework .registerVersionedRoute({ diff --git a/x-pack/plugins/infra/server/routes/log_views/index.ts b/x-pack/plugins/logs_shared/server/routes/log_views/index.ts similarity index 82% rename from x-pack/plugins/infra/server/routes/log_views/index.ts rename to x-pack/plugins/logs_shared/server/routes/log_views/index.ts index fa7e6f6e1b9d..b2670cfa3e40 100644 --- a/x-pack/plugins/infra/server/routes/log_views/index.ts +++ b/x-pack/plugins/logs_shared/server/routes/log_views/index.ts @@ -6,13 +6,13 @@ */ import { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter'; -import { InfraPluginStartServicesAccessor } from '../../types'; +import { LogsSharedPluginStartServicesAccessor } from '../../types'; import { initGetLogViewRoute } from './get_log_view'; import { initPutLogViewRoute } from './put_log_view'; export const initLogViewRoutes = (dependencies: { framework: KibanaFramework; - getStartServices: InfraPluginStartServicesAccessor; + getStartServices: LogsSharedPluginStartServicesAccessor; }) => { initGetLogViewRoute(dependencies); initPutLogViewRoute(dependencies); diff --git a/x-pack/plugins/infra/server/routes/log_views/put_log_view.ts b/x-pack/plugins/logs_shared/server/routes/log_views/put_log_view.ts similarity index 93% rename from x-pack/plugins/infra/server/routes/log_views/put_log_view.ts rename to x-pack/plugins/logs_shared/server/routes/log_views/put_log_view.ts index 310960156abf..899200902aa1 100644 --- a/x-pack/plugins/infra/server/routes/log_views/put_log_view.ts +++ b/x-pack/plugins/logs_shared/server/routes/log_views/put_log_view.ts @@ -9,14 +9,14 @@ import { logViewsV1 } from '../../../common/http_api'; import { LOG_VIEW_URL } from '../../../common/http_api/log_views'; import { createValidationFunction } from '../../../common/runtime_types'; import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter'; -import type { InfraPluginStartServicesAccessor } from '../../types'; +import type { LogsSharedPluginStartServicesAccessor } from '../../types'; export const initPutLogViewRoute = ({ framework, getStartServices, }: { framework: KibanaFramework; - getStartServices: InfraPluginStartServicesAccessor; + getStartServices: LogsSharedPluginStartServicesAccessor; }) => { framework .registerVersionedRoute({ diff --git a/x-pack/plugins/logs_shared/server/saved_objects/index.ts b/x-pack/plugins/logs_shared/server/saved_objects/index.ts new file mode 100644 index 000000000000..bd7ecac5179a --- /dev/null +++ b/x-pack/plugins/logs_shared/server/saved_objects/index.ts @@ -0,0 +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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './log_view'; diff --git a/x-pack/plugins/infra/server/saved_objects/log_view/index.ts b/x-pack/plugins/logs_shared/server/saved_objects/log_view/index.ts similarity index 100% rename from x-pack/plugins/infra/server/saved_objects/log_view/index.ts rename to x-pack/plugins/logs_shared/server/saved_objects/log_view/index.ts diff --git a/x-pack/plugins/infra/server/saved_objects/log_view/log_view_saved_object.ts b/x-pack/plugins/logs_shared/server/saved_objects/log_view/log_view_saved_object.ts similarity index 100% rename from x-pack/plugins/infra/server/saved_objects/log_view/log_view_saved_object.ts rename to x-pack/plugins/logs_shared/server/saved_objects/log_view/log_view_saved_object.ts index 9202227867dc..246c398ea5a6 100644 --- a/x-pack/plugins/infra/server/saved_objects/log_view/log_view_saved_object.ts +++ b/x-pack/plugins/logs_shared/server/saved_objects/log_view/log_view_saved_object.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { SavedObject, SavedObjectsType } from '@kbn/core/server'; import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { SavedObject, SavedObjectsType } from '@kbn/core/server'; import { logViewSavedObjectRT } from './types'; export const logViewSavedObjectName = 'infrastructure-monitoring-log-view'; diff --git a/x-pack/plugins/infra/server/saved_objects/log_view/references/index.ts b/x-pack/plugins/logs_shared/server/saved_objects/log_view/references/index.ts similarity index 100% rename from x-pack/plugins/infra/server/saved_objects/log_view/references/index.ts rename to x-pack/plugins/logs_shared/server/saved_objects/log_view/references/index.ts diff --git a/x-pack/plugins/infra/server/saved_objects/log_view/references/log_indices.ts b/x-pack/plugins/logs_shared/server/saved_objects/log_view/references/log_indices.ts similarity index 85% rename from x-pack/plugins/infra/server/saved_objects/log_view/references/log_indices.ts rename to x-pack/plugins/logs_shared/server/saved_objects/log_view/references/log_indices.ts index ea45be30bc0b..660f01f47eb5 100644 --- a/x-pack/plugins/infra/server/saved_objects/log_view/references/log_indices.ts +++ b/x-pack/plugins/logs_shared/server/saved_objects/log_view/references/log_indices.ts @@ -7,24 +7,24 @@ import { SavedObjectReference } from '@kbn/core/server'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; -import { LogViewAttributes } from '../../../../common/log_views'; import { SavedObjectAttributesWithReferences, SavedObjectReferenceResolutionError, } from '../../references'; +import { LogViewSavedObjectAttributes } from '../types'; export const logIndicesDataViewReferenceName = 'log-indices-data-view-0'; export const extractLogIndicesSavedObjectReferences = ( - unextractedAttributes: LogViewAttributes -): SavedObjectAttributesWithReferences => { + unextractedAttributes: LogViewSavedObjectAttributes +): SavedObjectAttributesWithReferences => { if (unextractedAttributes.logIndices.type === 'data_view') { const logDataViewReference: SavedObjectReference = { id: unextractedAttributes.logIndices.dataViewId, type: DATA_VIEW_SAVED_OBJECT_TYPE, name: logIndicesDataViewReferenceName, }; - const attributes: LogViewAttributes = { + const attributes: LogViewSavedObjectAttributes = { ...unextractedAttributes, logIndices: { ...unextractedAttributes.logIndices, @@ -44,9 +44,9 @@ export const extractLogIndicesSavedObjectReferences = ( }; export const resolveLogIndicesSavedObjectReferences = ( - attributes: LogViewAttributes, + attributes: LogViewSavedObjectAttributes, references: SavedObjectReference[] -): LogViewAttributes => { +): LogViewSavedObjectAttributes => { if (attributes.logIndices?.type === 'data_view') { const logDataViewReference = references.find( (reference) => reference.name === logIndicesDataViewReferenceName diff --git a/x-pack/plugins/infra/server/saved_objects/log_view/types.ts b/x-pack/plugins/logs_shared/server/saved_objects/log_view/types.ts similarity index 95% rename from x-pack/plugins/infra/server/saved_objects/log_view/types.ts rename to x-pack/plugins/logs_shared/server/saved_objects/log_view/types.ts index 34387b5e3308..fb8bf49781a2 100644 --- a/x-pack/plugins/infra/server/saved_objects/log_view/types.ts +++ b/x-pack/plugins/logs_shared/server/saved_objects/log_view/types.ts @@ -58,6 +58,8 @@ export const logViewSavedObjectAttributesRT = rt.strict({ logColumns: rt.array(logViewSavedObjectColumnConfigurationRT), }); +export type LogViewSavedObjectAttributes = rt.TypeOf; + export const logViewSavedObjectRT = rt.intersection([ rt.type({ id: rt.string, diff --git a/x-pack/plugins/logs_shared/server/saved_objects/references.test.ts b/x-pack/plugins/logs_shared/server/saved_objects/references.test.ts new file mode 100644 index 000000000000..674aabbd9d05 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/saved_objects/references.test.ts @@ -0,0 +1,121 @@ +/* + * 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 { SavedObjectReference } from '@kbn/core/server'; +import { + extractSavedObjectReferences, + resolveSavedObjectReferences, + SavedObjectAttributesWithReferences, +} from './references'; + +it('extractSavedObjectReferences extracts references using the given extractors', () => { + const { attributes, references } = extractSavedObjectReferences([ + extractReferenceA, + extractReferenceB, + ])({ + a: 'id-a', + b: 'id-b', + c: 'something-else', + }); + + expect(references).toMatchObject([ + { id: 'id-a', name: REFERENCE_A_NAME, type: 'some-reference' }, + { id: 'id-b', name: REFERENCE_B_NAME, type: 'some-reference' }, + ]); + expect(attributes).toMatchObject({ + a: REFERENCE_A_NAME, + b: REFERENCE_B_NAME, + c: 'something-else', + }); +}); + +it('resolveSavedObjectReferences resolves references using the given resolvers', () => { + const attributes = resolveSavedObjectReferences([resolveReferenceA, resolveReferenceB])( + { + a: REFERENCE_A_NAME, + b: REFERENCE_B_NAME, + c: 'something-else', + }, + [ + { id: 'id-a', name: REFERENCE_A_NAME, type: 'some-reference' }, + { id: 'id-b', name: REFERENCE_B_NAME, type: 'some-reference' }, + ] + ); + + expect(attributes).toMatchObject({ + a: 'id-a', + b: 'id-b', + c: 'something-else', + }); +}); + +interface TestSavedObjectAttributes { + a: string; + b: string; + c: string; +} + +const REFERENCE_A_NAME = 'reference-a'; +const REFERENCE_B_NAME = 'reference-b'; + +const extractReferenceA = ( + attributes: TestSavedObjectAttributes +): SavedObjectAttributesWithReferences => ({ + attributes: { ...attributes, a: REFERENCE_A_NAME }, + references: [ + { + id: attributes.a, + name: REFERENCE_A_NAME, + type: 'some-reference', + }, + ], +}); + +const extractReferenceB = ( + attributes: TestSavedObjectAttributes +): SavedObjectAttributesWithReferences => ({ + attributes: { ...attributes, b: REFERENCE_B_NAME }, + references: [ + { + id: attributes.b, + name: REFERENCE_B_NAME, + type: 'some-reference', + }, + ], +}); + +const resolveReferenceA = ( + attributes: TestSavedObjectAttributes, + references: SavedObjectReference[] +): TestSavedObjectAttributes => { + const referenceA = references.find((reference) => reference.name === REFERENCE_A_NAME); + + if (referenceA != null) { + return { + ...attributes, + a: referenceA.id, + }; + } else { + return attributes; + } +}; + +const resolveReferenceB = ( + attributes: TestSavedObjectAttributes, + references: SavedObjectReference[] +): TestSavedObjectAttributes => { + const referenceB = references.find((reference) => reference.name === REFERENCE_B_NAME); + + if (referenceB != null) { + return { + ...attributes, + b: referenceB.id, + }; + } else { + return attributes; + } +}; diff --git a/x-pack/plugins/logs_shared/server/saved_objects/references.ts b/x-pack/plugins/logs_shared/server/saved_objects/references.ts new file mode 100644 index 000000000000..13b64ab6e6e7 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/saved_objects/references.ts @@ -0,0 +1,78 @@ +/* + * 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 rt from 'io-ts'; +import { SavedObject, SavedObjectReference } from '@kbn/core/server'; + +export type SavedObjectAttributesWithReferences = Pick< + SavedObject, + 'attributes' | 'references' +>; + +export type SavedObjectReferenceExtractor = ( + savedObjectAttributes: SavedObjectAttributes +) => SavedObjectAttributesWithReferences; + +export type SavedObjectReferenceResolver = ( + savedObjectAttributes: SavedObjectAttributes, + references: SavedObjectReference[] +) => SavedObjectAttributes; + +export const savedObjectReferenceRT = rt.strict({ + name: rt.string, + type: rt.string, + id: rt.string, +}); + +/** + * Rewrites a saved object such that well-known saved object references + * are extracted in the `references` array and replaced by the appropriate + * name. This is the inverse operation to `resolveSavedObjectReferences`. + */ +export const extractSavedObjectReferences = + ( + referenceExtractors: Array> + ) => + ( + savedObjectAttributes: SavedObjectAttributes + ): SavedObjectAttributesWithReferences => + referenceExtractors.reduce>( + ({ attributes: accumulatedAttributes, references: accumulatedReferences }, extract) => { + const { attributes, references } = extract(accumulatedAttributes); + return { + attributes, + references: [...accumulatedReferences, ...references], + }; + }, + { + attributes: savedObjectAttributes, + references: [], + } + ); + +/** + * Rewrites a source configuration such that well-known saved object references + * are resolved from the `references` argument and replaced by the real saved + * object ids. This is the inverse operation to `extractSavedObjectReferences`. + */ +export const resolveSavedObjectReferences = + ( + referenceResolvers: Array> + ) => + (attributes: SavedObjectAttributes, references: SavedObjectReference[]): SavedObjectAttributes => + referenceResolvers.reduce( + (accumulatedAttributes, resolve) => resolve(accumulatedAttributes, references), + attributes + ); + +export class SavedObjectReferenceResolutionError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + this.name = 'SavedObjectReferenceResolutionError'; + } +} diff --git a/x-pack/plugins/infra/server/services/log_entries/index.ts b/x-pack/plugins/logs_shared/server/services/log_entries/index.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/index.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/index.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts b/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/log_entries_search_strategy.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entries_service.ts b/x-pack/plugins/logs_shared/server/services/log_entries/log_entries_service.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/log_entries_service.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/log_entries_service.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/log_entry_search_strategy.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/log_entry_search_strategy.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.ts b/x-pack/plugins/logs_shared/server/services/log_entries/log_entry_search_strategy.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/log_entry_search_strategy.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_apache2.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_apache2.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_apache2.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_apache2.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_apache2.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_apache2.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_apache2.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_apache2.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_auditd.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_auditd.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_auditd.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_auditd.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_auditd.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_auditd.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_auditd.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_auditd.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_haproxy.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_haproxy.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_haproxy.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_haproxy.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_haproxy.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_haproxy.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_haproxy.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_haproxy.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_icinga.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_icinga.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_icinga.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_icinga.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_icinga.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_icinga.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_icinga.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_icinga.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_iis.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_iis.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_iis.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_iis.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_iis.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_iis.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_iis.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_iis.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_kafka.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_kafka.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_kafka.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_kafka.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_logstash.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_logstash.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_logstash.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_logstash.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_logstash.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_logstash.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_logstash.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_logstash.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mongodb.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mongodb.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mongodb.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mongodb.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mongodb.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mongodb.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mongodb.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mongodb.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mysql.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mysql.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mysql.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mysql.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mysql.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mysql.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_mysql.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_mysql.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_nginx.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_nginx.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_nginx.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_nginx.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_nginx.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_nginx.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_nginx.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_nginx.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_osquery.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_osquery.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_osquery.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_osquery.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_osquery.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_osquery.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_osquery.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_osquery.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_redis.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_redis.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_redis.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_redis.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_system.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_system.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_system.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_system.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_traefik.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_traefik.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_traefik.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_traefik.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_traefik.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_traefik.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/filebeat_traefik.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/filebeat_traefik.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/generic.test.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/generic.test.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/generic.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/generic.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic_webserver.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/generic_webserver.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic_webserver.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/generic_webserver.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/helpers.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/helpers.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/helpers.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/helpers.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/index.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/index.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/index.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/builtin_rules/index.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/index.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/index.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/index.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/index.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/message.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/message.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/message.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/message.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/message/rule_types.ts b/x-pack/plugins/logs_shared/server/services/log_entries/message/rule_types.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/message/rule_types.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/message/rule_types.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/queries/common.ts b/x-pack/plugins/logs_shared/server/services/log_entries/queries/common.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/queries/common.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/queries/common.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/queries/log_entries.ts b/x-pack/plugins/logs_shared/server/services/log_entries/queries/log_entries.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/queries/log_entries.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/queries/log_entries.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/queries/log_entry.ts b/x-pack/plugins/logs_shared/server/services/log_entries/queries/log_entry.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/queries/log_entry.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/queries/log_entry.ts diff --git a/x-pack/plugins/infra/server/services/log_entries/types.ts b/x-pack/plugins/logs_shared/server/services/log_entries/types.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_entries/types.ts rename to x-pack/plugins/logs_shared/server/services/log_entries/types.ts diff --git a/x-pack/plugins/infra/server/services/log_views/errors.ts b/x-pack/plugins/logs_shared/server/services/log_views/errors.ts similarity index 65% rename from x-pack/plugins/infra/server/services/log_views/errors.ts rename to x-pack/plugins/logs_shared/server/services/log_views/errors.ts index fb0dc3b03151..088dd6244bba 100644 --- a/x-pack/plugins/infra/server/services/log_views/errors.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/errors.ts @@ -5,9 +5,18 @@ * 2.0. */ +/* eslint-disable max-classes-per-file */ + export class NotFoundError extends Error { constructor(message?: string) { super(message); Object.setPrototypeOf(this, new.target.prototype); } } + +export class LogViewFallbackUnregisteredError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/x-pack/plugins/infra/server/services/log_views/index.ts b/x-pack/plugins/logs_shared/server/services/log_views/index.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_views/index.ts rename to x-pack/plugins/logs_shared/server/services/log_views/index.ts diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_client.mock.ts b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.mock.ts similarity index 100% rename from x-pack/plugins/infra/server/services/log_views/log_views_client.mock.ts rename to x-pack/plugins/logs_shared/server/services/log_views/log_views_client.mock.ts diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts similarity index 88% rename from x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts rename to x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts index e517ae8aef7f..5efdf9e125de 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.test.ts @@ -17,42 +17,11 @@ import { LogViewsStaticConfig, } from '../../../common/log_views'; import { createLogViewMock } from '../../../common/log_views/log_view.mock'; -import { InfraSource } from '../../lib/sources'; -import { createInfraSourcesMock } from '../../lib/sources/mocks'; import { extractLogViewSavedObjectReferences, logViewSavedObjectName, } from '../../saved_objects/log_view'; -import { getAttributesFromSourceConfiguration, LogViewsClient } from './log_views_client'; - -describe('getAttributesFromSourceConfiguration function', () => { - it('converts the index_pattern log indices type to data_view', () => { - const logViewAttributes = getAttributesFromSourceConfiguration(basicTestSourceConfiguration); - - expect(logViewAttributes.logIndices).toEqual({ - type: 'data_view', - dataViewId: 'INDEX_PATTERN_ID', - }); - }); - - it('preserves the index_name log indices type', () => { - const logViewAttributes = getAttributesFromSourceConfiguration({ - ...basicTestSourceConfiguration, - configuration: { - ...basicTestSourceConfiguration.configuration, - logIndices: { - type: 'index_name', - indexName: 'INDEX_NAME', - }, - }, - }); - - expect(logViewAttributes.logIndices).toEqual({ - type: 'index_name', - indexName: 'INDEX_NAME', - }); - }); -}); +import { LogViewsClient } from './log_views_client'; describe('LogViewsClient class', () => { it('getLogView resolves the default id to a real saved object id if it exists', async () => { @@ -116,9 +85,9 @@ describe('LogViewsClient class', () => { }); it('getLogView preserves the default id for fallback lookups', async () => { - const { infraSources, logViewsClient, savedObjectsClient } = createLogViewsClient(); + const { logViewFallbackHandler, logViewsClient, savedObjectsClient } = createLogViewsClient(); - infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); + logViewFallbackHandler.mockResolvedValue(basicTestSourceConfiguration); savedObjectsClient.find.mockResolvedValue({ total: 0, @@ -129,10 +98,9 @@ describe('LogViewsClient class', () => { await logViewsClient.getLogView(defaultLogViewId); - expect(infraSources.getSourceConfiguration).toHaveBeenCalledWith( - savedObjectsClient, - defaultLogViewId - ); + expect(logViewFallbackHandler).toHaveBeenCalledWith(defaultLogViewId, { + soClient: savedObjectsClient, + }); }); it('putLogView resolves the default id to a real saved object id if one exists', async () => { @@ -364,7 +332,7 @@ const createLogViewsClient = () => { const logger = loggerMock.create(); const dataViews = dataViewsServiceMock; const savedObjectsClient = savedObjectsClientMock.create(); - const infraSources = createInfraSourcesMock(); + const logViewFallbackHandler = jest.fn(); const internalLogViews = new Map(); const logViewStaticConfig: LogViewsStaticConfig = { messageFields: ['message'], @@ -374,14 +342,14 @@ const createLogViewsClient = () => { logger, Promise.resolve(dataViews), savedObjectsClient, - infraSources, + logViewFallbackHandler, internalLogViews, logViewStaticConfig ); return { dataViews, - infraSources, + logViewFallbackHandler, internalLogViews, logViewStaticConfig, logViewsClient, @@ -389,7 +357,7 @@ const createLogViewsClient = () => { }; }; -const basicTestSourceConfiguration: InfraSource = { +const basicTestSourceConfiguration = { id: 'ID', origin: 'stored', configuration: { diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_client.ts b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.ts similarity index 77% rename from x-pack/plugins/infra/server/services/log_views/log_views_client.ts rename to x-pack/plugins/logs_shared/server/services/log_views/log_views_client.ts index 3f832c677071..452bc1b3b969 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_client.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/log_views_client.ts @@ -16,7 +16,6 @@ import { import { defaultLogViewAttributes, defaultLogViewId, - LogIndexReference, LogView, LogViewAttributes, LogViewReference, @@ -26,16 +25,14 @@ import { resolveLogView, } from '../../../common/log_views'; import { decodeOrThrow } from '../../../common/runtime_types'; -import { LogIndexReference as SourceConfigurationLogIndexReference } from '../../../common/source_configuration/source_configuration'; -import type { IInfraSources, InfraSource } from '../../lib/sources'; import { extractLogViewSavedObjectReferences, logViewSavedObjectName, resolveLogViewSavedObjectReferences, } from '../../saved_objects/log_view'; import { logViewSavedObjectRT } from '../../saved_objects/log_view/types'; -import { NotFoundError } from './errors'; -import { ILogViewsClient } from './types'; +import { LogViewFallbackUnregisteredError, NotFoundError } from './errors'; +import { ILogViewsClient, LogViewFallbackHandler } from './types'; type DataViewsService = ReturnType; @@ -48,7 +45,7 @@ export class LogViewsClient implements ILogViewsClient { private readonly logger: Logger, private readonly dataViews: DataViewsService, private readonly savedObjectsClient: SavedObjectsClientContract, - private readonly infraSources: IInfraSources, + private readonly logViewFallbackHandler: LogViewFallbackHandler, private readonly internalLogViews: Map, private readonly config: LogViewsStaticConfig ) {} @@ -62,7 +59,7 @@ export class LogViewsClient implements ILogViewsClient { ) .catch((err) => err instanceof NotFoundError - ? this.getLogViewFromInfraSourceConfiguration(logViewId) + ? this.getLogViewFromLogsSharedSourceConfiguration(logViewId) : Promise.reject(err) ); } @@ -142,21 +139,16 @@ export class LogViewsClient implements ILogViewsClient { return internalLogView; } - private async getLogViewFromInfraSourceConfiguration(sourceId: string): Promise { - this.logger.debug(`Trying to load log view from source configuration "${sourceId}"...`); + private async getLogViewFromLogsSharedSourceConfiguration(sourceId: string): Promise { + this.logger.debug(`Trying to load log view from fallback configuration "${sourceId}"...`); - const sourceConfiguration = await this.infraSources.getSourceConfiguration( - this.savedObjectsClient, - sourceId - ); + if (this.logViewFallbackHandler === null) { + throw new LogViewFallbackUnregisteredError( + 'A fallback LogView handler is not registered. Register one in the setup method of your server plugin.' + ); + } - return { - id: sourceConfiguration.id, - version: sourceConfiguration.version, - updatedAt: sourceConfiguration.updatedAt, - origin: `infra-source-${sourceConfiguration.origin}`, - attributes: getAttributesFromSourceConfiguration(sourceConfiguration), - }; + return this.logViewFallbackHandler(sourceId, { soClient: this.savedObjectsClient }); } private async resolveLogViewId(logViewId: string): Promise { @@ -197,22 +189,3 @@ const getLogViewFromSavedObject = (savedObject: SavedObject): LogView = ), }; }; - -export const getAttributesFromSourceConfiguration = ({ - configuration: { name, description, logIndices, logColumns }, -}: InfraSource): LogViewAttributes => ({ - name, - description, - logIndices: getLogIndicesFromSourceConfigurationLogIndices(logIndices), - logColumns, -}); - -const getLogIndicesFromSourceConfigurationLogIndices = ( - logIndices: SourceConfigurationLogIndexReference -): LogIndexReference => - logIndices.type === 'index_pattern' - ? { - type: 'data_view', - dataViewId: logIndices.indexPatternId, - } - : logIndices; diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts b/x-pack/plugins/logs_shared/server/services/log_views/log_views_service.mock.ts similarity index 90% rename from x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts rename to x-pack/plugins/logs_shared/server/services/log_views/log_views_service.mock.ts index e472e30fae2b..295b1fd77452 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/log_views_service.mock.ts @@ -10,6 +10,8 @@ import { LogViewsServiceSetup, LogViewsServiceStart } from './types'; export const createLogViewsServiceSetupMock = (): jest.Mocked => ({ defineInternalLogView: jest.fn(), + registerLogViewFallbackHandler: jest.fn(), + setLogViewsStaticConfig: jest.fn(), }); export const createLogViewsServiceStartMock = (): jest.Mocked => ({ diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_service.ts b/x-pack/plugins/logs_shared/server/services/log_views/log_views_service.ts similarity index 65% rename from x-pack/plugins/infra/server/services/log_views/log_views_service.ts rename to x-pack/plugins/logs_shared/server/services/log_views/log_views_service.ts index cf1c7595ae5c..5479c16dff41 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_service.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/log_views_service.ts @@ -11,12 +11,25 @@ import { Logger, SavedObjectsClientContract, } from '@kbn/core/server'; -import { defaultLogViewAttributes, LogView, LogViewAttributes } from '../../../common/log_views'; +import { + defaultLogViewAttributes, + defaultLogViewsStaticConfig, + LogView, + LogViewAttributes, + LogViewsStaticConfig, +} from '../../../common/log_views'; import { LogViewsClient } from './log_views_client'; -import { LogViewsServiceSetup, LogViewsServiceStart, LogViewsServiceStartDeps } from './types'; +import { + LogViewFallbackHandler, + LogViewsServiceSetup, + LogViewsServiceStart, + LogViewsServiceStartDeps, +} from './types'; export class LogViewsService { private internalLogViews: Map = new Map(); + private logViewFallbackHandler: LogViewFallbackHandler | null = null; + private logViewsStaticConfig: LogViewsStaticConfig = defaultLogViewsStaticConfig; constructor(private readonly logger: Logger) {} @@ -24,7 +37,7 @@ export class LogViewsService { const { internalLogViews } = this; return { - defineInternalLogView(logViewId: string, logViewAttributes: Partial) { + defineInternalLogView: (logViewId: string, logViewAttributes: Partial) => { internalLogViews.set(logViewId, { id: logViewId, origin: 'internal', @@ -32,17 +45,21 @@ export class LogViewsService { updatedAt: Date.now(), }); }, + registerLogViewFallbackHandler: (handler) => { + this.logViewFallbackHandler = handler; + }, + setLogViewsStaticConfig: (config: LogViewsStaticConfig) => { + this.logViewsStaticConfig = config; + }, }; } public start({ - config, dataViews, elasticsearch, - infraSources, savedObjects, }: LogViewsServiceStartDeps): LogViewsServiceStart { - const { internalLogViews, logger } = this; + const { internalLogViews, logger, logViewFallbackHandler, logViewsStaticConfig } = this; return { getClient( @@ -54,9 +71,9 @@ export class LogViewsService { logger, dataViews.dataViewsServiceFactory(savedObjectsClient, elasticsearchClient, request), savedObjectsClient, - infraSources, + logViewFallbackHandler, internalLogViews, - config + logViewsStaticConfig ); }, getScopedClient(request: KibanaRequest) { diff --git a/x-pack/plugins/infra/server/services/log_views/types.ts b/x-pack/plugins/logs_shared/server/services/log_views/types.ts similarity index 81% rename from x-pack/plugins/infra/server/services/log_views/types.ts rename to x-pack/plugins/logs_shared/server/services/log_views/types.ts index b5f91cb3587b..db1410207c11 100644 --- a/x-pack/plugins/infra/server/services/log_views/types.ts +++ b/x-pack/plugins/logs_shared/server/services/log_views/types.ts @@ -20,18 +20,25 @@ import { LogViewsStaticConfig, ResolvedLogView, } from '../../../common/log_views'; -import { InfraSources } from '../../lib/sources'; export interface LogViewsServiceStartDeps { - config: LogViewsStaticConfig; dataViews: DataViewsServerPluginStart; elasticsearch: ElasticsearchServiceStart; - infraSources: InfraSources; savedObjects: SavedObjectsServiceStart; } +export interface LogViewFallbackHandlerOptions { + soClient: SavedObjectsClientContract; +} + +export type LogViewFallbackHandler = + | ((sourceId: string, options: LogViewFallbackHandlerOptions) => Promise) + | null; + export interface LogViewsServiceSetup { defineInternalLogView(logViewId: string, logViewAttributes: Partial): void; + registerLogViewFallbackHandler: (handler: LogViewFallbackHandler) => void; + setLogViewsStaticConfig: (config: LogViewsStaticConfig) => void; } export interface LogViewsServiceStart { diff --git a/x-pack/plugins/logs_shared/server/types.ts b/x-pack/plugins/logs_shared/server/types.ts new file mode 100644 index 000000000000..2e922eceeb18 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/types.ts @@ -0,0 +1,48 @@ +/* + * 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 { CoreSetup, RequestHandlerContext } from '@kbn/core/server'; +import { + PluginSetup as DataPluginSetup, + PluginStart as DataPluginStart, +} from '@kbn/data-plugin/server'; +import { PluginStart as DataViewsPluginStart } from '@kbn/data-views-plugin/server'; +import { LogsSharedDomainLibs } from './lib/logs_shared_types'; +import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views/types'; + +export type LogsSharedPluginCoreSetup = CoreSetup< + LogsSharedServerPluginStartDeps, + LogsSharedPluginStart +>; +export type LogsSharedPluginStartServicesAccessor = LogsSharedPluginCoreSetup['getStartServices']; + +export interface LogsSharedPluginSetup extends LogsSharedDomainLibs { + logViews: LogViewsServiceSetup; + registerUsageCollectorActions: (usageCollector: UsageCollector) => void; +} + +export interface LogsSharedPluginStart { + logViews: LogViewsServiceStart; +} + +export interface LogsSharedServerPluginSetupDeps { + data: DataPluginSetup; +} + +export interface LogsSharedServerPluginStartDeps { + data: DataPluginStart; + dataViews: DataViewsPluginStart; +} + +export interface UsageCollector { + countLogs?: () => void; +} + +/** + * @internal + */ +export type LogsSharedPluginRequestHandlerContext = RequestHandlerContext; diff --git a/x-pack/plugins/logs_shared/server/utils/elasticsearch_runtime_types.ts b/x-pack/plugins/logs_shared/server/utils/elasticsearch_runtime_types.ts new file mode 100644 index 000000000000..e2dbf02ae2d0 --- /dev/null +++ b/x-pack/plugins/logs_shared/server/utils/elasticsearch_runtime_types.ts @@ -0,0 +1,41 @@ +/* + * 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 rt from 'io-ts'; + +export const shardFailureRT = rt.partial({ + index: rt.union([rt.string, rt.null]), + node: rt.union([rt.string, rt.null]), + reason: rt.partial({ + reason: rt.union([rt.string, rt.null]), + type: rt.union([rt.string, rt.null]), + }), + shard: rt.number, +}); + +export type ShardFailure = rt.TypeOf; + +export const commonSearchSuccessResponseFieldsRT = rt.type({ + _shards: rt.intersection([ + rt.type({ + total: rt.number, + successful: rt.number, + skipped: rt.number, + failed: rt.number, + }), + rt.partial({ + failures: rt.array(shardFailureRT), + }), + ]), + timed_out: rt.boolean, + took: rt.number, +}); + +export const commonHitFieldsRT = rt.type({ + _index: rt.string, + _id: rt.string, +}); diff --git a/x-pack/plugins/logs_shared/server/utils/serialized_query.ts b/x-pack/plugins/logs_shared/server/utils/serialized_query.ts new file mode 100644 index 000000000000..b3b2569528ae --- /dev/null +++ b/x-pack/plugins/logs_shared/server/utils/serialized_query.ts @@ -0,0 +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. + */ + +import { JsonObject } from '@kbn/utility-types'; + +export const parseFilterQuery = ( + filterQuery: string | null | undefined +): JsonObject | undefined => { + try { + if (filterQuery) { + const parsedFilterQuery = JSON.parse(filterQuery); + if ( + !parsedFilterQuery || + ['string', 'number', 'boolean'].includes(typeof parsedFilterQuery) || + Array.isArray(parsedFilterQuery) + ) { + throw new Error('expected value to be an object'); + } + return parsedFilterQuery; + } else { + return undefined; + } + } catch (err) { + throw new Error(`Failed to parse query: ${err}`); + } +}; diff --git a/x-pack/plugins/infra/server/utils/typed_search_strategy.ts b/x-pack/plugins/logs_shared/server/utils/typed_search_strategy.ts similarity index 100% rename from x-pack/plugins/infra/server/utils/typed_search_strategy.ts rename to x-pack/plugins/logs_shared/server/utils/typed_search_strategy.ts diff --git a/x-pack/plugins/logs_shared/tsconfig.json b/x-pack/plugins/logs_shared/tsconfig.json new file mode 100644 index 000000000000..31c446661597 --- /dev/null +++ b/x-pack/plugins/logs_shared/tsconfig.json @@ -0,0 +1,31 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": ["../../../typings/**/*", "common/**/*", "public/**/*", "server/**/*", "types/**/*"], + "exclude": ["target/**/*"], + "kbn_references": [ + "@kbn/core", + "@kbn/i18n", + "@kbn/i18n-react", + "@kbn/data-views-plugin", + "@kbn/io-ts-utils", + "@kbn/data-plugin", + "@kbn/kibana-utils-plugin", + "@kbn/es-query", + "@kbn/utility-types", + "@kbn/core-http-server", + "@kbn/logging", + "@kbn/config-schema", + "@kbn/std", + "@kbn/logging-mocks", + "@kbn/kibana-react-plugin", + "@kbn/test-subj-selector", + "@kbn/observability-shared-plugin", + "@kbn/observability-plugin", + "@kbn/datemath", + "@kbn/core-http-browser", + "@kbn/ui-actions-plugin", + ] +} diff --git a/x-pack/plugins/monitoring/kibana.jsonc b/x-pack/plugins/monitoring/kibana.jsonc index 236e6390d1bc..8da632c4b7d6 100644 --- a/x-pack/plugins/monitoring/kibana.jsonc +++ b/x-pack/plugins/monitoring/kibana.jsonc @@ -19,6 +19,7 @@ ], "optionalPlugins": [ "infra", + "logsShared", "usageCollection", "home", "cloud", diff --git a/x-pack/plugins/monitoring/server/lib/logs/init_infra_source.ts b/x-pack/plugins/monitoring/server/lib/logs/init_log_view.ts similarity index 74% rename from x-pack/plugins/monitoring/server/lib/logs/init_infra_source.ts rename to x-pack/plugins/monitoring/server/lib/logs/init_log_view.ts index 43fb8f7cc5db..52b0f4364738 100644 --- a/x-pack/plugins/monitoring/server/lib/logs/init_infra_source.ts +++ b/x-pack/plugins/monitoring/server/lib/logs/init_log_view.ts @@ -5,20 +5,20 @@ * 2.0. */ -import { InfraPluginSetup } from '@kbn/infra-plugin/server'; +import { LogsSharedPluginSetup } from '@kbn/logs-shared-plugin/server'; import { CCS_REMOTE_PATTERN, INFRA_SOURCE_ID } from '../../../common/constants'; import { MonitoringConfig } from '../../config'; import { getIndexPatterns } from '../cluster/get_index_patterns'; -export const initInfraSource = (config: MonitoringConfig, infraPlugin: InfraPluginSetup) => { - if (infraPlugin) { +export const initLogView = (config: MonitoringConfig, logsShared: LogsSharedPluginSetup) => { + if (logsShared) { const logsIndexPattern = getIndexPatterns({ config, type: 'logs', ccs: CCS_REMOTE_PATTERN, }); - infraPlugin.logViews.defineInternalLogView(INFRA_SOURCE_ID, { + logsShared.logViews.defineInternalLogView(INFRA_SOURCE_ID, { name: 'Elastic Stack Logs', logIndices: { type: 'index_name', diff --git a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts index b743e0300aef..e7ffe701fbd1 100644 --- a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts +++ b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts @@ -7,6 +7,7 @@ import { featuresPluginMock } from '@kbn/features-plugin/server/mocks'; import { infraPluginMock } from '@kbn/infra-plugin/server/mocks'; +import { logsSharedPluginMock } from '@kbn/logs-shared-plugin/server/mocks'; import { loggerMock } from '@kbn/logging-mocks'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks'; import { configSchema, createConfig } from '../../../config'; @@ -38,6 +39,7 @@ const mockReq = ( usageCollection: usageCollectionSetup, features: featuresPluginMock.createSetup(), infra: infraPluginMock.createSetupContract(), + logsShared: logsSharedPluginMock.createSetupContract(), }, }, }, diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 5996eb125b6d..174785511296 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -35,7 +35,7 @@ import { configSchema, createConfig, MonitoringConfig } from './config'; import { instantiateClient } from './es_client/instantiate_client'; import { initBulkUploader } from './kibana_monitoring'; import { registerCollectors } from './kibana_monitoring/collectors'; -import { initInfraSource } from './lib/logs/init_infra_source'; +import { initLogView } from './lib/logs/init_log_view'; import { LicenseService } from './license_service'; import { requireUIRoutes } from './routes'; import { EndpointTypes, Globals } from './static_globals'; @@ -202,7 +202,7 @@ export class MonitoringPlugin alerting: plugins.alerting, logger: this.log, }); - initInfraSource(config, plugins.infra); + initLogView(config, plugins.logsShared); } } diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 64931f588851..7e056cbac5fb 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -35,6 +35,7 @@ import { PluginSetupContract as FeaturesPluginSetupContract } from '@kbn/feature import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { CloudSetup } from '@kbn/cloud-plugin/server'; import { RouteConfig, RouteMethod, Headers } from '@kbn/core/server'; +import { LogsSharedPluginSetup } from '@kbn/logs-shared-plugin/server'; import { ElasticsearchModifiedSource } from '../common/types/es'; import { RulesByType } from '../common/types/alerts'; import { configSchema, MonitoringConfig } from './config'; @@ -56,6 +57,7 @@ export interface PluginsSetup { alerting?: AlertingPluginSetupContract; infra: InfraPluginSetup; cloud?: CloudSetup; + logsShared: LogsSharedPluginSetup; } export type RequestHandlerContextMonitoringPlugin = CustomRequestHandlerContext<{ diff --git a/x-pack/plugins/monitoring/tsconfig.json b/x-pack/plugins/monitoring/tsconfig.json index 5ebd037e0464..00ca96256814 100644 --- a/x-pack/plugins/monitoring/tsconfig.json +++ b/x-pack/plugins/monitoring/tsconfig.json @@ -41,6 +41,7 @@ "@kbn/shared-ux-router", "@kbn/observability-shared-plugin", "@kbn/shared-ux-link-redirect-app", + "@kbn/logs-shared-plugin", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 1f9d308c783f..29c1982ae0d3 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -18117,7 +18117,6 @@ "xpack.infra.analysisSetup.indicesSelectionIndexNotFound": "Aucun index ne correspond au modèle {index}", "xpack.infra.analysisSetup.indicesSelectionNoTimestampField": "Il manque un champ {field} obligatoire dans au moins un index correspondant à {index}.", "xpack.infra.analysisSetup.indicesSelectionTimestampNotValid": "Au moins un index correspondant à {index} comprend un champ appelé {field} sans le type correct.", - "xpack.infra.dataSearch.shardFailureErrorMessage": "Index {indexName} : {errorMessage}", "xpack.infra.deprecations.containerAdjustIndexing": "Ajustez votre indexation pour identifier les conteneurs Docker utilisant \"{field}\"", "xpack.infra.deprecations.deprecatedFieldConfigDescription": "La configuration de \"xpack.infra.sources.default.fields.{fieldKey}\" a été déclassée et sera retirée dans la version 8.0.0.", "xpack.infra.deprecations.deprecatedFieldConfigTitle": "\"{fieldKey}\" est déclassé.", @@ -18138,8 +18137,6 @@ "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId} n'existe pas.", "xpack.infra.linkTo.hostWithIp.error": "Hôte avec l'adresse IP \"{hostIp}\" introuvable.", "xpack.infra.linkTo.hostWithIp.loading": "Chargement de l'hôte avec l'adresse IP \"{hostIp}\" en cours.", - "xpack.infra.logFlyout.flyoutSubTitle": "À partir de l'index {indexName}", - "xpack.infra.logFlyout.flyoutTitle": "Détails de l'entrée de log {logEntryId}", "xpack.infra.logs.alertDetails.chart.chartTitle": "Logs pour {criteria}", "xpack.infra.logs.alertFlyout.groupByOptimizationWarning": "Lors de la définition d'une valeur \"regrouper par\", nous recommandons fortement d'utiliser le comparateur \"{comparator}\" pour votre seuil. Cela peut permettre d'améliorer considérablement les performances.", "xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription": "{actualCount, plural, one {la {actualCount} dernière entrée de log} many {les {actualCount} dernières entrées de log} other {les {actualCount} dernières entrées de log}} dans {duration} pour {groupName}. Alerte lorsque {comparator} {expectedCount}.", @@ -18157,22 +18154,11 @@ "xpack.infra.logs.analysis.mlUnavailableBody": "Pour en savoir plus, consultez {machineLearningAppLink}.", "xpack.infra.logs.common.invalidStateMessage": "Impossible de traiter l'état {stateValue}.", "xpack.infra.logs.customizeLogs.textSizeRadioGroup": "{textScale, select, small {Petite} medium {Moyenne} large {Large} other {{textScale}}}", - "xpack.infra.logs.extendTimeframeByDaysButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {jour} many {jours} other {jours}}", - "xpack.infra.logs.extendTimeframeByHoursButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {heure} many {heures} other {heures}}", - "xpack.infra.logs.extendTimeframeByMillisecondsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {milliseconde} many {millisecondes} other {millisecondes}}", - "xpack.infra.logs.extendTimeframeByMinutesButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {minute} many {minutes} other {minutes}}", - "xpack.infra.logs.extendTimeframeByMonthsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {mois} many {mois} other {mois}}", - "xpack.infra.logs.extendTimeframeBySecondsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {seconde} many {secondes} other {secondes}}", - "xpack.infra.logs.extendTimeframeByWeeksButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {semaine} many {semaines} other {semaines}}", - "xpack.infra.logs.extendTimeframeByYearsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {an} many {années} other {années}}", - "xpack.infra.logs.lastUpdate": "Dernière mise à jour {timestamp}", "xpack.infra.logs.logEntryCategories.manyCategoriesWarningReasonDescription": "Le rapport de catégories par document analysé est très élevé avec {categoriesDocumentRatio, number}.", "xpack.infra.logs.logEntryCategories.manyDeadCategoriesWarningReasonDescription": "Aucun nouveau message n'est attribué à {deadCategoriesRatio, number, percent} des catégories, car des catégories moins spécifiques les masquent.", "xpack.infra.logs.logEntryCategories.manyRareCategoriesWarningReasonDescription": "Les messages sont rarement attribués à {rareCategoriesRatio, number, percent} des catégories.", "xpack.infra.logs.logEntryCategories.truncatedPatternSegmentDescription": "{extraSegmentCount, plural, one {un autre segment} many {# autres segments} other {# autres segments}}", "xpack.infra.logs.searchResultTooltip": "{bucketCount, plural, one {# entrée mise en surbrillance} many {# entrées mises en surbrillance} other {# entrées mises en surbrillance}}", - "xpack.infra.logs.showingEntriesFromTimestamp": "Affichage des entrées à partir de {timestamp}", - "xpack.infra.logs.showingEntriesUntilTimestamp": "Affichage des entrées jusqu'à {timestamp}", "xpack.infra.logs.viewInContext.logsFromContainerTitle": "Les logs affichés proviennent du conteneur {container}", "xpack.infra.logs.viewInContext.logsFromFileTitle": "Les logs affichés proviennent du fichier {file} et de l'hôte {host}", "xpack.infra.logSourceConfiguration.invalidMessageFieldTypeErrorMessage": "Le champ {messageField} doit être un champ textuel.", @@ -18283,9 +18269,6 @@ "xpack.infra.chartSection.notEnoughDataPointsToRenderTitle": "Données insuffisantes", "xpack.infra.common.tabBetaBadgeLabel": "Version bêta", "xpack.infra.configureSourceActionLabel": "Modifier la configuration de la source", - "xpack.infra.dataSearch.abortedRequestErrorMessage": "La demande a été annulée.", - "xpack.infra.dataSearch.cancelButtonLabel": "Annuler la demande", - "xpack.infra.dataSearch.loadingErrorRetryButtonLabel": "Réessayer", "xpack.infra.deprecations.containerIdFieldName": "ID de conteneur", "xpack.infra.deprecations.containerIdFieldTitle": "Le champ de configuration source \"ID de conteneur\" est déclassé.", "xpack.infra.deprecations.hostnameFieldName": "nom d'hôte", @@ -18439,18 +18422,7 @@ "xpack.infra.legendControls.stepsLabel": "Nombre de couleurs", "xpack.infra.legendControls.switchLabel": "Calculer automatiquement la plage", "xpack.infra.legnedControls.boundRangeError": "La valeur minimale doit être inférieure à la valeur maximale", - "xpack.infra.lobs.logEntryActionsViewInContextButton": "Afficher en contexte", "xpack.infra.logAnomalies.logEntryExamplesMenuLabel": "Afficher les actions de l'entrée du log", - "xpack.infra.logEntryActionsMenu.apmActionLabel": "Afficher dans APM", - "xpack.infra.logEntryActionsMenu.buttonLabel": "Examiner", - "xpack.infra.logEntryActionsMenu.uptimeActionLabel": "Afficher le statut dans Uptime", - "xpack.infra.logEntryItemView.logEntryActionsMenuToolTip": "Afficher les actions de la ligne", - "xpack.infra.logFlyout.fieldColumnLabel": "Champ", - "xpack.infra.logFlyout.filterAriaLabel": "Filtre", - "xpack.infra.logFlyout.loadingErrorCalloutTitle": "Erreur lors de la recherche de l'entrée de log", - "xpack.infra.logFlyout.loadingMessage": "Recherche de l'entrée de log dans les partitions", - "xpack.infra.logFlyout.setFilterTooltip": "Afficher l'événement avec filtre", - "xpack.infra.logFlyout.valueColumnLabel": "Valeur", "xpack.infra.logs.alertDetails.chart.ratioTitle": "Ratio de Requête A à Requête B", "xpack.infra.logs.alertDetails.chartAnnotation.alertStarted": "Alerte démarrée", "xpack.infra.logs.alertDetails.chartHistory.alertsTriggered": "Alertes déclenchées", @@ -18569,9 +18541,6 @@ "xpack.infra.logs.customizeLogs.lineWrappingFormRowLabel": "Retour automatique à la ligne", "xpack.infra.logs.customizeLogs.textSizeFormRowLabel": "Taille du texte", "xpack.infra.logs.customizeLogs.wrapLongLinesSwitchLabel": "Formater les longues lignes", - "xpack.infra.logs.emptyView.checkForNewDataButtonLabel": "Rechercher de nouvelles données", - "xpack.infra.logs.emptyView.noLogMessageDescription": "Essayez d'ajuster votre filtre.", - "xpack.infra.logs.emptyView.noLogMessageTitle": "Il n'y a aucun message de log à afficher.", "xpack.infra.logs.highlights.clearHighlightTermsButtonLabel": "Effacer les termes à mettre en surbrillance", "xpack.infra.logs.highlights.goToNextHighlightButtonLabel": "Passer au surlignage suivant", "xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel": "Passer au surlignage précédent", @@ -18581,10 +18550,7 @@ "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "Catégories", "xpack.infra.logs.index.settingsTabTitle": "Paramètres", "xpack.infra.logs.index.streamTabTitle": "Flux", - "xpack.infra.logs.jumpToTailText": "Passer aux entrées les plus récentes", - "xpack.infra.logs.loadingNewEntriesText": "Chargement des nouvelles entrées", "xpack.infra.logs.logCategoriesTitle": "Catégories", - "xpack.infra.logs.logEntryActionsDetailsButton": "Afficher les détails", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlButtonLabel": "Analyse dans ML", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlTooltipDescription": "Analysez cette catégorie dans l'application ML.", "xpack.infra.logs.logEntryCategories.categoryColumnTitle": "Catégorie", @@ -18615,7 +18581,6 @@ "xpack.infra.logs.noDataConfig.beatsCard.title": "Ajouter une intégration au logging", "xpack.infra.logs.noDataConfig.solutionName": "Observabilité", "xpack.infra.logs.pluginTitle": "Logs", - "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "Chargement des entrées", "xpack.infra.logs.search.nextButtonLabel": "Suivant", "xpack.infra.logs.search.previousButtonLabel": "Précédent", "xpack.infra.logs.search.searchInLogsAriaLabel": "rechercher", @@ -18625,10 +18590,6 @@ "xpack.infra.logs.settings.inlineLogViewCalloutTitle": "Vue de log en ligne utilisée", "xpack.infra.logs.startStreamingButtonLabel": "Diffuser en direct", "xpack.infra.logs.stopStreamingButtonLabel": "Arrêter la diffusion", - "xpack.infra.logs.stream.messageColumnTitle": "Message", - "xpack.infra.logs.stream.timestampColumnTitle": "Horodatage", - "xpack.infra.logs.streamingNewEntriesText": "Diffusion de nouvelles entrées", - "xpack.infra.logs.streamLive": "Diffuser en direct", "xpack.infra.logs.streamPageTitle": "Flux", "xpack.infra.logsHeaderAddDataButtonLabel": "Ajouter des données", "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "L'état d'au moins un champ du formulaire est non valide.", @@ -18655,8 +18616,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "Réessayer", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "Recherche d'entrées de log… (par ex. host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "Erreur de filtrage du log", - "xpack.infra.logStream.kqlErrorTitle": "Expression KQL non valide", - "xpack.infra.logStream.unknownErrorTitle": "Une erreur s'est produite", "xpack.infra.logStreamEmbeddable.description": "Ajoutez un tableau de logs de diffusion en direct.", "xpack.infra.logStreamEmbeddable.displayName": "Flux de log", "xpack.infra.logStreamEmbeddable.title": "Flux de log", @@ -19283,6 +19242,47 @@ "xpack.infra.waffle.unableToSelectMetricErrorTitle": "Impossible de sélectionner les options ou la valeur pour l'indicateur.", "xpack.infra.waffleTime.autoRefreshButtonLabel": "Actualisation automatique", "xpack.infra.waffleTime.stopRefreshingButtonLabel": "Arrêter l'actualisation", + "xpack.logsShared.dataSearch.shardFailureErrorMessage": "Index {indexName} : {errorMessage}", + "xpack.logsShared.logFlyout.flyoutSubTitle": "À partir de l'index {indexName}", + "xpack.logsShared.logFlyout.flyoutTitle": "Détails de l'entrée de log {logEntryId}", + "xpack.logsShared.logs.extendTimeframeByDaysButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {jour} many {jours} other {jours}}", + "xpack.logsShared.logs.extendTimeframeByHoursButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {heure} many {heures} other {heures}}", + "xpack.logsShared.logs.extendTimeframeByMillisecondsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {milliseconde} many {millisecondes} other {millisecondes}}", + "xpack.logsShared.logs.extendTimeframeByMinutesButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {minute} many {minutes} other {minutes}}", + "xpack.logsShared.logs.extendTimeframeByMonthsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {mois} many {mois} other {mois}}", + "xpack.logsShared.logs.extendTimeframeBySecondsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {seconde} many {secondes} other {secondes}}", + "xpack.logsShared.logs.extendTimeframeByWeeksButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {semaine} many {semaines} other {semaines}}", + "xpack.logsShared.logs.extendTimeframeByYearsButton": "Étendre le délai d'exécution de {amount, number} {amount, plural, one {an} many {années} other {années}}", + "xpack.logsShared.logs.lastUpdate": "Dernière mise à jour {timestamp}", + "xpack.logsShared.logs.showingEntriesFromTimestamp": "Affichage des entrées à partir de {timestamp}", + "xpack.logsShared.logs.showingEntriesUntilTimestamp": "Affichage des entrées jusqu'à {timestamp}", + "xpack.logsShared.dataSearch.abortedRequestErrorMessage": "La demande a été annulée.", + "xpack.logsShared.dataSearch.cancelButtonLabel": "Annuler la demande", + "xpack.logsShared.dataSearch.loadingErrorRetryButtonLabel": "Réessayer", + "xpack.logsShared.lobs.logEntryActionsViewInContextButton": "Afficher en contexte", + "xpack.logsShared.logEntryActionsMenu.apmActionLabel": "Afficher dans APM", + "xpack.logsShared.logEntryActionsMenu.buttonLabel": "Examiner", + "xpack.logsShared.logEntryActionsMenu.uptimeActionLabel": "Afficher le statut dans Uptime", + "xpack.logsShared.logEntryItemView.logEntryActionsMenuToolTip": "Afficher les actions de la ligne", + "xpack.logsShared.logFlyout.fieldColumnLabel": "Champ", + "xpack.logsShared.logFlyout.filterAriaLabel": "Filtre", + "xpack.logsShared.logFlyout.loadingErrorCalloutTitle": "Erreur lors de la recherche de l'entrée de log", + "xpack.logsShared.logFlyout.loadingMessage": "Recherche de l'entrée de log dans les partitions", + "xpack.logsShared.logFlyout.setFilterTooltip": "Afficher l'événement avec filtre", + "xpack.logsShared.logFlyout.valueColumnLabel": "Valeur", + "xpack.logsShared.logs.emptyView.checkForNewDataButtonLabel": "Rechercher de nouvelles données", + "xpack.logsShared.logs.emptyView.noLogMessageDescription": "Essayez d'ajuster votre filtre.", + "xpack.logsShared.logs.emptyView.noLogMessageTitle": "Il n'y a aucun message de log à afficher.", + "xpack.logsShared.logs.jumpToTailText": "Passer aux entrées les plus récentes", + "xpack.logsShared.logs.loadingNewEntriesText": "Chargement des nouvelles entrées", + "xpack.logsShared.logs.logEntryActionsDetailsButton": "Afficher les détails", + "xpack.logsShared.logs.scrollableLogTextStreamView.loadingEntriesLabel": "Chargement des entrées", + "xpack.logsShared.logs.stream.messageColumnTitle": "Message", + "xpack.logsShared.logs.stream.timestampColumnTitle": "Horodatage", + "xpack.logsShared.logs.streamingNewEntriesText": "Diffusion de nouvelles entrées", + "xpack.logsShared.logs.streamLive": "Diffuser en direct", + "xpack.logsShared.logStream.kqlErrorTitle": "Expression KQL non valide", + "xpack.logsShared.logStream.unknownErrorTitle": "Une erreur s'est produite", "xpack.ingestPipelines.app.deniedPrivilegeDescription": "Pour utiliser l'option Ingérer des pipelines, vous devez avoir {privilegesCount, plural, one {ce privilège de cluster} many {ces privilèges de cluster} other {ces privilèges de cluster}} : {missingPrivileges}.", "xpack.ingestPipelines.clone.loadSourcePipelineErrorTitle": "Impossible de charger {name}.", "xpack.ingestPipelines.createFromCsv.errorMessage": "{message}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 602616c9ffac..2b610e8af95c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -18116,7 +18116,6 @@ "xpack.infra.analysisSetup.indicesSelectionIndexNotFound": "インデックスがパターン{index}と一致しません", "xpack.infra.analysisSetup.indicesSelectionNoTimestampField": "{index}と一致する1つ以上のインデックスに、必須フィールド{field}がありません。", "xpack.infra.analysisSetup.indicesSelectionTimestampNotValid": "{index}と一致する1つ以上のインデックスに、正しい型がない{field}フィールドがあります。", - "xpack.infra.dataSearch.shardFailureErrorMessage": "インデックス{indexName}:{errorMessage}", "xpack.infra.deprecations.containerAdjustIndexing": "インデックスを調整し、\"{field}\"を使用してDockerコンテナーを特定", "xpack.infra.deprecations.deprecatedFieldConfigDescription": "「xpack.infra.sources.default.fields.{fieldKey}」の構成は廃止予定であり、8.0.0で削除されます。", "xpack.infra.deprecations.deprecatedFieldConfigTitle": "\"{fieldKey}\"は廃止予定です。", @@ -18137,8 +18136,6 @@ "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId}は存在しません。", "xpack.infra.linkTo.hostWithIp.error": "IPアドレス「{hostIp}」でホストが見つかりません。", "xpack.infra.linkTo.hostWithIp.loading": "IPアドレス「{hostIp}」のホストを読み込み中です。", - "xpack.infra.logFlyout.flyoutSubTitle": "インデックス{indexName}から", - "xpack.infra.logFlyout.flyoutTitle": "ログエントリ{logEntryId}の詳細", "xpack.infra.logs.alertDetails.chart.chartTitle": "{criteria} のログ", "xpack.infra.logs.alertFlyout.groupByOptimizationWarning": "「group by」を設定するときには、しきい値で\"{comparator}\"比較演算子を使用することを強くお勧めします。これにより、パフォーマンスを大きく改善できます。", "xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription": "{groupName}の過去{duration}の{actualCount, plural, other {{actualCount}件のログエントリ}}。{comparator} {expectedCount}のときにアラートを通知します。", @@ -18156,22 +18153,11 @@ "xpack.infra.logs.analysis.mlUnavailableBody": "詳細は {machineLearningAppLink} をご覧ください。", "xpack.infra.logs.common.invalidStateMessage": "状態{stateValue}を処理できません。", "xpack.infra.logs.customizeLogs.textSizeRadioGroup": "{textScale, select, small {小} medium {中} large {大} other {{textScale}}}", - "xpack.infra.logs.extendTimeframeByDaysButton": "タイムフレームを{amount, number}{amount, plural, other {日}}延長", - "xpack.infra.logs.extendTimeframeByHoursButton": "タイムフレームを{amount, number}{amount, plural, other {時間}}延長", - "xpack.infra.logs.extendTimeframeByMillisecondsButton": "タイムフレームを{amount, number}{amount, plural, other {ミリ秒}}延長", - "xpack.infra.logs.extendTimeframeByMinutesButton": "タイムフレームを{amount, number}{amount, plural, other {分}}延長", - "xpack.infra.logs.extendTimeframeByMonthsButton": "タイムフレームを{amount, number}{amount, plural, other {月}}延長", - "xpack.infra.logs.extendTimeframeBySecondsButton": "タイムフレームを{amount, number}{amount, plural, other {秒}}延長", - "xpack.infra.logs.extendTimeframeByWeeksButton": "タイムフレームを{amount, number}{amount, plural, other {週}}延長", - "xpack.infra.logs.extendTimeframeByYearsButton": "タイムフレームを{amount, number}{amount, plural, other {年}}延長", - "xpack.infra.logs.lastUpdate": "最終更新:{timestamp}", "xpack.infra.logs.logEntryCategories.manyCategoriesWarningReasonDescription": "分析されたドキュメントごとのカテゴリ比率が{categoriesDocumentRatio, number}で、非常に高い値です。", "xpack.infra.logs.logEntryCategories.manyDeadCategoriesWarningReasonDescription": "特定のカテゴリが少ないことで、目立たなくなるため、{deadCategoriesRatio, number, percent}のカテゴリには新しいメッセージが割り当てられません。", "xpack.infra.logs.logEntryCategories.manyRareCategoriesWarningReasonDescription": "{rareCategoriesRatio, number, percent}のカテゴリには、ほとんどメッセージが割り当てられません。", "xpack.infra.logs.logEntryCategories.truncatedPatternSegmentDescription": "{extraSegmentCount, plural, other {#個の追加のセグメント}}", "xpack.infra.logs.searchResultTooltip": "{bucketCount, plural, other {#個のハイライトされたエントリ}}", - "xpack.infra.logs.showingEntriesFromTimestamp": "{timestamp}以降のエントリーを表示中", - "xpack.infra.logs.showingEntriesUntilTimestamp": "{timestamp}までのエントリーを表示中", "xpack.infra.logs.viewInContext.logsFromContainerTitle": "表示されたログはコンテナー{container}から取得されました", "xpack.infra.logs.viewInContext.logsFromFileTitle": "表示されたログは、ファイル{file}およびホスト{host}から取得されました", "xpack.infra.logSourceConfiguration.invalidMessageFieldTypeErrorMessage": "{messageField}フィールドはテキストフィールドでなければなりません。", @@ -18282,9 +18268,6 @@ "xpack.infra.chartSection.notEnoughDataPointsToRenderTitle": "データが不十分です", "xpack.infra.common.tabBetaBadgeLabel": "ベータ", "xpack.infra.configureSourceActionLabel": "ソース構成を変更", - "xpack.infra.dataSearch.abortedRequestErrorMessage": "リクエストが中断されましたか。", - "xpack.infra.dataSearch.cancelButtonLabel": "リクエストのキャンセル", - "xpack.infra.dataSearch.loadingErrorRetryButtonLabel": "再試行", "xpack.infra.deprecations.containerIdFieldName": "コンテナーID", "xpack.infra.deprecations.containerIdFieldTitle": "ソース構成フィールド[コンテナーID]は廃止予定です。", "xpack.infra.deprecations.hostnameFieldName": "ホスト名", @@ -18438,18 +18421,7 @@ "xpack.infra.legendControls.stepsLabel": "色の数", "xpack.infra.legendControls.switchLabel": "自動計算範囲", "xpack.infra.legnedControls.boundRangeError": "最小値は最大値よりも小さくなければなりません", - "xpack.infra.lobs.logEntryActionsViewInContextButton": "コンテキストで表示", "xpack.infra.logAnomalies.logEntryExamplesMenuLabel": "ログエントリのアクションを表示", - "xpack.infra.logEntryActionsMenu.apmActionLabel": "APMで表示", - "xpack.infra.logEntryActionsMenu.buttonLabel": "調査", - "xpack.infra.logEntryActionsMenu.uptimeActionLabel": "監視ステータスを表示", - "xpack.infra.logEntryItemView.logEntryActionsMenuToolTip": "行のアクションを表示", - "xpack.infra.logFlyout.fieldColumnLabel": "フィールド", - "xpack.infra.logFlyout.filterAriaLabel": "フィルター", - "xpack.infra.logFlyout.loadingErrorCalloutTitle": "ログエントリの検索中のエラー", - "xpack.infra.logFlyout.loadingMessage": "シャードのログエントリを検索しています", - "xpack.infra.logFlyout.setFilterTooltip": "フィルターでイベントを表示", - "xpack.infra.logFlyout.valueColumnLabel": "値", "xpack.infra.logs.alertDetails.chart.ratioTitle": "クエリAとクエリBの比率", "xpack.infra.logs.alertDetails.chartAnnotation.alertStarted": "アラートが開始しました", "xpack.infra.logs.alertDetails.chartHistory.alertsTriggered": "アラートがトリガーされました", @@ -18568,9 +18540,6 @@ "xpack.infra.logs.customizeLogs.lineWrappingFormRowLabel": "改行", "xpack.infra.logs.customizeLogs.textSizeFormRowLabel": "テキストサイズ", "xpack.infra.logs.customizeLogs.wrapLongLinesSwitchLabel": "長い行を改行", - "xpack.infra.logs.emptyView.checkForNewDataButtonLabel": "新規データを確認", - "xpack.infra.logs.emptyView.noLogMessageDescription": "フィルターを調整してみてください。", - "xpack.infra.logs.emptyView.noLogMessageTitle": "表示するログメッセージがありません。", "xpack.infra.logs.highlights.clearHighlightTermsButtonLabel": "ハイライトする用語をクリア", "xpack.infra.logs.highlights.goToNextHighlightButtonLabel": "次のハイライトにスキップ", "xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel": "前のハイライトにスキップ", @@ -18580,10 +18549,7 @@ "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "カテゴリー", "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", - "xpack.infra.logs.jumpToTailText": "最も新しいエントリーに移動", - "xpack.infra.logs.loadingNewEntriesText": "新しいエントリーを読み込み中", "xpack.infra.logs.logCategoriesTitle": "カテゴリー", - "xpack.infra.logs.logEntryActionsDetailsButton": "詳細を表示", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlButtonLabel": "ML で分析", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlTooltipDescription": "ML アプリでこのカテゴリーを分析します。", "xpack.infra.logs.logEntryCategories.categoryColumnTitle": "カテゴリー", @@ -18614,7 +18580,6 @@ "xpack.infra.logs.noDataConfig.beatsCard.title": "ロギング統合を追加", "xpack.infra.logs.noDataConfig.solutionName": "Observability", "xpack.infra.logs.pluginTitle": "ログ", - "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "エントリーを読み込み中", "xpack.infra.logs.search.nextButtonLabel": "次へ", "xpack.infra.logs.search.previousButtonLabel": "前へ", "xpack.infra.logs.search.searchInLogsAriaLabel": "検索", @@ -18624,10 +18589,6 @@ "xpack.infra.logs.settings.inlineLogViewCalloutTitle": "使用中のインラインログビュー", "xpack.infra.logs.startStreamingButtonLabel": "ライブストリーム", "xpack.infra.logs.stopStreamingButtonLabel": "ストリーム停止", - "xpack.infra.logs.stream.messageColumnTitle": "メッセージ", - "xpack.infra.logs.stream.timestampColumnTitle": "タイムスタンプ", - "xpack.infra.logs.streamingNewEntriesText": "新しいエントリーをストリーム中", - "xpack.infra.logs.streamLive": "ライブストリーム", "xpack.infra.logs.streamPageTitle": "ストリーム", "xpack.infra.logsHeaderAddDataButtonLabel": "データの追加", "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "1つ以上のフォームフィールドが無効な状態です。", @@ -18654,8 +18615,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "再試行", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "ログエントリーを検索中…(例:host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "ログフィルターエラー", - "xpack.infra.logStream.kqlErrorTitle": "無効なKQL式", - "xpack.infra.logStream.unknownErrorTitle": "エラーが発生しました", "xpack.infra.logStreamEmbeddable.description": "ライブストリーミングログのテーブルを追加します。", "xpack.infra.logStreamEmbeddable.displayName": "ログストリーム", "xpack.infra.logStreamEmbeddable.title": "ログストリーム", @@ -19282,6 +19241,47 @@ "xpack.infra.waffle.unableToSelectMetricErrorTitle": "メトリックのオプションまたは値を選択できません。", "xpack.infra.waffleTime.autoRefreshButtonLabel": "自動更新", "xpack.infra.waffleTime.stopRefreshingButtonLabel": "更新中止", + "xpack.logsShared.dataSearch.shardFailureErrorMessage": "インデックス{indexName}:{errorMessage}", + "xpack.logsShared.logFlyout.flyoutSubTitle": "インデックス{indexName}から", + "xpack.logsShared.logFlyout.flyoutTitle": "ログエントリ{logEntryId}の詳細", + "xpack.logsShared.logs.extendTimeframeByDaysButton": "タイムフレームを{amount, number}{amount, plural, other {日}}延長", + "xpack.logsShared.logs.extendTimeframeByHoursButton": "タイムフレームを{amount, number}{amount, plural, other {時間}}延長", + "xpack.logsShared.logs.extendTimeframeByMillisecondsButton": "タイムフレームを{amount, number}{amount, plural, other {ミリ秒}}延長", + "xpack.logsShared.logs.extendTimeframeByMinutesButton": "タイムフレームを{amount, number}{amount, plural, other {分}}延長", + "xpack.logsShared.logs.extendTimeframeByMonthsButton": "タイムフレームを{amount, number}{amount, plural, other {月}}延長", + "xpack.logsShared.logs.extendTimeframeBySecondsButton": "タイムフレームを{amount, number}{amount, plural, other {秒}}延長", + "xpack.logsShared.logs.extendTimeframeByWeeksButton": "タイムフレームを{amount, number}{amount, plural, other {週}}延長", + "xpack.logsShared.logs.extendTimeframeByYearsButton": "タイムフレームを{amount, number}{amount, plural, other {年}}延長", + "xpack.logsShared.logs.lastUpdate": "最終更新:{timestamp}", + "xpack.logsShared.logs.showingEntriesFromTimestamp": "{timestamp}以降のエントリーを表示中", + "xpack.logsShared.logs.showingEntriesUntilTimestamp": "{timestamp}までのエントリーを表示中", + "xpack.logsShared.dataSearch.abortedRequestErrorMessage": "リクエストが中断されましたか。", + "xpack.logsShared.dataSearch.cancelButtonLabel": "リクエストのキャンセル", + "xpack.logsShared.dataSearch.loadingErrorRetryButtonLabel": "再試行", + "xpack.logsShared.lobs.logEntryActionsViewInContextButton": "コンテキストで表示", + "xpack.logsShared.logEntryActionsMenu.apmActionLabel": "APMで表示", + "xpack.logsShared.logEntryActionsMenu.buttonLabel": "調査", + "xpack.logsShared.logEntryActionsMenu.uptimeActionLabel": "監視ステータスを表示", + "xpack.logsShared.logEntryItemView.logEntryActionsMenuToolTip": "行のアクションを表示", + "xpack.logsShared.logFlyout.fieldColumnLabel": "フィールド", + "xpack.logsShared.logFlyout.filterAriaLabel": "フィルター", + "xpack.logsShared.logFlyout.loadingErrorCalloutTitle": "ログエントリの検索中のエラー", + "xpack.logsShared.logFlyout.loadingMessage": "シャードのログエントリを検索しています", + "xpack.logsShared.logFlyout.setFilterTooltip": "フィルターでイベントを表示", + "xpack.logsShared.logFlyout.valueColumnLabel": "値", + "xpack.logsShared.logs.emptyView.checkForNewDataButtonLabel": "新規データを確認", + "xpack.logsShared.logs.emptyView.noLogMessageDescription": "フィルターを調整してみてください。", + "xpack.logsShared.logs.emptyView.noLogMessageTitle": "表示するログメッセージがありません。", + "xpack.logsShared.logs.jumpToTailText": "最も新しいエントリーに移動", + "xpack.logsShared.logs.loadingNewEntriesText": "新しいエントリーを読み込み中", + "xpack.logsShared.logs.logEntryActionsDetailsButton": "詳細を表示", + "xpack.logsShared.logs.scrollableLogTextStreamView.loadingEntriesLabel": "エントリーを読み込み中", + "xpack.logsShared.logs.stream.messageColumnTitle": "メッセージ", + "xpack.logsShared.logs.stream.timestampColumnTitle": "タイムスタンプ", + "xpack.logsShared.logs.streamingNewEntriesText": "新しいエントリーをストリーム中", + "xpack.logsShared.logs.streamLive": "ライブストリーム", + "xpack.logsShared.logStream.kqlErrorTitle": "無効なKQL式", + "xpack.logsShared.logStream.unknownErrorTitle": "エラーが発生しました", "xpack.ingestPipelines.app.deniedPrivilegeDescription": "インジェストパイプラインを使用するには、{privilegesCount, plural, other {これらのクラスター権限}}が必要です:{missingPrivileges}。", "xpack.ingestPipelines.clone.loadSourcePipelineErrorTitle": "{name}を読み込めません。", "xpack.ingestPipelines.createFromCsv.errorMessage": "{message}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9c38c99996e8..90059adb4de1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -18116,7 +18116,6 @@ "xpack.infra.analysisSetup.indicesSelectionIndexNotFound": "没有索引匹配模式 {index}", "xpack.infra.analysisSetup.indicesSelectionNoTimestampField": "匹配 {index} 的索引至少有一个缺少必需字段 {field}。", "xpack.infra.analysisSetup.indicesSelectionTimestampNotValid": "匹配 {index} 的索引至少有一个具有称作 {field} 且类型不正确的字段。", - "xpack.infra.dataSearch.shardFailureErrorMessage": "索引 {indexName}:{errorMessage}", "xpack.infra.deprecations.containerAdjustIndexing": "调整索引以使用“{field}”标识 Docker 容器", "xpack.infra.deprecations.deprecatedFieldConfigDescription": "配置“xpack.infra.sources.default.fields.{fieldKey}”已过时,将在 8.0.0 中移除。", "xpack.infra.deprecations.deprecatedFieldConfigTitle": "“{fieldKey}”已过时。", @@ -18137,8 +18136,6 @@ "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId} 不存在。", "xpack.infra.linkTo.hostWithIp.error": "未找到 IP 地址为“{hostIp}”的主机。", "xpack.infra.linkTo.hostWithIp.loading": "正在加载 IP 地址为“{hostIp}”的主机。", - "xpack.infra.logFlyout.flyoutSubTitle": "从索引 {indexName}", - "xpack.infra.logFlyout.flyoutTitle": "日志条目 {logEntryId} 的详细信息", "xpack.infra.logs.alertDetails.chart.chartTitle": "{criteria} 的日志", "xpack.infra.logs.alertFlyout.groupByOptimizationWarning": "设置“分组依据”时,强烈建议将“{comparator}”比较符用于阈值。这会使性能有较大提升。", "xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription": "对于 {groupName},过去 {duration}中有 {actualCount, plural, other {{actualCount} 个日志条目}}。{comparator} {expectedCount} 时告警。", @@ -18156,22 +18153,11 @@ "xpack.infra.logs.analysis.mlUnavailableBody": "查看 {machineLearningAppLink}以了解更多信息。", "xpack.infra.logs.common.invalidStateMessage": "无法处理状态 {stateValue}。", "xpack.infra.logs.customizeLogs.textSizeRadioGroup": "{textScale, select, small {小} medium {中} large {大} other {{textScale}}}", - "xpack.infra.logs.extendTimeframeByDaysButton": "将时间范围延伸 {amount, number} {amount, plural, other {天}}", - "xpack.infra.logs.extendTimeframeByHoursButton": "将时间范围延伸 {amount, number} {amount, plural, other {小时}}", - "xpack.infra.logs.extendTimeframeByMillisecondsButton": "将时间范围延伸 {amount, number} {amount, plural, other {毫秒}}", - "xpack.infra.logs.extendTimeframeByMinutesButton": "将时间范围延伸 {amount, number} {amount, plural, other {分钟}}", - "xpack.infra.logs.extendTimeframeByMonthsButton": "将时间范围延伸 {amount, number} 个{amount, plural, other {月}}", - "xpack.infra.logs.extendTimeframeBySecondsButton": "将时间范围延伸 {amount, number} {amount, plural, other {秒}}", - "xpack.infra.logs.extendTimeframeByWeeksButton": "将时间范围延伸 {amount, number} {amount, plural, other {周}}", - "xpack.infra.logs.extendTimeframeByYearsButton": "将时间范围延伸 {amount, number} {amount, plural, other {年}}", - "xpack.infra.logs.lastUpdate": "上次更新时间 {timestamp}", "xpack.infra.logs.logEntryCategories.manyCategoriesWarningReasonDescription": "每个分析文档的类别比率非常高,达到 {categoriesDocumentRatio, number}。", "xpack.infra.logs.logEntryCategories.manyDeadCategoriesWarningReasonDescription": "不会为 {deadCategoriesRatio, number, percent} 的类别分配新消息,因为较为笼统的类别遮蔽了它们。", "xpack.infra.logs.logEntryCategories.manyRareCategoriesWarningReasonDescription": "仅很少的时候为 {rareCategoriesRatio, number, percent} 的类别分配消息。", "xpack.infra.logs.logEntryCategories.truncatedPatternSegmentDescription": "{extraSegmentCount, plural, other {另 # 个分段}}", "xpack.infra.logs.searchResultTooltip": "{bucketCount, plural, other {# 个高亮条目}}", - "xpack.infra.logs.showingEntriesFromTimestamp": "正在显示自 {timestamp} 起的条目", - "xpack.infra.logs.showingEntriesUntilTimestamp": "正在显示截止于 {timestamp} 的条目", "xpack.infra.logs.viewInContext.logsFromContainerTitle": "显示的日志来自容器 {container}", "xpack.infra.logs.viewInContext.logsFromFileTitle": "显示的日志来自文件 {file} 和主机 {host}", "xpack.infra.logSourceConfiguration.invalidMessageFieldTypeErrorMessage": "{messageField} 字段必须是文本字段。", @@ -18282,9 +18268,6 @@ "xpack.infra.chartSection.notEnoughDataPointsToRenderTitle": "没有足够的数据", "xpack.infra.common.tabBetaBadgeLabel": "公测版", "xpack.infra.configureSourceActionLabel": "更改源配置", - "xpack.infra.dataSearch.abortedRequestErrorMessage": "请求已中止。", - "xpack.infra.dataSearch.cancelButtonLabel": "取消请求", - "xpack.infra.dataSearch.loadingErrorRetryButtonLabel": "重试", "xpack.infra.deprecations.containerIdFieldName": "容器 ID", "xpack.infra.deprecations.containerIdFieldTitle": "源配置字段“容器 ID”已过时。", "xpack.infra.deprecations.hostnameFieldName": "主机名", @@ -18438,18 +18421,7 @@ "xpack.infra.legendControls.stepsLabel": "颜色个数", "xpack.infra.legendControls.switchLabel": "自动计算范围", "xpack.infra.legnedControls.boundRangeError": "最小值必须小于最大值", - "xpack.infra.lobs.logEntryActionsViewInContextButton": "在上下文中查看", "xpack.infra.logAnomalies.logEntryExamplesMenuLabel": "查看日志条目的操作", - "xpack.infra.logEntryActionsMenu.apmActionLabel": "在 APM 中查看", - "xpack.infra.logEntryActionsMenu.buttonLabel": "调查", - "xpack.infra.logEntryActionsMenu.uptimeActionLabel": "在Uptime 中查看状态", - "xpack.infra.logEntryItemView.logEntryActionsMenuToolTip": "查看适用于以下行的操作:", - "xpack.infra.logFlyout.fieldColumnLabel": "字段", - "xpack.infra.logFlyout.filterAriaLabel": "筛选", - "xpack.infra.logFlyout.loadingErrorCalloutTitle": "搜索日志条目时出错", - "xpack.infra.logFlyout.loadingMessage": "正在分片中搜索日志条目", - "xpack.infra.logFlyout.setFilterTooltip": "使用筛选查看事件", - "xpack.infra.logFlyout.valueColumnLabel": "值", "xpack.infra.logs.alertDetails.chart.ratioTitle": "查询 A 到查询 B 的比率", "xpack.infra.logs.alertDetails.chartAnnotation.alertStarted": "已启动告警", "xpack.infra.logs.alertDetails.chartHistory.alertsTriggered": "已触发告警", @@ -18568,9 +18540,6 @@ "xpack.infra.logs.customizeLogs.lineWrappingFormRowLabel": "换行", "xpack.infra.logs.customizeLogs.textSizeFormRowLabel": "文本大小", "xpack.infra.logs.customizeLogs.wrapLongLinesSwitchLabel": "长行换行", - "xpack.infra.logs.emptyView.checkForNewDataButtonLabel": "检查新数据", - "xpack.infra.logs.emptyView.noLogMessageDescription": "尝试调整您的筛选。", - "xpack.infra.logs.emptyView.noLogMessageTitle": "没有可显示的日志消息。", "xpack.infra.logs.highlights.clearHighlightTermsButtonLabel": "清除要突出显示的词", "xpack.infra.logs.highlights.goToNextHighlightButtonLabel": "跳转到下一高亮条目", "xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel": "跳转到上一高亮条目", @@ -18580,10 +18549,7 @@ "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "类别", "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", - "xpack.infra.logs.jumpToTailText": "跳到最近的条目", - "xpack.infra.logs.loadingNewEntriesText": "正在加载新条目", "xpack.infra.logs.logCategoriesTitle": "类别", - "xpack.infra.logs.logEntryActionsDetailsButton": "查看详情", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlButtonLabel": "在 ML 中分析", "xpack.infra.logs.logEntryCategories.analyzeCategoryInMlTooltipDescription": "在 ML 应用中分析此类别。", "xpack.infra.logs.logEntryCategories.categoryColumnTitle": "类别", @@ -18614,7 +18580,6 @@ "xpack.infra.logs.noDataConfig.beatsCard.title": "添加日志记录集成", "xpack.infra.logs.noDataConfig.solutionName": "Observability", "xpack.infra.logs.pluginTitle": "日志", - "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "正在加载条目", "xpack.infra.logs.search.nextButtonLabel": "下一页", "xpack.infra.logs.search.previousButtonLabel": "上一页", "xpack.infra.logs.search.searchInLogsAriaLabel": "搜索", @@ -18624,10 +18589,6 @@ "xpack.infra.logs.settings.inlineLogViewCalloutTitle": "正使用内联日志视图", "xpack.infra.logs.startStreamingButtonLabel": "实时流式传输", "xpack.infra.logs.stopStreamingButtonLabel": "停止流式传输", - "xpack.infra.logs.stream.messageColumnTitle": "消息", - "xpack.infra.logs.stream.timestampColumnTitle": "时间戳", - "xpack.infra.logs.streamingNewEntriesText": "正在流式传输新条目", - "xpack.infra.logs.streamLive": "实时流式传输", "xpack.infra.logs.streamPageTitle": "流式传输", "xpack.infra.logsHeaderAddDataButtonLabel": "添加数据", "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "至少一个表单字段处于无效状态。", @@ -18654,8 +18615,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "重试", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "搜索日志条目……(例如 host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "日志筛选错误", - "xpack.infra.logStream.kqlErrorTitle": "KQL 表达式无效", - "xpack.infra.logStream.unknownErrorTitle": "发生错误", "xpack.infra.logStreamEmbeddable.description": "添加实时流式传输日志的表。", "xpack.infra.logStreamEmbeddable.displayName": "日志流", "xpack.infra.logStreamEmbeddable.title": "日志流", @@ -19282,6 +19241,47 @@ "xpack.infra.waffle.unableToSelectMetricErrorTitle": "无法选择指标选项或指标值。", "xpack.infra.waffleTime.autoRefreshButtonLabel": "自动刷新", "xpack.infra.waffleTime.stopRefreshingButtonLabel": "停止刷新", + "xpack.logsShared.dataSearch.shardFailureErrorMessage": "索引 {indexName}:{errorMessage}", + "xpack.logsShared.logFlyout.flyoutSubTitle": "从索引 {indexName}", + "xpack.logsShared.logFlyout.flyoutTitle": "日志条目 {logEntryId} 的详细信息", + "xpack.logsShared.logs.extendTimeframeByDaysButton": "将时间范围延伸 {amount, number} {amount, plural, other {天}}", + "xpack.logsShared.logs.extendTimeframeByHoursButton": "将时间范围延伸 {amount, number} {amount, plural, other {小时}}", + "xpack.logsShared.logs.extendTimeframeByMillisecondsButton": "将时间范围延伸 {amount, number} {amount, plural, other {毫秒}}", + "xpack.logsShared.logs.extendTimeframeByMinutesButton": "将时间范围延伸 {amount, number} {amount, plural, other {分钟}}", + "xpack.logsShared.logs.extendTimeframeByMonthsButton": "将时间范围延伸 {amount, number} 个{amount, plural, other {月}}", + "xpack.logsShared.logs.extendTimeframeBySecondsButton": "将时间范围延伸 {amount, number} {amount, plural, other {秒}}", + "xpack.logsShared.logs.extendTimeframeByWeeksButton": "将时间范围延伸 {amount, number} {amount, plural, other {周}}", + "xpack.logsShared.logs.extendTimeframeByYearsButton": "将时间范围延伸 {amount, number} {amount, plural, other {年}}", + "xpack.logsShared.logs.lastUpdate": "上次更新时间 {timestamp}", + "xpack.logsShared.logs.showingEntriesFromTimestamp": "正在显示自 {timestamp} 起的条目", + "xpack.logsShared.logs.showingEntriesUntilTimestamp": "正在显示截止于 {timestamp} 的条目", + "xpack.logsShared.dataSearch.abortedRequestErrorMessage": "请求已中止。", + "xpack.logsShared.dataSearch.cancelButtonLabel": "取消请求", + "xpack.logsShared.dataSearch.loadingErrorRetryButtonLabel": "重试", + "xpack.logsShared.lobs.logEntryActionsViewInContextButton": "在上下文中查看", + "xpack.logsShared.logEntryActionsMenu.apmActionLabel": "在 APM 中查看", + "xpack.logsShared.logEntryActionsMenu.buttonLabel": "调查", + "xpack.logsShared.logEntryActionsMenu.uptimeActionLabel": "在Uptime 中查看状态", + "xpack.logsShared.logEntryItemView.logEntryActionsMenuToolTip": "查看适用于以下行的操作:", + "xpack.logsShared.logFlyout.fieldColumnLabel": "字段", + "xpack.logsShared.logFlyout.filterAriaLabel": "筛选", + "xpack.logsShared.logFlyout.loadingErrorCalloutTitle": "搜索日志条目时出错", + "xpack.logsShared.logFlyout.loadingMessage": "正在分片中搜索日志条目", + "xpack.logsShared.logFlyout.setFilterTooltip": "使用筛选查看事件", + "xpack.logsShared.logFlyout.valueColumnLabel": "值", + "xpack.logsShared.logs.emptyView.checkForNewDataButtonLabel": "检查新数据", + "xpack.logsShared.logs.emptyView.noLogMessageDescription": "尝试调整您的筛选。", + "xpack.logsShared.logs.emptyView.noLogMessageTitle": "没有可显示的日志消息。", + "xpack.logsShared.logs.jumpToTailText": "跳到最近的条目", + "xpack.logsShared.logs.loadingNewEntriesText": "正在加载新条目", + "xpack.logsShared.logs.logEntryActionsDetailsButton": "查看详情", + "xpack.logsShared.logs.scrollableLogTextStreamView.loadingEntriesLabel": "正在加载条目", + "xpack.logsShared.logs.stream.messageColumnTitle": "消息", + "xpack.logsShared.logs.stream.timestampColumnTitle": "时间戳", + "xpack.logsShared.logs.streamingNewEntriesText": "正在流式传输新条目", + "xpack.logsShared.logs.streamLive": "实时流式传输", + "xpack.logsShared.logStream.kqlErrorTitle": "KQL 表达式无效", + "xpack.logsShared.logStream.unknownErrorTitle": "发生错误", "xpack.ingestPipelines.app.deniedPrivilegeDescription": "要使用采集管道,您必须具有{privilegesCount, plural, other {以下集群权限}}:{missingPrivileges}。", "xpack.ingestPipelines.clone.loadSourcePipelineErrorTitle": "无法加载 {name}。", "xpack.ingestPipelines.createFromCsv.errorMessage": "{message}", diff --git a/x-pack/plugins/upgrade_assistant/kibana.jsonc b/x-pack/plugins/upgrade_assistant/kibana.jsonc index 3f04775f4ca1..ff3584ef714b 100644 --- a/x-pack/plugins/upgrade_assistant/kibana.jsonc +++ b/x-pack/plugins/upgrade_assistant/kibana.jsonc @@ -21,7 +21,8 @@ "usageCollection", "cloud", "security", - "infra" + "infra", + "logsShared" ], "requiredBundles": [ "esUiShared", diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index f77a5eabe1bd..6dfb65be85d7 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -16,7 +16,7 @@ import { SavedObjectsServiceStart, } from '@kbn/core/server'; import { SecurityPluginStart } from '@kbn/security-plugin/server'; -import { InfraPluginSetup } from '@kbn/infra-plugin/server'; +import { LogsSharedPluginSetup } from '@kbn/logs-shared-plugin/server'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import { SecurityPluginSetup } from '@kbn/security-plugin/server'; @@ -43,7 +43,7 @@ interface PluginsSetup { usageCollection: UsageCollectionSetup; licensing: LicensingPluginSetup; features: FeaturesPluginSetup; - infra: InfraPluginSetup; + logsShared: LogsSharedPluginSetup; security?: SecurityPluginSetup; } @@ -83,7 +83,7 @@ export class UpgradeAssistantServerPlugin implements Plugin { setup( { http, getStartServices, savedObjects }: CoreSetup, - { usageCollection, features, licensing, infra, security }: PluginsSetup + { usageCollection, features, licensing, logsShared, security }: PluginsSetup ) { this.licensing = licensing; @@ -105,7 +105,7 @@ export class UpgradeAssistantServerPlugin implements Plugin { // We need to initialize the deprecation logs plugin so that we can // navigate from this app to the observability app using a source_id. - infra?.logViews.defineInternalLogView(DEPRECATION_LOGS_SOURCE_ID, { + logsShared?.logViews.defineInternalLogView(DEPRECATION_LOGS_SOURCE_ID, { name: 'deprecationLogs', description: 'deprecation logs', logIndices: { diff --git a/x-pack/plugins/upgrade_assistant/tsconfig.json b/x-pack/plugins/upgrade_assistant/tsconfig.json index c9d8201c1607..59d562c03c87 100644 --- a/x-pack/plugins/upgrade_assistant/tsconfig.json +++ b/x-pack/plugins/upgrade_assistant/tsconfig.json @@ -20,7 +20,6 @@ "@kbn/features-plugin", "@kbn/licensing-plugin", "@kbn/es-ui-shared-plugin", - "@kbn/infra-plugin", "@kbn/cloud-plugin", "@kbn/test-jest-helpers", "@kbn/share-plugin", @@ -37,6 +36,7 @@ "@kbn/core-elasticsearch-client-server-mocks", "@kbn/utility-types", "@kbn/shared-ux-router", + "@kbn/logs-shared-plugin", ], "exclude": [ "target/**/*", diff --git a/x-pack/test/api_integration/apis/logs_ui/log_views.ts b/x-pack/test/api_integration/apis/logs_ui/log_views.ts index 646d9fd3bb5e..07b2ffdbf683 100644 --- a/x-pack/test/api_integration/apis/logs_ui/log_views.ts +++ b/x-pack/test/api_integration/apis/logs_ui/log_views.ts @@ -6,14 +6,14 @@ */ import expect from '@kbn/expect'; -import { defaultLogViewId, LogViewAttributes } from '@kbn/infra-plugin/common/log_views'; +import { defaultLogViewId, LogViewAttributes } from '@kbn/logs-shared-plugin/common/log_views'; import { defaultSourceConfiguration, infraSourceConfigurationSavedObjectName, mergeSourceConfiguration, } from '@kbn/infra-plugin/server/lib/sources'; import { extractSavedObjectReferences } from '@kbn/infra-plugin/server/lib/sources/saved_object_references'; -import { logViewSavedObjectName } from '@kbn/infra-plugin/server/saved_objects/log_view'; +import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/api_integration/apis/metrics_ui/log_entry_highlights.ts b/x-pack/test/api_integration/apis/metrics_ui/log_entry_highlights.ts index 6efb17e1571c..fd87af88ebc8 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/log_entry_highlights.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/log_entry_highlights.ts @@ -17,7 +17,7 @@ import { LOG_ENTRIES_HIGHLIGHTS_PATH, logEntriesHighlightsRequestRT, logEntriesHighlightsResponseRT, -} from '@kbn/infra-plugin/common/http_api'; +} from '@kbn/logs-shared-plugin/common'; import { FtrProviderContext } from '../../ftr_provider_context'; diff --git a/x-pack/test/api_integration/apis/metrics_ui/log_summary.ts b/x-pack/test/api_integration/apis/metrics_ui/log_summary.ts index 9c7c5548fcdb..4ae51422ea68 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/log_summary.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/log_summary.ts @@ -19,7 +19,7 @@ import { LOG_ENTRIES_SUMMARY_PATH, logEntriesSummaryRequestRT, logEntriesSummaryResponseRT, -} from '@kbn/infra-plugin/common/http_api'; +} from '@kbn/logs-shared-plugin/common'; import { FtrProviderContext } from '../../ftr_provider_context'; diff --git a/x-pack/test/common/services/infra_log_views.ts b/x-pack/test/common/services/infra_log_views.ts index a6a87da67e1a..3862295e997a 100644 --- a/x-pack/test/common/services/infra_log_views.ts +++ b/x-pack/test/common/services/infra_log_views.ts @@ -10,8 +10,8 @@ import { PutLogViewRequestPayload, putLogViewRequestPayloadRT, putLogViewResponsePayloadRT, -} from '@kbn/infra-plugin/common/http_api'; -import { getLogViewUrl } from '@kbn/infra-plugin/common/http_api/log_views'; +} from '@kbn/logs-shared-plugin/common/http_api'; +import { getLogViewUrl } from '@kbn/logs-shared-plugin/common/http_api/log_views'; import { decodeOrThrow } from '@kbn/infra-plugin/common/runtime_types'; import { FtrProviderContext } from '../ftr_provider_context'; diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 03496116b207..4a2aff8ae773 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -130,6 +130,7 @@ "@kbn/slo-schema", "@kbn/lens-plugin", "@kbn/notifications-plugin", + "@kbn/logs-shared-plugin", "@kbn/telemetry-tools", "@kbn/profiling-plugin", "@kbn/observability-onboarding-plugin" diff --git a/yarn.lock b/yarn.lock index dec901f6e1b2..3037b73b85d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4618,6 +4618,10 @@ version "0.0.0" uid "" +"@kbn/logs-shared-plugin@link:x-pack/plugins/logs_shared": + version "0.0.0" + uid "" + "@kbn/logstash-plugin@link:x-pack/plugins/logstash": version "0.0.0" uid "" From e654c8e936853e1952b2f46b862fd34911618d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=BCrkan=20YALAMAN?= Date: Wed, 5 Jul 2023 10:38:55 +0200 Subject: [PATCH 02/89] [Enterprise Search]Add 404 error handling for mappings and documents endpoints (#161203) ## Summary Add 404 handler for the Document and mappings endpoints. They were default handlers before and returning 502. Error message is much more meaningful at the moment. ![Screenshot 2023-07-04 at 18 16 53](https://github.com/elastic/kibana/assets/1410658/f595391b-2889-4370-907f-7e5c0d331f2c) ![Screenshot 2023-07-04 at 18 17 01](https://github.com/elastic/kibana/assets/1410658/018d34d1-273a-4d85-9f5a-78bbf15cf526) ### Checklist Delete any items that are not applicable to this PR. - [ ] [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 --- .../enterprise_search/common/constants.ts | 2 + .../components/search_index/documents.tsx | 4 +- .../search_index/index_mappings.tsx | 4 +- .../plugins/enterprise_search/server/index.ts | 1 - .../server/lib/connectors/start_sync.test.ts | 8 ++-- .../server/lib/connectors/start_sync.ts | 11 +++-- .../indices/delete_access_control_index.ts | 2 +- .../routes/enterprise_search/mapping.ts | 26 ++++++++--- .../server/routes/enterprise_search/search.ts | 43 +++++++++++++------ 9 files changed, 67 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index c2af49bb9e95..88290c2ed483 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -210,3 +210,5 @@ export const DEFAULT_PRODUCT_FEATURES: ProductFeatures = { hasNativeConnectors: true, hasWebCrawler: true, }; + +export const CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX = '.search-acl-filter-'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx index 8cf25e16426a..ea1b1aaecd23 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx @@ -20,6 +20,8 @@ import { import { i18n } from '@kbn/i18n'; +import { CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX } from '../../../../../common/constants'; + import { KibanaLogic } from '../../../shared/kibana'; import { @@ -44,7 +46,7 @@ export const SearchIndexDocuments: React.FC = () => { const indexToShow = selectedIndexType === 'content-index' ? indexName - : indexName.replace('search-', '.search-acl-filter-'); + : indexName.replace('search-', CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX); const shouldShowAccessControlSwitcher = hasDocumentLevelSecurityFeature && productFeatures.hasDocumentLevelSecurityEnabled; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_mappings.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_mappings.tsx index 4bc77672f5f7..5ec31986b2fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_mappings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_mappings.tsx @@ -25,6 +25,8 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX } from '../../../../../common/constants'; + import { docLinks } from '../../../shared/doc_links'; import { KibanaLogic } from '../../../shared/kibana'; @@ -51,7 +53,7 @@ export const SearchIndexIndexMappings: React.FC = () => { const indexToShow = selectedIndexType === 'content-index' ? indexName - : indexName.replace('search-', '.search-acl-filter-'); + : indexName.replace('search-', CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX); const shouldShowAccessControlSwitch = hasDocumentLevelSecurityFeature && productFeatures.hasDocumentLevelSecurityEnabled; diff --git a/x-pack/plugins/enterprise_search/server/index.ts b/x-pack/plugins/enterprise_search/server/index.ts index 704595b708c5..291864181434 100644 --- a/x-pack/plugins/enterprise_search/server/index.ts +++ b/x-pack/plugins/enterprise_search/server/index.ts @@ -53,4 +53,3 @@ export const CURRENT_CONNECTORS_INDEX = '.elastic-connectors-v1'; export const CONNECTORS_JOBS_INDEX = '.elastic-connectors-sync-jobs'; export const CONNECTORS_VERSION = 1; export const CRAWLERS_INDEX = '.ent-search-actastic-crawler2_configurations_v2'; -export const CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX = '.search-acl-filter-'; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts index ca7ae9fc76a6..4e35acd1434b 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts @@ -7,11 +7,9 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { - CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX, - CONNECTORS_INDEX, - CONNECTORS_JOBS_INDEX, -} from '../..'; +import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '../..'; +import { CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX } from '../../../common/constants'; + import { SyncJobType, SyncStatus, TriggerMethod } from '../../../common/types/connectors'; import { ErrorCode } from '../../../common/types/error_codes'; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts index e4ef3288819f..a3b2164c467a 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts @@ -7,14 +7,13 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { - CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX, - CONNECTORS_INDEX, - CONNECTORS_JOBS_INDEX, -} from '../..'; +import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '../..'; import { isConfigEntry } from '../../../common/connectors/is_category_entry'; -import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../../common/constants'; +import { + CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX, + ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE, +} from '../../../common/constants'; import { ConnectorConfiguration, diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts b/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts index 2df773b1af30..3d185c9fde6f 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts @@ -8,7 +8,7 @@ import { isIndexNotFoundException } from '@kbn/core-saved-objects-migration-server-internal'; import { IScopedClusterClient } from '@kbn/core/server'; -import { CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX } from '../..'; +import { CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX } from '../../../common/constants'; export const deleteAccessControlIndex = async (client: IScopedClusterClient, index: string) => { try { diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/mapping.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/mapping.ts index b963157694ac..0ce48c33a9d3 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/mapping.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/mapping.ts @@ -7,9 +7,13 @@ import { schema } from '@kbn/config-schema'; +import { ErrorCode } from '../../../common/types/error_codes'; + import { fetchMapping } from '../../lib/fetch_mapping'; import { RouteDependencies } from '../../plugin'; +import { createError } from '../../utils/create_error'; import { elasticsearchErrorHandler } from '../../utils/elasticsearch_error_handler'; +import { isIndexNotFoundException } from '../../utils/identify_exceptions'; export function registerMappingRoute({ router, log }: RouteDependencies) { router.get( @@ -24,12 +28,24 @@ export function registerMappingRoute({ router, log }: RouteDependencies) { elasticsearchErrorHandler(log, async (context, request, response) => { const { client } = (await context.core).elasticsearch; - const mapping = await fetchMapping(client, request.params.index_name); + try { + const mapping = await fetchMapping(client, request.params.index_name); - return response.ok({ - body: mapping, - headers: { 'content-type': 'application/json' }, - }); + return response.ok({ + body: mapping, + headers: { 'content-type': 'application/json' }, + }); + } catch (error) { + if (isIndexNotFoundException(error)) { + return createError({ + errorCode: ErrorCode.INDEX_NOT_FOUND, + message: 'Could not found index', + response, + statusCode: 404, + }); + } + throw error; + } }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts index 3ea14a2013a5..40158204e3ea 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts @@ -10,10 +10,13 @@ import { SearchResponseBody } from '@elastic/elasticsearch/lib/api/types'; import { schema } from '@kbn/config-schema'; import { ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } from '../../../common/constants'; +import { ErrorCode } from '../../../common/types/error_codes'; import { fetchSearchResults } from '../../lib/fetch_search_results'; import { RouteDependencies } from '../../plugin'; +import { createError } from '../../utils/create_error'; import { elasticsearchErrorHandler } from '../../utils/elasticsearch_error_handler'; +import { isIndexNotFoundException } from '../../utils/identify_exceptions'; const calculateMeta = (searchResults: SearchResponseBody, page: number, size: number) => { let totalResults = 0; @@ -64,21 +67,33 @@ export function registerSearchRoute({ router, log }: RouteDependencies) { const { client } = (await context.core).elasticsearch; const { page = 0, size = ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } = request.query; const from = page * size; - const searchResults: SearchResponseBody = await fetchSearchResults( - client, - indexName, - searchQuery, - from, - size - ); + try { + const searchResults: SearchResponseBody = await fetchSearchResults( + client, + indexName, + searchQuery, + from, + size + ); - return response.ok({ - body: { - meta: calculateMeta(searchResults, page, size), - results: searchResults, - }, - headers: { 'content-type': 'application/json' }, - }); + return response.ok({ + body: { + meta: calculateMeta(searchResults, page, size), + results: searchResults, + }, + headers: { 'content-type': 'application/json' }, + }); + } catch (error) { + if (isIndexNotFoundException(error)) { + return createError({ + errorCode: ErrorCode.INDEX_NOT_FOUND, + message: 'Could not found index', + response, + statusCode: 404, + }); + } + throw error; + } }) ); } From 77285193045db36453f367d61552638218fde013 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Wed, 5 Jul 2023 10:53:02 +0200 Subject: [PATCH 03/89] [ML] Fix Anomaly Explorer URL for alerting context with non-default space (#160899) ## Summary Fixes #160762 ### Checklist - [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 --- .../ml/server/lib/alerts/alerting_service.ts | 16 +++++++++++++--- .../register_anomaly_detection_alert_type.ts | 4 ++-- .../ml_rule_types/anomaly_detection/alert.ts | 7 ++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts index 68b3ed349150..31794a901416 100644 --- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts +++ b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts @@ -23,6 +23,7 @@ import { type MlAnomalyResultType, ML_ANOMALY_RESULT_TYPE, } from '@kbn/ml-anomaly-utils'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { MlClient } from '../ml_client'; import { MlAnomalyDetectionAlertParams, @@ -78,6 +79,7 @@ export function buildExplorerUrl( jobIds: string[], timeRange: { from: string; to: string; mode?: string }, type: MlAnomalyResultType, + spaceId: string, r?: AlertExecutionResult ): string { const isInfluencerResult = type === ML_ANOMALY_RESULT_TYPE.INFLUENCER; @@ -145,7 +147,11 @@ export function buildExplorerUrl( }, }, }; - return `/app/ml/explorer/?_g=${encodeURIComponent( + + const spacePathComponent: string = + !spaceId || spaceId === DEFAULT_SPACE_ID ? '' : `/s/${spaceId}`; + + return `${spacePathComponent}/app/ml/explorer/?_g=${encodeURIComponent( rison.encode(globalState) )}&_a=${encodeURIComponent(rison.encode(appState))}`; } @@ -765,9 +771,11 @@ export function alertingServiceProvider( * Return the result of an alert condition execution. * * @param params - Alert params + * @param spaceId */ execute: async ( - params: MlAnomalyDetectionAlertParams + params: MlAnomalyDetectionAlertParams, + spaceId: string ): Promise< { context: AnomalyDetectionAlertContext; name: string; isHealthy: boolean } | undefined > => { @@ -784,6 +792,7 @@ export function alertingServiceProvider( result.jobIds, { from: result.bucketRange.start, to: result.bucketRange.end }, params.resultType, + spaceId, result ); @@ -806,7 +815,8 @@ export function alertingServiceProvider( to: 'now', mode: 'relative', }, - queryParams.resultType + queryParams.resultType, + spaceId ), jobIds: queryParams.jobIds, message: i18n.translate( diff --git a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts index 7cee5917f656..d68e40f44b4a 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts @@ -137,13 +137,13 @@ export function registerAnomalyDetectionAlertType({ minimumLicenseRequired: MINIMUM_FULL_LICENSE, isExportable: true, doesSetRecoveryContext: true, - async executor({ services, params }) { + async executor({ services, params, spaceId }) { const fakeRequest = {} as KibanaRequest; const { execute } = mlSharedServices.alertingServiceProvider( services.savedObjectsClient, fakeRequest ); - const executionResult = await execute(params); + const executionResult = await execute(params, spaceId); if (executionResult && !executionResult.isHealthy) { const alertInstanceName = executionResult.name; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts index 1d69200a277d..9bd615b99e53 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/ml_rule_types/anomaly_detection/alert.ts @@ -133,12 +133,16 @@ export default function alertTests({ getService }: FtrProviderContext) { const docs = await waitForDocs(1); for (const doc of docs) { - const { name, message } = doc._source.params; + const { name, message, anomalyExplorerUrl } = doc._source.params; expect(name).to.be('Test AD job'); expect(message).to.be( 'Alerts are raised based on real-time scores. Remember that scores may be adjusted over time as data continues to be analyzed.' ); + // check only part of the URL as time bounds vary based on the anomaly + expect(anomalyExplorerUrl).to.contain( + '/s/space1/app/ml/explorer/?_g=(ml%3A(jobIds%3A!(rt-anomaly-mean-value))' + ); } }); @@ -166,6 +170,7 @@ export default function alertTests({ getService }: FtrProviderContext) { params: { name: '{{{alertName}}}', message: '{{{context.message}}}', + anomalyExplorerUrl: '{{{context.anomalyExplorerUrl}}}', }, }, ], From 0b13a843f86097ed38086791d846754edd17b25a Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Wed, 5 Jul 2023 11:23:46 +0200 Subject: [PATCH 04/89] Update publicBaseUrl warning id (#161204) Related to #161091 ## Summary I learned that when we update a default message, we should manually remove outdated translations. In this PR, I updated the ID and removed outdated translations. I will not backport this PR since the translation for the previous version is already started so I will keep the previous translation for v8.9. Please let me know if there is an issue with this approach. --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - .../public/application/lib/validate_params_for_warnings.ts | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 29c1982ae0d3..9ab100fe729a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -37760,7 +37760,6 @@ "xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "Ajouter un connecteur", "xpack.triggersActionsUI.sections.actionTypeForm.notifyWhenThrottleWarning": "Les intervalles d'action personnalisés ne peuvent pas être plus courts que l'intervalle de vérification de la règle", "xpack.triggersActionsUI.sections.actionTypeForm.summaryGroupTitle": "Résumé des alertes", - "xpack.triggersActionsUI.sections.actionTypeForm.warning.publicUrl": "server.publicBaseUrl n'est pas défini. Les actions utiliseront des URL relatives.", "xpack.triggersActionsUI.sections.addConnectorForm.flyoutHeaderCompatibility": "Compatibilité :", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "Sélectionner un connecteur", "xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "Création de \"{connectorName}\" effectuée", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2b610e8af95c..c79922135bd8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -37734,7 +37734,6 @@ "xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "コネクターの追加", "xpack.triggersActionsUI.sections.actionTypeForm.notifyWhenThrottleWarning": "カスタムアクション間隔をルールのチェック間隔よりも短くすることはできません", "xpack.triggersActionsUI.sections.actionTypeForm.summaryGroupTitle": "アラートの概要", - "xpack.triggersActionsUI.sections.actionTypeForm.warning.publicUrl": "server.publicBaseUrlが設定されていません。アクションは相対URLを使用します。", "xpack.triggersActionsUI.sections.addConnectorForm.flyoutHeaderCompatibility": "互換性:", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "コネクターを選択", "xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "「{connectorName}」を作成しました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 90059adb4de1..163f3c66e144 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -37728,7 +37728,6 @@ "xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "添加连接器", "xpack.triggersActionsUI.sections.actionTypeForm.notifyWhenThrottleWarning": "定制操作时间间隔不能短于规则的检查时间间隔", "xpack.triggersActionsUI.sections.actionTypeForm.summaryGroupTitle": "告警的摘要", - "xpack.triggersActionsUI.sections.actionTypeForm.warning.publicUrl": "未设置 server.publicBaseUrl。操作将使用相对 URL。", "xpack.triggersActionsUI.sections.addConnectorForm.flyoutHeaderCompatibility": "兼容性:", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "选择连接器", "xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts index 447a751f32a3..e0f552f6bb0f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts @@ -11,7 +11,7 @@ import { ActionVariable, RuleActionParam } from '@kbn/alerting-plugin/common'; import Mustache from 'mustache'; const publicUrlWarning = i18n.translate( - 'xpack.triggersActionsUI.sections.actionTypeForm.warning.publicUrl', + 'xpack.triggersActionsUI.sections.actionTypeForm.warning.publicBaseUrl', { defaultMessage: 'server.publicBaseUrl is not set. Generated URLs will be either relative or empty.', From 38b487a879ba12a2a6b37930fd05d60aa3c7ae10 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 5 Jul 2023 11:27:54 +0200 Subject: [PATCH 05/89] [Fleet] Fix permissions in integrations Assets page (#161233) Fixes https://github.com/elastic/kibana/issues/161058 ## Summary Fix permissions for Integrations assets tab. A user with role "Fleet All - Integration Read" wasn't able to visualize the assets tab. ### Test - Create a user with "Fleet All - Integration Read" as shown in this video: https://github.com/elastic/kibana/assets/16084106/a13c6ddd-a3d1-4e15-9c9d-9d56e1dbb0f0 - Log in with this new user - Navigate to any installed integration, then to the Assets tab - Verify that the assets are shown as usual (no warnings are shown) Screenshot 2023-07-05 at 10 22 36 --- .../integrations/sections/epm/screens/detail/assets/assets.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index b9a023bef1ef..2f6e20f09760 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -46,7 +46,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { const { spaces } = useStartServices(); const customAssetsExtension = useUIExtension(packageInfo.name, 'package-detail-assets'); - const canReadPackageSettings = useAuthz().integrations.readPackageSettings; + const canReadPackageSettings = useAuthz().integrations.readPackageInfo; const { getPath } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); From 3d146f3eff411ecd12dc7c1f561bd74e8b60a02d Mon Sep 17 00:00:00 2001 From: Andrew Gizas Date: Wed, 5 Jul 2023 12:41:24 +0300 Subject: [PATCH 06/89] fixing the path of manifets for hints autodiscover (#161075) ## Summary Bug: Fixing the path for downloading hint templates for elastic standalone agent. See that the path had changed to https://github.com/elastic/elastic-agent/tree/main/deploy/kubernetes/elastic-agent-standalone/templates.d --- x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts index 63691b878eaa..f01f6d71df89 100644 --- a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts +++ b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts @@ -42,7 +42,7 @@ spec: # - -c # - >- # mkdir -p /etc/elastic-agent/inputs.d && - # wget -O - https://github.com/elastic/elastic-agent/archive/main.tar.gz | tar xz -C /etc/elastic-agent/inputs.d --strip=5 "elastic-agent-main/deploy/kubernetes/elastic-agent/templates.d" + # wget -O - https://github.com/elastic/elastic-agent/archive/main.tar.gz | tar xz -C /etc/elastic-agent/inputs.d --strip=5 "elastic-agent-main/deploy/kubernetes/elastic-agent-standalone/templates.d" # volumeMounts: # - name: external-inputs # mountPath: /etc/elastic-agent/inputs.d From 6a84ea186b664b55c19a9465b2ea80ddd29c6ffd Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 5 Jul 2023 11:55:38 +0200 Subject: [PATCH 07/89] [Synthetics] Remove TLS alert option for ICMP monitor (#161173) --- .../monitor_add_edit/form/field_config.tsx | 21 ++++++++++--------- .../monitor_add_edit/form/form_config.tsx | 1 - .../tls_rule/tls_rule_executor.test.ts | 3 ++- .../alert_rules/tls_rule/tls_rule_executor.ts | 5 +++-- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx index 56987e86b9c8..133f509c4b0e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx @@ -507,9 +507,13 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ controlled: true, props: ({ setValue, field }): EuiSwitchProps => ({ id: 'syntheticsMonitorConfigIsAlertEnabled', - label: i18n.translate('xpack.synthetics.monitorConfig.enabledAlerting.label', { - defaultMessage: 'Enable status alerts', - }), + label: field?.value + ? i18n.translate('xpack.synthetics.monitorConfig.enabledAlerting.label', { + defaultMessage: 'Disable status alerts on this monitor', + }) + : i18n.translate('xpack.synthetics.monitorConfig.disabledAlerting.label', { + defaultMessage: 'Enable status alerts on this monitor', + }), checked: field?.value || false, onChange: (event) => { setValue(AlertConfigKey.STATUS_ENABLED, !!event.target.checked); @@ -522,18 +526,15 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ [AlertConfigKey.TLS_ENABLED]: { fieldKey: AlertConfigKey.TLS_ENABLED, component: Switch, - label: i18n.translate('xpack.synthetics.monitorConfig.enabledAlerting.tls.label', { - defaultMessage: 'Enable TLS alerts', - }), controlled: true, - props: ({ isEdit, setValue, field }): EuiSwitchProps => ({ + props: ({ setValue, field }): EuiSwitchProps => ({ id: 'syntheticsMonitorConfigIsTlsAlertEnabled', - label: isEdit + label: field?.value ? i18n.translate('xpack.synthetics.monitorConfig.edit.alertTlsEnabled.label', { - defaultMessage: 'Disabling will stop tls alerting on this monitor.', + defaultMessage: 'Disable TLS alerts on this monitor.', }) : i18n.translate('xpack.synthetics.monitorConfig.create.alertTlsEnabled.label', { - defaultMessage: 'Enable tls alerts on this monitor.', + defaultMessage: 'Enable TLS alerts on this monitor.', }), checked: field?.value || false, onChange: (event) => { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx index ffd8f1c3cfcc..8e44f9727a5c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx @@ -287,7 +287,6 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.TIMEOUT], FIELD(readOnly)[ConfigKey.ENABLED], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], - FIELD(readOnly)[AlertConfigKey.TLS_ENABLED], ], advanced: [DEFAULT_DATA_OPTIONS(readOnly), ICMP_ADVANCED(readOnly).requestConfig], }, diff --git a/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.test.ts b/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.test.ts index ac89f6f6bbbb..eec9a8be02fd 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.test.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.test.ts @@ -75,7 +75,8 @@ describe('tlsRuleExecutor', () => { expect(certs).toEqual([]); expect(spy).toHaveBeenCalledWith({ - filter: 'synthetics-monitor.attributes.alert.tls.enabled: true', + filter: + 'synthetics-monitor.attributes.alert.tls.enabled: true and (synthetics-monitor.attributes.type: http or synthetics-monitor.attributes.type: tcp)', soClient, }); }); diff --git a/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts b/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts index ad5f6ad2b88d..71cfd7453c12 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts @@ -21,7 +21,7 @@ import { getAllMonitors, processMonitors, } from '../../saved_objects/synthetics_monitor/get_all_monitors'; -import { CertResult, EncryptedSyntheticsMonitor } from '../../../common/runtime_types'; +import { CertResult, ConfigKey, EncryptedSyntheticsMonitor } from '../../../common/runtime_types'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; import { monitorAttributes } from '../../../common/types/saved_objects'; import { AlertConfigKey } from '../../../common/constants/monitor_management'; @@ -55,9 +55,10 @@ export class TLSRuleExecutor { } async getMonitors() { + const HTTP_OR_TCP = `${monitorAttributes}.${ConfigKey.MONITOR_TYPE}: http or ${monitorAttributes}.${ConfigKey.MONITOR_TYPE}: tcp`; this.monitors = await getAllMonitors({ soClient: this.soClient, - filter: `${monitorAttributes}.${AlertConfigKey.TLS_ENABLED}: true`, + filter: `${monitorAttributes}.${AlertConfigKey.TLS_ENABLED}: true and (${HTTP_OR_TCP})`, }); const { From 54dc40ff69f8ca0035cc722594b6cfc1512e499b Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 5 Jul 2023 12:10:32 +0200 Subject: [PATCH 08/89] [Synthetics] Overview page fix last refresh value display (#161086) Co-authored-by: Abdul Zahid --- .../plugins/synthetics/common/constants/ui.ts | 21 --------- .../common/components/last_refreshed.tsx | 11 ----- .../synthetics/utils/formatting/format.ts | 38 ---------------- .../utils/monitor_test_result/timestamp.ts | 45 ------------------- .../public/hooks/use_date_format.test.tsx | 25 +++++++++++ .../public/hooks/use_date_format.ts | 11 +++-- 6 files changed, 33 insertions(+), 118 deletions(-) delete mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/timestamp.ts diff --git a/x-pack/plugins/synthetics/common/constants/ui.ts b/x-pack/plugins/synthetics/common/constants/ui.ts index d014b8b8ea6f..189dd40660ae 100644 --- a/x-pack/plugins/synthetics/common/constants/ui.ts +++ b/x-pack/plugins/synthetics/common/constants/ui.ts @@ -67,27 +67,6 @@ export const ML_MODULE_ID = 'uptime_heartbeat'; export const UNNAMED_LOCATION = 'Unnamed-location'; -export const SHORT_TS_LOCALE = 'en-short-locale'; - -export const SHORT_TIMESPAN_LOCALE = { - relativeTime: { - future: 'in %s', - past: '%s ago', - s: '%ds', - ss: '%ss', - m: '%dm', - mm: '%dm', - h: '%dh', - hh: '%dh', - d: '%dd', - dd: '%dd', - M: '%d Mon', - MM: '%d Mon', - y: '%d Yr', - yy: '%d Yr', - }, -}; - export enum CERT_STATUS { OK = 'OK', EXPIRING_SOON = 'EXPIRING_SOON', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/last_refreshed.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/last_refreshed.tsx index 28a4ef1c5b10..bc086f67c822 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/last_refreshed.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/last_refreshed.tsx @@ -10,7 +10,6 @@ import moment from 'moment'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useSelector } from 'react-redux'; -import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; import { selectRefreshPaused } from '../../../state'; @@ -41,18 +40,8 @@ export function LastRefreshed() { const isWarning = moment().diff(moment(lastRefreshed), 'minutes') > 1; const isDanger = moment().diff(moment(lastRefreshed), 'minutes') > 5; - const prevLocal: string = moment.locale() ?? 'en'; - - const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE; - if (!shortLocale) { - moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE); - } - const updatedDate = moment(lastRefreshed).from(refresh); - // Need to reset locale so it doesn't effect other parts of the app - moment.locale(prevLocal); - return ( { - if (relative) { - const prevLocale: string = moment.locale() ?? 'en'; - - const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE; - - if (!shortLocale) { - moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE); - } - - let shortTimestamp; - if (typeof timeStamp === 'string') { - shortTimestamp = parseTimestamp(timeStamp).fromNow(); - } else { - shortTimestamp = timeStamp.fromNow(); - } - - // Reset it so, it doesn't impact other part of the app - moment.locale(prevLocale); - return shortTimestamp; - } else { - if (moment().diff(timeStamp, 'd') >= 1) { - return timeStamp.format('ll LTS'); - } - return timeStamp.format('LTS'); - } -}; - -export const parseTimestamp = (tsValue: string): Moment => { - let parsed = Date.parse(tsValue); - if (isNaN(parsed)) { - parsed = parseInt(tsValue, 10); - } - return moment(parsed); -}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/timestamp.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/timestamp.ts deleted file mode 100644 index c9c4d022b869..000000000000 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/timestamp.ts +++ /dev/null @@ -1,45 +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 moment from 'moment'; -import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../common/constants'; - -export const parseTimestamp = (tsValue: string): moment.Moment => { - let parsed = Date.parse(tsValue); - if (isNaN(parsed)) { - parsed = parseInt(tsValue, 10); - } - return moment(parsed); -}; - -export const getShortTimeStamp = (timeStamp: moment.Moment, relative = false) => { - if (relative) { - const prevLocale: string = moment.locale() ?? 'en'; - - const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE; - - if (!shortLocale) { - moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE); - } - - let shortTimestamp; - if (typeof (timeStamp as unknown) === 'string') { - shortTimestamp = parseTimestamp(timeStamp as unknown as string).fromNow(); - } else { - shortTimestamp = timeStamp.fromNow(); - } - - // Reset it so, it doesn't impact other part of the app - moment.locale(prevLocale); - return shortTimestamp; - } else { - if (moment().diff(timeStamp, 'd') >= 1) { - return timeStamp.format('ll LTS'); - } - return timeStamp.format('LTS'); - } -}; diff --git a/x-pack/plugins/synthetics/public/hooks/use_date_format.test.tsx b/x-pack/plugins/synthetics/public/hooks/use_date_format.test.tsx index 3ddfe441d389..42cee2817e8f 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_date_format.test.tsx +++ b/x-pack/plugins/synthetics/public/hooks/use_date_format.test.tsx @@ -6,10 +6,25 @@ */ import { renderHook } from '@testing-library/react-hooks'; +import { i18n } from '@kbn/i18n'; + +jest.mock('@kbn/i18n', () => ({ + i18n: { + getLocale: jest.fn().mockReturnValue(undefined), + }, +})); import { useDateFormat } from './use_date_format'; describe('useDateFormat', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + Object.defineProperty(global.navigator, 'language', { value: 'en-US', writable: true, @@ -26,4 +41,14 @@ describe('useDateFormat', () => { const response = renderHook(() => useDateFormat()); expect(response.result.current('2023-02-01 13:00:00')).toEqual('1 Feb 2023 @ 13:00'); }); + it('prefers Kibana locale if set', () => { + jest.spyOn(i18n, 'getLocale').mockReturnValue('fr-FR'); + + Object.defineProperty(global.navigator, 'language', { + value: 'en-GB', + writable: true, + }); + const response = renderHook(() => useDateFormat()); + expect(response.result.current('2023-02-01 13:00:00')).toEqual('1 févr. 2023 @ 13:00'); + }); }); diff --git a/x-pack/plugins/synthetics/public/hooks/use_date_format.ts b/x-pack/plugins/synthetics/public/hooks/use_date_format.ts index 2fc143af4a87..4b402cc2367c 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_date_format.ts +++ b/x-pack/plugins/synthetics/public/hooks/use_date_format.ts @@ -7,13 +7,18 @@ import moment from 'moment'; import { useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; export function useDateFormat(): (timestamp?: string) => string { - const locale = navigator.language; + const kibanaLocale = i18n.getLocale(); + const clientLocale = navigator.language; useEffect(() => { - moment.locale(locale); - }, [locale]); + const preferredLocale = kibanaLocale ?? clientLocale; + if (moment.locale() !== preferredLocale) { + moment.locale(preferredLocale); + } + }, [kibanaLocale, clientLocale]); return (timestamp?: string) => { if (!timestamp) return ''; From ee6ca657eeadd498f4783331539b102ec619097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:38:55 +0200 Subject: [PATCH 09/89] [Console] Add logic for query params (#160515) ## Summary Fixes https://github.com/elastic/kibana/issues/160528 Follow up to https://github.com/elastic/kibana/pull/159241 This PR adds logic for query parameters to the new script generating Console autocomplete definitions from ES specification. The logic is explained in details in the [file](https://github.com/elastic/kibana/pull/160515/files#diff-b6853462c38db4e237dbb3cdec9d9f6659aa3fdc5f96a6193f2c4bac1439db43) but here is a short version: - Currently, the autocomplete engine only works with url params of 2 types: boolean (`__flag__`) and a list of options (for example `['all', 'open', 'hidden']`). The script will convert all of the url params from the specification into this format: a boolean or a list. If there are no set options for a parameter, but a default value is known, then it will be converted into a list with 1 option, for example `['random']` so that the autocomplete engine will display it as a single suggestion. We also need to convert any numbers to strings, because they won't be displayed otherwise. - Endpoints in the specification have a property `request` which in turn has 2 properties describing url params: `attachedBehaviours` and `query`. Both object contain an array of `property`'s each describing a url param. `property` is configured with either a built in type (`string`, `number`, `boolean`) or defined type, for example `ExpandWildcards`. By finding the type `ExpandWildcards` in the specification, we can convert this type to a list of options `['open', 'all', 'none', 'hidden']`. ### How to test Similar to https://github.com/elastic/kibana/pull/159241, you need to re-generenate the definitions and see if local changes to definitions files make sense. 1. Checkout the ES specification [repo](https://github.com/elastic/elasticsearch-specification) 2. Run the command `node scripts/generate_console_definitions.js --source --emptyDest` 3. Check the changes in the folder `KIBANA_REPO/src/plugins/console/server/lib/spec_definitions/json/generated` #### Intended changes to the definitions files - Most of endpoints have 4 default url params that previously were not in the definitions files but added to all endpoints in this [file](https://github.com/elastic/kibana/blob/main/src/plugins/console/public/lib/autocomplete/url_params.js). These params are configured in the interface `CommonQueryParameters` in the specification (see this [file](https://github.com/elastic/elasticsearch-specification/blob/main/specification/_spec_utils/behaviors.ts)).
The interface in the specification ```js export interface CommonQueryParameters { error_trace?: boolean filter_path?: string | string[] human?: boolean pretty?: boolean } ``` The converted url params ```json "error_trace": "__flag__", "filter_path": [], "human": "__flag__", "pretty": "__flag__", ```
- Previously existing `url_components` property in the definitions is deleted and this change will be addressed separately. (not sure it is currently working but added a task to the meta issue) - Previously numbers were configured as `0` or `0.0` but that is not currently displayed in suggestions. Instead, the new script converts numbers to strings and if any default value is present, it will be displayed as a suggestion. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../README.md | 11 +- .../src/generate_console_definitions.ts | 98 ++-- .../src/generate_query_params.test.ts | 375 +++++++++++++++ .../src/generate_query_params.ts | 219 +++++++++ .../types/autocomplete_definition_types.ts | 23 + .../src/types/index.ts | 15 + .../src/types/specification_types.ts | 442 ++++++++++++++++++ .../src/utils.ts | 17 + 8 files changed, 1131 insertions(+), 69 deletions(-) create mode 100644 packages/kbn-generate-console-definitions/src/generate_query_params.test.ts create mode 100644 packages/kbn-generate-console-definitions/src/generate_query_params.ts create mode 100644 packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts create mode 100644 packages/kbn-generate-console-definitions/src/types/index.ts create mode 100644 packages/kbn-generate-console-definitions/src/types/specification_types.ts create mode 100644 packages/kbn-generate-console-definitions/src/utils.ts diff --git a/packages/kbn-generate-console-definitions/README.md b/packages/kbn-generate-console-definitions/README.md index 71596b6fdf0b..75d43fd7c493 100644 --- a/packages/kbn-generate-console-definitions/README.md +++ b/packages/kbn-generate-console-definitions/README.md @@ -1,3 +1,10 @@ -# @kbn/generate-console-definitions +# Generate console definitions +This package is a script to generate definitions used in Console to display autocomplete suggestions. The script is +a new implementation of `kbn-spec-to-console` package: The old script uses [JSON specs](https://github.com/elastic/elasticsearch/tree/main/rest-api-spec) from the Elasticsearch repo as the source, whereas this script uses the Elasticsearch specification [repo](https://github.com/elastic/elasticsearch-specification) as the source. + +## Instructions +1. Checkout the Elasticsearch specification [repo](https://github.com/elastic/elasticsearch-specification). +2. Run the command `node scripts/generate_console_definitions.js --source --emptyDest` + This command will use the folder `` as the source and the constant [`AUTOCOMPLETE_DEFINITIONS_FOLDER`](https://github.com/elastic/kibana/blob/main/src/plugins/console/common/constants/autocomplete_definitions.ts) as the destination. Based on the value of the constant, the autocomplete definitions will be generated in the folder `/src/plugins/server/lib/spec_definitions/json/generated`. Using the flag `--emptyDest` will remove any existing files in the destination folder. +3. It's possible to generate the definitions into a different folder. For that pass an option to the command `--dest ` and also update the constant [`AUTOCOMPLETE_DEFINITIONS_FOLDER`](https://github.com/elastic/kibana/blob/main/src/plugins/console/common/constants/autocomplete_definitions.ts) so that the Console server will load the definitions from this folder. -Empty package generated by @kbn/generate diff --git a/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts b/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts index f5bb7cd687c7..1d4918f44c6b 100644 --- a/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts +++ b/packages/kbn-generate-console-definitions/src/generate_console_definitions.ts @@ -9,51 +9,16 @@ import fs from 'fs'; import Path, { join } from 'path'; import { ToolingLog } from '@kbn/tooling-log'; - -interface EndpointRequest { - name: string; - namespace: string; -} - -interface Endpoint { - name: string; - urls: Array<{ - methods: string[]; - path: string; - }>; - docUrl: string; - request: null | EndpointRequest; -} - -interface SchemaType { - name: { - name: string; - namespace: string; - }; -} - -interface Schema { - endpoints: Endpoint[]; - types: SchemaType[]; -} - -interface UrlParams { - [key: string]: number | string; -} - -interface BodyParams { - [key: string]: number | string; -} - -interface Definition { - documentation?: string; - methods: string[]; - patterns: string[]; - url_params?: UrlParams; - data_autocomplete_rules?: BodyParams; -} - -const generateMethods = (endpoint: Endpoint): string[] => { +import { generateQueryParams } from './generate_query_params'; +import type { + AutocompleteBodyParams, + AutocompleteDefinition, + AutocompleteUrlParams, + SpecificationTypes, +} from './types'; +import { findTypeDefinition } from './utils'; + +const generateMethods = (endpoint: SpecificationTypes.Endpoint): string[] => { // this array consists of arrays of strings const methodsArray = endpoint.urls.map((url) => url.methods); // flatten to return array of strings @@ -62,7 +27,7 @@ const generateMethods = (endpoint: Endpoint): string[] => { return [...new Set(flattenMethodsArray)]; }; -const generatePatterns = (endpoint: Endpoint): string[] => { +const generatePatterns = (endpoint: SpecificationTypes.Endpoint): string[] => { return endpoint.urls.map(({ path }) => { let pattern = path; // remove leading / if present @@ -73,42 +38,37 @@ const generatePatterns = (endpoint: Endpoint): string[] => { }); }; -const generateDocumentation = (endpoint: Endpoint): string => { +const generateDocumentation = (endpoint: SpecificationTypes.Endpoint): string => { return endpoint.docUrl; }; const generateParams = ( - endpoint: Endpoint, - schema: Schema -): { urlParams: UrlParams; bodyParams: BodyParams } | undefined => { + endpoint: SpecificationTypes.Endpoint, + schema: SpecificationTypes.Model +): { urlParams: AutocompleteUrlParams; bodyParams: AutocompleteBodyParams } | undefined => { const { request } = endpoint; if (!request) { return; } - const requestType = schema.types.find( - ({ name: { name, namespace } }) => name === request.name && namespace === request.namespace - ); + const requestType = findTypeDefinition(schema, request); if (!requestType) { return; } - - const urlParams = generateUrlParams(requestType); + const urlParams = generateQueryParams(requestType as SpecificationTypes.Request, schema); const bodyParams = generateBodyParams(requestType); return { urlParams, bodyParams }; }; -const generateUrlParams = (requestType: SchemaType): UrlParams => { - return {}; -}; - -const generateBodyParams = (requestType: SchemaType): BodyParams => { +const generateBodyParams = ( + requestType: SpecificationTypes.TypeDefinition +): AutocompleteBodyParams => { return {}; }; const addParams = ( - definition: Definition, - params: { urlParams: UrlParams; bodyParams: BodyParams } -): Definition => { + definition: AutocompleteDefinition, + params: { urlParams: AutocompleteUrlParams; bodyParams: AutocompleteBodyParams } +): AutocompleteDefinition => { const { urlParams, bodyParams } = params; if (urlParams && Object.keys(urlParams).length > 0) { definition.url_params = urlParams; @@ -119,15 +79,19 @@ const addParams = ( return definition; }; -const generateDefinition = (endpoint: Endpoint, schema: Schema): Definition => { +const generateDefinition = ( + endpoint: SpecificationTypes.Endpoint, + schema: SpecificationTypes.Model +): AutocompleteDefinition => { const methods = generateMethods(endpoint); const patterns = generatePatterns(endpoint); const documentation = generateDocumentation(endpoint); - let definition: Definition = { methods, patterns, documentation }; + let definition: AutocompleteDefinition = {}; const params = generateParams(endpoint, schema); if (params) { definition = addParams(definition, params); } + definition = { ...definition, methods, patterns, documentation }; return definition; }; @@ -143,7 +107,7 @@ export function generateConsoleDefinitions({ }) { const pathToSchemaFile = Path.resolve(specsRepo, 'output/schema/schema.json'); log.info('loading the ES specification schema file'); - const schema = JSON.parse(fs.readFileSync(pathToSchemaFile, 'utf8')) as Schema; + const schema = JSON.parse(fs.readFileSync(pathToSchemaFile, 'utf8')) as SpecificationTypes.Model; const { endpoints } = schema; log.info(`iterating over endpoints array: ${endpoints.length} endpoints`); @@ -151,7 +115,7 @@ export function generateConsoleDefinitions({ const { name } = endpoint; log.info(name); const definition = generateDefinition(endpoint, schema); - const fileContent: { [name: string]: Definition } = { + const fileContent: { [name: string]: AutocompleteDefinition } = { [name]: definition, }; fs.writeFileSync( diff --git a/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts b/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts new file mode 100644 index 000000000000..3d658ba60f17 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/generate_query_params.test.ts @@ -0,0 +1,375 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { SpecificationTypes } from './types'; +import { generateQueryParams } from './generate_query_params'; +import { UrlParamValue } from './types/autocomplete_definition_types'; + +describe('generateQueryParams', () => { + const mockRequestType: SpecificationTypes.Request = { + body: { kind: 'no_body' }, + kind: 'request', + name: { + name: 'TestRequest', + namespace: 'test.namespace', + }, + path: [], + query: [], + specLocation: '', + }; + + const getMockProperty = ({ + propertyName, + typeName, + serverDefault, + type, + }: { + propertyName: string; + typeName?: SpecificationTypes.TypeName; + serverDefault?: SpecificationTypes.Property['serverDefault']; + type?: SpecificationTypes.ValueOf; + }): SpecificationTypes.Property => { + return { + description: 'Description', + name: propertyName, + required: false, + serverDefault: serverDefault ?? undefined, + type: type ?? { + kind: 'instance_of', + type: typeName ?? { + name: 'string', + namespace: '_builtins', + }, + }, + }; + }; + + const mockSchema: SpecificationTypes.Model = { + endpoints: [], + types: [], + }; + + it('iterates over attachedBehaviours', () => { + const behaviour1: SpecificationTypes.Interface = { + kind: 'interface', + name: { + name: 'behaviour1', + namespace: 'test.namespace', + }, + properties: [getMockProperty({ propertyName: 'property1' })], + specLocation: '', + }; + const behaviour2: SpecificationTypes.Interface = { + kind: 'interface', + name: { + name: 'behaviour2', + namespace: 'test.namespace', + }, + properties: [ + getMockProperty({ propertyName: 'property2' }), + getMockProperty({ propertyName: 'property3' }), + ], + specLocation: '', + }; + const schema: SpecificationTypes.Model = { + ...mockSchema, + types: [behaviour1, behaviour2], + }; + const requestType: SpecificationTypes.Request = { + ...mockRequestType, + attachedBehaviors: ['behaviour1', 'behaviour2'], + }; + const urlParams = generateQueryParams(requestType, schema); + expect(urlParams).toEqual({ + property1: '', + property2: '', + property3: '', + }); + }); + + it('iterates over query properties', () => { + const requestType = { + ...mockRequestType, + query: [ + getMockProperty({ propertyName: 'property1' }), + getMockProperty({ propertyName: 'property2' }), + ], + }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + property1: '', + property2: '', + }); + }); + + it('converts builtin types', () => { + const stringProperty = getMockProperty({ + propertyName: 'stringProperty', + typeName: { name: 'string', namespace: '_builtins' }, + }); + const numberProperty = getMockProperty({ + propertyName: 'numberProperty', + typeName: { name: 'number', namespace: '_builtins' }, + }); + const booleanProperty = getMockProperty({ + propertyName: 'booleanProperty', + typeName: { name: 'boolean', namespace: '_builtins' }, + }); + const requestType = { + ...mockRequestType, + query: [stringProperty, numberProperty, booleanProperty], + }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + stringProperty: '', + numberProperty: '', + booleanProperty: '__flag__', + }); + }); + + it('adds serverDefault value if any', () => { + const propertyWithDefault = getMockProperty({ + propertyName: 'propertyWithDefault', + serverDefault: 'default', + }); + const requestType = { ...mockRequestType, query: [propertyWithDefault] }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + propertyWithDefault: ['default'], + }); + }); + + it('converts an enum property', () => { + const enumProperty = getMockProperty({ + propertyName: 'enumProperty', + typeName: { name: 'EnumType', namespace: 'test.namespace' }, + }); + const enumType: SpecificationTypes.Enum = { + kind: 'enum', + members: [ + { + name: 'enum1', + }, + { + name: 'enum2', + }, + ], + name: { + name: 'EnumType', + namespace: 'test.namespace', + }, + specLocation: '', + }; + const requestType = { ...mockRequestType, query: [enumProperty] }; + const schema = { ...mockSchema, types: [enumType] }; + const urlParams = generateQueryParams(requestType, schema); + expect(urlParams).toEqual({ + enumProperty: ['enum1', 'enum2'], + }); + }); + + it('converts a type alias', () => { + const typeAliasProperty = getMockProperty({ + propertyName: 'typeAliasProperty', + typeName: { + name: 'SomeTypeAlias', + namespace: 'test.namespace', + }, + }); + const typeAliasType: SpecificationTypes.TypeAlias = { + kind: 'type_alias', + name: { + name: 'SomeTypeAlias', + namespace: 'test.namespace', + }, + specLocation: '', + type: { + kind: 'instance_of', + type: { + name: 'integer', + namespace: '_types', + }, + }, + }; + const requestType = { ...mockRequestType, query: [typeAliasProperty] }; + const schema: SpecificationTypes.Model = { ...mockSchema, types: [typeAliasType] }; + const urlParams = generateQueryParams(requestType, schema); + expect(urlParams).toEqual({ + typeAliasProperty: '', + }); + }); + + it('converts a literal_value to a string', () => { + const stringProperty = getMockProperty({ + propertyName: 'stringProperty', + type: { kind: 'literal_value', value: 'stringValue' }, + }); + const numberProperty = getMockProperty({ + propertyName: 'numberProperty', + type: { kind: 'literal_value', value: 14 }, + }); + const booleanProperty = getMockProperty({ + propertyName: 'booleanProperty', + type: { kind: 'literal_value', value: true }, + }); + const requestType = { + ...mockRequestType, + query: [stringProperty, numberProperty, booleanProperty], + }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + stringProperty: ['stringValue'], + numberProperty: ['14'], + booleanProperty: ['true'], + }); + }); + + describe('converts a union_of', () => { + it('flattens the array if one of the items is converted to an array', () => { + const enumType: SpecificationTypes.Enum = { + kind: 'enum', + members: [ + { + name: 'enum1', + }, + { name: 'enum2' }, + ], + name: { name: 'EnumType', namespace: 'test.namespace' }, + specLocation: '', + }; + const unionProperty = getMockProperty({ + propertyName: 'unionProperty', + type: { + kind: 'union_of', + items: [ + { + kind: 'instance_of', + type: { + name: 'EnumType', + namespace: 'test.namespace', + }, + }, + ], + }, + }); + const requestType = { ...mockRequestType, query: [unionProperty] }; + const schema: SpecificationTypes.Model = { ...mockSchema, types: [enumType] }; + const urlParams = generateQueryParams(requestType, schema); + expect(urlParams).toEqual({ + unionProperty: ['enum1', 'enum2'], + }); + }); + + it('removes empty string from the array', () => { + const unionProperty = getMockProperty({ + propertyName: 'unionProperty', + type: { + kind: 'union_of', + items: [ + { + kind: 'instance_of', + type: { + name: 'string', + namespace: '_builtins', + }, + }, + ], + }, + }); + const requestType = { ...mockRequestType, query: [unionProperty] }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + unionProperty: [], + }); + }); + + it('if one item is a boolean and others are empty, converts to a flag', () => { + const unionProperty = getMockProperty({ + propertyName: 'unionProperty', + type: { + kind: 'union_of', + items: [ + { + kind: 'instance_of', + type: { + name: 'string', + namespace: '_builtins', + }, + }, + { + kind: 'instance_of', + type: { + name: 'number', + namespace: '_builtins', + }, + }, + { + kind: 'instance_of', + type: { + name: 'boolean', + namespace: '_builtins', + }, + }, + ], + }, + }); + const requestType = { ...mockRequestType, query: [unionProperty] }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + unionProperty: '__flag__', + }); + }); + + it('if one item is an unknown type, converts it to an empty string', () => { + const unionProperty = getMockProperty({ + propertyName: 'unionProperty', + type: { + kind: 'union_of', + items: [ + { + kind: 'literal_value', + value: 'test', + }, + { + kind: 'instance_of', + type: { + name: 'UnknownType', + namespace: 'test.namespace', + }, + }, + ], + }, + }); + + const requestType = { ...mockRequestType, query: [unionProperty] }; + const urlParams = generateQueryParams(requestType, mockSchema); + // check that no `undefined` values are added + const value = urlParams.unionProperty as UrlParamValue[]; + expect(value.length).toEqual(1); + }); + }); + + it('converts an unknown type to an empty string', () => { + const unknownTypeProperty = getMockProperty({ + propertyName: 'unknownTypeProperty', + type: { + kind: 'instance_of', + type: { + name: 'UnknownType', + namespace: 'test.namespace', + }, + }, + }); + + const requestType = { ...mockRequestType, query: [unknownTypeProperty] }; + const urlParams = generateQueryParams(requestType, mockSchema); + expect(urlParams).toEqual({ + unknownTypeProperty: '', + }); + }); +}); diff --git a/packages/kbn-generate-console-definitions/src/generate_query_params.ts b/packages/kbn-generate-console-definitions/src/generate_query_params.ts new file mode 100644 index 000000000000..5310e85d6893 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/generate_query_params.ts @@ -0,0 +1,219 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * Types that are important for query params conversion: + * TypeDefinition = Interface | Request | Response | Enum | TypeAlias + * ValueOf = InstanceOf | ArrayOf | UnionOf | DictionaryOf | UserDefinedValue | LiteralValue; + * + * Conversion steps: + * 1. The schema has a property `endpoints` which is "Endpoint[]" + * 2. Each "Endpoint" has a property `request` which is "TypeName" + * 3. Using "TypeName" we find the "TypeDefinition" in the property `types` of the schema + * 4. the "TypeDefinition" is cast to "Request" + * - "Request" has a property `query` which is "Property[]" + * - "Request" has a property `attachedBehaviours` which is "string[]" + * With "string" we find a "TypeDefinition" that is "Interface" + * This "Interface" has a property `properties` which is "Property[]" + * 5. Each "Property" (from both `query` and `attachedBehaviours`) now can be converted + * 6. Each "Property" has a property `type` that is "ValueOf" + * 7. If "ValueOf" can be one of "InstanceOf", "ArrayOf", "UnionOf", "DictionaryOf", "UserDefinedValue", "LiteralValue" + * - "InstanceOf": it has a property `type` which is a "TypeName" + * - if "TypeName" has a `namespace` = "_builtins" then it's a primitive type like "string" -> convert according to set rules for primitives + * - if "TypeName" has a `namespace` = "_types" then it's a defined type that can be found in the schema + * - the found "TypeDefinition" can be either "Enum" or "TypeAlias" (not "Interface", "Request" or "Response") + * - if it's "TypeAlias", it has a property `type` which is "ValueOf" -> handle it as "ValueOf" (recursion) + * - if it's "Enum", it has a property `members` which is "EnumMember[]" -> convert each "EnumMember" (only need `name` property) + * - "ArrayOf": it has a property `value` which is "ValueOf" -> convert as "ValueOf" + * - "UnionOf": it has a property `items` which is "ValueOf[]" -> convert each as "ValueOf" + * - "DictionaryOf": not used for query params + * - "UserDefinedValue": not used for query params + * - "LiteralValue": it has `value` that is `string`, `number` or `boolean` + * + * Autocomplete definitions currently work with 2 url param types: + * - "__flag__" for a boolean (suggesting value 'true' and 'false') + * - list of options in an array, for example ['30s', '-1', '0'], suggesting all 3 values in a list + * If there is only a default value, we need to wrap it in an array, so that this value is displayed in a suggestion (similar to the list). + * Numbers need to be converted to strings, otherwise they are not displayed as suggestions. + * + */ + +import { UrlParamValue } from './types/autocomplete_definition_types'; +import type { AutocompleteUrlParams, SpecificationTypes } from './types'; +import { findTypeDefinition } from './utils'; + +const booleanFlagString = '__flag__'; +const trueValueString = String(true); +const falseValueString = String(false); + +export const generateQueryParams = ( + requestType: SpecificationTypes.Request, + schema: SpecificationTypes.Model +): AutocompleteUrlParams => { + let urlParams = {} as AutocompleteUrlParams; + const { types } = schema; + const { attachedBehaviors, query } = requestType; + // if there are any attached behaviors, iterate over each and find its type + if (attachedBehaviors) { + for (const attachedBehavior of attachedBehaviors) { + const foundBehavior = types.find((type) => type.name.name === attachedBehavior); + if (foundBehavior) { + // attached behaviours are interfaces + const behaviorType = foundBehavior as SpecificationTypes.Interface; + // if there are any properties in the behavior, iterate over each and add it to url params + const { properties } = behaviorType; + urlParams = convertProperties(properties, urlParams, schema); + } + } + } + + // iterate over properties in query and add it to url params + urlParams = convertProperties(query, urlParams, schema); + + return urlParams; +}; + +const convertProperties = ( + properties: SpecificationTypes.Property[], + urlParams: AutocompleteUrlParams, + schema: SpecificationTypes.Model +): AutocompleteUrlParams => { + for (const property of properties) { + const { name, serverDefault, type } = property; + // property has `type` which is `ValueOf` + const convertedValue = convertValueOf(type, serverDefault, schema); + urlParams[name] = convertedValue ?? ''; + } + return urlParams; +}; + +const convertValueOf = ( + valueOf: SpecificationTypes.ValueOf, + serverDefault: SpecificationTypes.Property['serverDefault'], + schema: SpecificationTypes.Model +): UrlParamValue | undefined => { + const { kind } = valueOf; + if (kind === 'instance_of') { + return convertInstanceOf(valueOf, serverDefault, schema); + } else if (kind === 'array_of') { + return convertArrayOf(valueOf, serverDefault, schema); + } else if (kind === 'union_of') { + return convertUnionOf(valueOf, serverDefault, schema); + } else if (kind === 'literal_value') { + return convertLiteralValue(valueOf); + } + // for query params we can ignore 'dictionary_of' and 'user_defined_value' + return ''; +}; + +const convertInstanceOf = ( + type: SpecificationTypes.InstanceOf, + serverDefault: SpecificationTypes.Property['serverDefault'], + schema: SpecificationTypes.Model +): UrlParamValue | undefined => { + const { type: typeName } = type; + const { name: propertyName, namespace } = typeName; + if (namespace === '_builtins') { + /** + * - `string` + * - `boolean` + * - `number` + * - `null` // ignore for query params + * - `void` // ignore for query params + * - `binary` // ignore for query params + */ + + if (propertyName === 'boolean') { + // boolean is converted to a flag param + return booleanFlagString; + } else { + // if default value, convert to string and put in an array + return serverDefault ? [serverDefault.toString()] : ''; + } + } else { + // if it's a defined type, try to convert it + const definedType = findTypeDefinition(schema, typeName); + if (definedType) { + // TypeDefinition can only be Enum or TypeAlias + if (definedType.kind === 'enum') { + return convertEnum(definedType as SpecificationTypes.Enum); + } else if (definedType.kind === 'type_alias') { + const aliasValueOf = definedType.type; + return convertValueOf(aliasValueOf, serverDefault, schema); + } + } + } + return ''; +}; + +const convertArrayOf = ( + type: SpecificationTypes.ArrayOf, + serverDefault: SpecificationTypes.Property['serverDefault'], + schema: SpecificationTypes.Model +): UrlParamValue | undefined => { + const { value } = type; + // simply convert the value of an array item + return convertValueOf(value, serverDefault, schema); +}; + +const convertUnionOf = ( + type: SpecificationTypes.UnionOf, + serverDefault: SpecificationTypes.Property['serverDefault'], + schema: SpecificationTypes.Model +): UrlParamValue | undefined => { + const { items } = type; + const itemValues = new Set(); + for (const item of items) { + // each item is ValueOf + const convertedValue = convertValueOf(item, serverDefault, schema); + // flatten array if needed + if (convertedValue instanceof Array) { + convertedValue.forEach((v) => itemValues.add(v)); + } else itemValues.add(convertedValue); + } + + // if an empty string is in values, delete it + if (itemValues.has('')) { + itemValues.delete(''); + } + + // if there is a flag in the values, convert it to "true" + "false" + if (itemValues.size > 1 && itemValues.has(booleanFlagString)) { + itemValues.delete(booleanFlagString); + itemValues.add(trueValueString); + itemValues.add(falseValueString); + } + + // if only 2 values ("true","false"), convert back to a flag + // that can happen if the values before were ("true", "__flag__") or ("false", "__flag__") + if ( + itemValues.size === 2 && + itemValues.has(trueValueString) && + itemValues.has(falseValueString) + ) { + itemValues.clear(); + itemValues.add(booleanFlagString); + } + + // if only 1 element that is a flag, don't put it in an array + if (itemValues.size === 1 && itemValues.has(booleanFlagString)) { + return itemValues.values().next().value; + } + return [...itemValues] as UrlParamValue; +}; + +const convertLiteralValue = (type: SpecificationTypes.LiteralValue): UrlParamValue | undefined => { + // convert the value to a string + return [type.value.toString()]; +}; + +const convertEnum = (enumDefinition: SpecificationTypes.Enum): UrlParamValue => { + const { members } = enumDefinition; + // only need the `name` property + return members.map((member) => member.name); +}; diff --git a/packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts b/packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts new file mode 100644 index 000000000000..edbb9bd74d9a --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/types/autocomplete_definition_types.ts @@ -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 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 or the Server + * Side Public License, v 1. + */ +export type UrlParamValue = number | string | number[] | string[] | boolean; +export interface AutocompleteUrlParams { + [key: string]: UrlParamValue; +} + +export interface AutocompleteBodyParams { + [key: string]: number | string; +} + +export interface AutocompleteDefinition { + documentation?: string; + methods?: string[]; + patterns?: string[]; + url_params?: AutocompleteUrlParams; + data_autocomplete_rules?: AutocompleteBodyParams; +} diff --git a/packages/kbn-generate-console-definitions/src/types/index.ts b/packages/kbn-generate-console-definitions/src/types/index.ts new file mode 100644 index 000000000000..a18259235068 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/types/index.ts @@ -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 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 or the Server + * Side Public License, v 1. + */ + +export type { + AutocompleteDefinition, + AutocompleteUrlParams, + AutocompleteBodyParams, +} from './autocomplete_definition_types'; + +export * as SpecificationTypes from './specification_types'; diff --git a/packages/kbn-generate-console-definitions/src/types/specification_types.ts b/packages/kbn-generate-console-definitions/src/types/specification_types.ts new file mode 100644 index 000000000000..b9e61ded0644 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/types/specification_types.ts @@ -0,0 +1,442 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * --------------- THIS FILE IS COPIED FROM ES SPECIFICATION REPO ------------------- + * + */ + +/** + * The name of a type, composed of a simple name and a namespace. Hierarchical namespace elements are separated by + * a dot, e.g 'cat.cat_aliases'. + * + * Builtin namespaces: + * - "generic" for type names that are generic parameter values from the enclosing type. + * - "internal" for primitive and builtin types (e.g. Id, IndexName, etc) + * Builtin types: + * - boolean, + * - string, + * - number: a 64bits floating point number. Additional types will be added for integers. + * - null: the null value. Since JS distinguishes undefined and null, some APIs make use of this value. + * - object: used to represent "any". We may forbid it at some point. UserDefinedValue should be used for user data. + */ +export interface TypeName { + namespace: string; + name: string; +} + +// ------------------------------------------------------------------------------------------------ +// Value types + +// Note: "required" is part of Property. This means we can have optional properties but we can't have null entries in +// containers (array and dictionary), which doesn't seem to be needed. +// +// The 'kind' property is used to tag and disambiguate union type members, and allow type-safe pattern matching in TS: +// see https://blog.logrocket.com/pattern-matching-and-type-safety-in-typescript-1da1231a2e34/ +// and https://medium.com/@fillopeter/pattern-matching-with-typescript-done-right-94049ddd671c + +/** + * Type of a value. Used both for property types and nested type definitions. + */ +export type ValueOf = + | InstanceOf + | ArrayOf + | UnionOf + | DictionaryOf + | UserDefinedValue + | LiteralValue; + +/** + * A single value + */ +export interface InstanceOf { + kind: 'instance_of'; + type: TypeName; + /** generic parameters: either concrete types or open parameters from the enclosing type */ + generics?: ValueOf[]; +} + +/** + * An array + */ +export interface ArrayOf { + kind: 'array_of'; + value: ValueOf; +} + +/** + * One of several possible types which don't necessarily have a common superinterface + */ +export interface UnionOf { + kind: 'union_of'; + items: ValueOf[]; +} + +/** + * A dictionary (or map). The key is a string or a number (or a union thereof), possibly through an alias. + * + * If `singleKey` is true, then this dictionary can only have a single key. This is a common pattern in ES APIs, + * used to associate a value to a field name or some other identifier. + */ +export interface DictionaryOf { + kind: 'dictionary_of'; + key: ValueOf; + value: ValueOf; + singleKey: boolean; +} + +/** + * A user defined value. To be used when bubbling a generic parameter up to the top-level interface is + * inconvenient or impossible (e.g. for lists of user-defined values of possibly different types). + * + * Clients will allow providing a serializer/deserializer when reading/writing properties of this type, + * and should also accept raw json. + * + * Think twice before using this as it defeats the purpose of a strongly typed API, and deserialization + * will also require to buffer raw JSON data which may have performance implications. + */ +export interface UserDefinedValue { + kind: 'user_defined_value'; +} + +/** + * A literal value. This is used for tagged unions, where each type member of a union has a 'type' + * attribute that defines its kind. This metamodel heavily uses this approach with its 'kind' attributes. + * + * It may later be used to set a property to a constant value, which is why it accepts not only strings but also + * other primitive types. + */ +export interface LiteralValue { + kind: 'literal_value'; + value: string | number | boolean; +} + +/** + * An interface or request interface property. + */ +export interface Property { + name: string; + type: ValueOf; + required: boolean; + description?: string; + docUrl?: string; + docId?: string; + since?: string; + serverDefault?: boolean | string | number | string[] | number[]; + deprecation?: Deprecation; + availability?: Availabilities; + stability?: Stability; + /** + * If specified takes precedence over `name` when generating code. `name` is always the value + * to be sent over the wire + */ + codegenName?: string; + /** An optional set of aliases for `name` */ + aliases?: string[]; + /** If the enclosing interface is a variants container, is this a property of the container and not a variant? */ + containerProperty?: boolean; + /** If this property has a quirk that needs special attention, give a short explanation about it */ + esQuirk?: string; +} + +// ------------------------------------------------------------------------------------------------ +// Type definitions + +export type TypeDefinition = Interface | Request | Response | Enum | TypeAlias; + +// ------------------------------------------------------------------------------------------------ + +/** + * Common attributes for all type definitions + */ +export interface BaseType { + name: TypeName; + description?: string; + /** Link to public documentation */ + docUrl?: string; + docId?: string; + deprecation?: Deprecation; + /** If this endpoint has a quirk that needs special attention, give a short explanation about it */ + esQuirk?: string; + kind: string; + /** Variant name for externally tagged variants */ + variantName?: string; + /** + * Additional identifiers for use by code generators. Usage depends on the actual type: + * - on unions (modeled as alias(union_of)), these are identifiers for the union members + * - for additional properties, this is the name of the dict that holds these properties + * - for additional property, this is the name of the key and value fields that hold the + * additional property + */ + codegenNames?: string[]; + /** + * Location of an item. The path is relative to the "specification" directory, e.g "_types/common.ts#L1-L2" + */ + specLocation: string; +} + +export type Variants = ExternalTag | InternalTag | Container; + +export interface VariantBase { + /** + * Is this variant type open to extensions? Default to false. Used for variants that can + * be extended with plugins. If true, target clients should allow for additional variants + * with a variant tag outside the ones defined in the spec and arbitrary data as the value. + */ + nonExhaustive?: boolean; +} + +export interface ExternalTag extends VariantBase { + kind: 'external_tag'; +} + +export interface InternalTag extends VariantBase { + kind: 'internal_tag'; + /* Name of the property that holds the variant tag */ + tag: string; + /* Default value for the variant tag if it's missing */ + defaultTag?: string; +} + +export interface Container extends VariantBase { + kind: 'container'; +} + +/** + * Inherits clause (aka extends or implements) for an interface or request + */ +export interface Inherits { + type: TypeName; + generics?: ValueOf[]; +} + +/** + * An interface type + */ +export interface Interface extends BaseType { + kind: 'interface'; + /** + * Open generic parameters. The name is that of the parameter, the namespace is an arbitrary value that allows + * this fully qualified type name to be used when this open generic parameter is used in property's type. + */ + generics?: TypeName[]; + inherits?: Inherits; + implements?: Inherits[]; + + /** + * Behaviors directly implemented by this interface + */ + behaviors?: Inherits[]; + + /** + * Behaviors attached to this interface, coming from the interface itself (see `behaviors`) + * or from inherits and implements ancestors + */ + attachedBehaviors?: string[]; + properties: Property[]; + /** + * The property that can be used as a shortcut for the entire data structure in the JSON. + */ + shortcutProperty?: string; + + /** Identify containers */ + variants?: Container; +} + +/** + * A request type + */ +export interface Request extends BaseType { + // Note: does not extend Interface as properties are split across path, query and body + kind: 'request'; + generics?: TypeName[]; + /** The parent defines additional body properties that are added to the body, that has to be a PropertyBody */ + inherits?: Inherits; + implements?: Inherits[]; + /** URL path properties */ + path: Property[]; + /** Query string properties */ + query: Property[]; + // FIXME: we need an annotation that lists query params replaced by a body property so that we can skip them. + // Examples on _search: sort -> sort, _source -> (_source, _source_include, _source_exclude) + // Or can we say that implicitly a body property replaces all path params starting with its name? + // Is there a priority rule between path and body parameters? + // + // We can also pull path parameter descriptions on body properties they replace + + /** + * Body type. Most often a list of properties (that can extend those of the inherited interface, see above), except for a + * few specific cases that use other types such as bulk (array) or create (generic parameter). Or NoBody for requests + * that don't have a body. + */ + body: Body; + behaviors?: Inherits[]; + attachedBehaviors?: string[]; +} + +/** + * A response type + */ +export interface Response extends BaseType { + kind: 'response'; + generics?: TypeName[]; + body: Body; + behaviors?: Inherits[]; + attachedBehaviors?: string[]; + exceptions?: ResponseException[]; +} + +export interface ResponseException { + description?: string; + body: Body; + statusCodes: number[]; +} + +export type Body = ValueBody | PropertiesBody | NoBody; + +export interface ValueBody { + kind: 'value'; + value: ValueOf; + codegenName?: string; +} + +export interface PropertiesBody { + kind: 'properties'; + properties: Property[]; +} + +export interface NoBody { + kind: 'no_body'; +} + +/** + * An enumeration member. + * + * When enumeration members can become ambiguous when translated to an identifier, the `name` property will be a good + * identifier name, and `stringValue` will be the string value to use on the wire. + * See DateMathTimeUnit for an example of this, which have members for "m" (minute) and "M" (month). + */ +export interface EnumMember { + /** The identifier to use for this enum */ + name: string; + /** An optional set of aliases for `name` */ + aliases?: string[]; + /** + * If specified takes precedence over `name` when generating code. `name` is always the value + * to be sent over the wire + */ + codegenName?: string; + description?: string; + deprecation?: Deprecation; + since?: string; +} + +/** + * An enumeration + */ +export interface Enum extends BaseType { + kind: 'enum'; + /** + * If the enum is open, it means that other than the specified values it can accept an arbitrary value. + * If this property is not present, it means that the enum is not open (in other words, is closed). + */ + isOpen?: boolean; + members: EnumMember[]; +} + +/** + * An alias for an existing type. + */ +export interface TypeAlias extends BaseType { + kind: 'type_alias'; + type: ValueOf; + /** generic parameters: either concrete types or open parameters from the enclosing type */ + generics?: TypeName[]; + /** Only applicable to `union_of` aliases: identify typed_key unions (external) and variant inventories (internal) */ + variants?: InternalTag | ExternalTag; +} + +// ------------------------------------------------------------------------------------------------ + +export enum Stability { + stable = 'stable', + beta = 'beta', + experimental = 'experimental', +} +export enum Visibility { + public = 'public', + feature_flag = 'feature_flag', + private = 'private', +} + +export interface Deprecation { + version: string; + description: string; +} + +export interface Availabilities { + stack?: Availability; + serverless?: Availability; +} + +export interface Availability { + since?: string; + featureFlag?: string; + stability?: Stability; + visibility?: Visibility; +} + +export interface Endpoint { + name: string; + description: string; + docUrl: string; + docId?: string; + deprecation?: Deprecation; + availability: Availabilities; + + /** + * If the request value is `null` it means that there is not yet a + * request type definition for this endpoint. + */ + request: TypeName | null; + requestBodyRequired: boolean; // Not sure this is useful + + /** + * If the response value is `null` it means that there is not yet a + * response type definition for this endpoint. + */ + response: TypeName | null; + + urls: UrlTemplate[]; + + /** + * The version when this endpoint reached its current stability level. + * Missing data means "forever", i.e. before any of the target client versions produced from this spec. + */ + since?: string; + stability?: Stability; + visibility?: Visibility; + featureFlag?: string; + requestMediaType?: string[]; + responseMediaType?: string[]; + privileges?: { + index?: string[]; + cluster?: string[]; + }; +} + +export interface UrlTemplate { + path: string; + methods: string[]; + deprecation?: Deprecation; +} + +export interface Model { + types: TypeDefinition[]; + endpoints: Endpoint[]; +} diff --git a/packages/kbn-generate-console-definitions/src/utils.ts b/packages/kbn-generate-console-definitions/src/utils.ts new file mode 100644 index 000000000000..29c24e63fa58 --- /dev/null +++ b/packages/kbn-generate-console-definitions/src/utils.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 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 or the Server + * Side Public License, v 1. + */ + +import type { SpecificationTypes } from './types'; +export const findTypeDefinition = ( + schema: SpecificationTypes.Model, + typeName: SpecificationTypes.TypeName +): SpecificationTypes.TypeDefinition | undefined => { + return schema.types.find( + (type) => type.name.name === typeName.name && type.name.namespace === typeName.namespace + ); +}; From f758ba47507fb3ec18ffbaeab32ff36ea828dd2c Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Wed, 5 Jul 2023 12:39:58 +0200 Subject: [PATCH 10/89] Use alert details page URL for the log threshold rule if the config is enabled (#161175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #161117 ## Summary If `xpack.observability.unsafe.alertDetails.logs.enabled` is enabled, we will use the new alert details page URL in `context.alertDetailsUrl` otherwise, we send the user to the alerts page filtered for that alert. (Partially brings back [the logic for alert details URL](https://github.com/elastic/kibana/pull/157987/files#diff-a71ca536380c1fde8805744b23566ce795707f92b94a03af73347cac46ccac63L1027) and [getAlertDetailsConfig](https://github.com/elastic/kibana/pull/157987/files#diff-a71ca536380c1fde8805744b23566ce795707f92b94a03af73347cac46ccac63L1027)) ## 🧪 How to test 1. Set `xpack.observability.unsafe.alertDetails.logs.enabled` as false in Kibana yml config or remove the config 2. Create a log threshold rule with an action for both active state and recovered state 3. When the alert is triggered, check the default message, it should include the alertDetailsURL, by clicking on that, you should land on the alerts page filtered for that alert 4. Make the alert recovered and check and similar URL should be generated New alert details page: 1. Set `xpack.observability.unsafe.alertDetails.logs.enabled` as true in Kibana yml config 2. Repeat the steps 2,3,4 as mentioned before 3. This time, you should land on the new alert details page ![image](https://github.com/elastic/kibana/assets/12370520/a2f99bd7-cfaa-4146-bedf-72458973b463) --- .../infra/server/lib/alerting/common/utils.ts | 10 +++++ .../log_threshold/log_threshold_executor.ts | 43 ++++++++++++------- .../plugins/infra/server/lib/infra_types.ts | 2 + x-pack/plugins/infra/server/plugin.ts | 1 + 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/infra/server/lib/alerting/common/utils.ts b/x-pack/plugins/infra/server/lib/alerting/common/utils.ts index 8377756654a9..dc518e1e08cf 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/utils.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/utils.ts @@ -9,6 +9,7 @@ import { isEmpty, isError } from 'lodash'; import { schema } from '@kbn/config-schema'; import { Logger, LogMeta } from '@kbn/logging'; import type { ElasticsearchClient, IBasePath } from '@kbn/core/server'; +import { ObservabilityConfig } from '@kbn/observability-plugin/server'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/common'; import { ALERT_RULE_PARAMETERS, TIMESTAMP } from '@kbn/rule-data-utils'; import { @@ -109,6 +110,15 @@ export const createScopedLogger = ( }; }; +export const getAlertDetailsPageEnabledForApp = ( + config: ObservabilityConfig['unsafe']['alertDetails'] | null, + appName: keyof ObservabilityConfig['unsafe']['alertDetails'] +): boolean => { + if (!config) return false; + + return config[appName].enabled; +}; + export const getViewInInventoryAppUrl = ({ basePath, criteria, diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index 9b93e021ae8f..e36ae54746b3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -7,7 +7,11 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { i18n } from '@kbn/i18n'; -import { getAlertUrl, AlertsLocatorParams } from '@kbn/observability-plugin/common'; +import { + AlertsLocatorParams, + getAlertDetailsUrl, + getAlertUrl, +} from '@kbn/observability-plugin/common'; import { ALERT_CONTEXT, ALERT_EVALUATION_THRESHOLD, @@ -59,6 +63,7 @@ import { InfraBackendLibs } from '../../infra_types'; import { AdditionalContext, flattenAdditionalContext, + getAlertDetailsPageEnabledForApp, getContextForRecoveredAlerts, getGroupByObject, unflattenObject, @@ -133,6 +138,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => getAlertByAlertUuid, } = services; const { basePath, alertsLocator } = libs; + const config = libs.getAlertDetailsConfig(); const alertFactory: LogThresholdAlertFactory = ( id, @@ -183,13 +189,15 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => alert.scheduleActions(actionGroup, { ...sharedContext, ...context, - alertDetailsUrl: await getAlertUrl( - alertUuid, - spaceId, - indexedStartedAt, - libs.alertsLocator, - libs.basePath.publicBaseUrl - ), + alertDetailsUrl: getAlertDetailsPageEnabledForApp(config, 'logs') + ? getAlertDetailsUrl(libs.basePath, spaceId, alertUuid) + : await getAlertUrl( + alertUuid, + spaceId, + indexedStartedAt, + libs.alertsLocator, + libs.basePath.publicBaseUrl + ), }); }); } @@ -246,6 +254,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => validatedParams, getAlertByAlertUuid, alertsLocator, + isAlertDetailsPageEnabled: getAlertDetailsPageEnabledForApp(config, 'logs'), }); } catch (e) { throw new Error(e); @@ -859,6 +868,7 @@ const processRecoveredAlerts = async ({ validatedParams, getAlertByAlertUuid, alertsLocator, + isAlertDetailsPageEnabled = false, }: { basePath: IBasePath; getAlertStartedDate: (alertId: string) => string | null; @@ -871,6 +881,7 @@ const processRecoveredAlerts = async ({ alertUuid: string ) => Promise | null> | null; alertsLocator?: LocatorPublic; + isAlertDetailsPageEnabled?: boolean; }) => { const groupByKeysObjectForRecovered = getGroupByObject( validatedParams.groupBy, @@ -887,13 +898,15 @@ const processRecoveredAlerts = async ({ const viewInAppUrl = addSpaceIdToPath(basePath.publicBaseUrl, spaceId, relativeViewInAppUrl); const baseContext = { - alertDetailsUrl: await getAlertUrl( - alertUuid, - spaceId, - indexedStartedAt, - alertsLocator, - basePath.publicBaseUrl - ), + alertDetailsUrl: isAlertDetailsPageEnabled + ? getAlertDetailsUrl(basePath, spaceId, alertUuid) + : await getAlertUrl( + alertUuid, + spaceId, + indexedStartedAt, + alertsLocator, + basePath.publicBaseUrl + ), group: hasGroupBy(validatedParams) ? recoveredAlertId : null, groupByKeys: groupByKeysObjectForRecovered[recoveredAlertId], timestamp: startedAt.toISOString(), diff --git a/x-pack/plugins/infra/server/lib/infra_types.ts b/x-pack/plugins/infra/server/lib/infra_types.ts index e31bad1b5ffe..6bb24c722fe8 100644 --- a/x-pack/plugins/infra/server/lib/infra_types.ts +++ b/x-pack/plugins/infra/server/lib/infra_types.ts @@ -9,6 +9,7 @@ import type { Logger } from '@kbn/logging'; import type { IBasePath } from '@kbn/core/server'; import type { handleEsError } from '@kbn/es-ui-shared-plugin/server'; import type { AlertsLocatorParams } from '@kbn/observability-plugin/common'; +import { ObservabilityConfig } from '@kbn/observability-plugin/server'; import type { LocatorPublic } from '@kbn/share-plugin/common'; import type { ILogsSharedLogEntriesDomain } from '@kbn/logs-shared-plugin/server'; import { RulesServiceSetup } from '../services/rules'; @@ -33,6 +34,7 @@ export interface InfraBackendLibs extends InfraDomainLibs { metricsRules: RulesServiceSetup; sources: InfraSources; sourceStatus: InfraSourceStatus; + getAlertDetailsConfig: () => ObservabilityConfig['unsafe']['alertDetails']; getStartServices: InfraPluginStartServicesAccessor; handleEsError: typeof handleEsError; logger: Logger; diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 2449293dfa3a..07bafb4781e7 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -193,6 +193,7 @@ export class InfraServerPlugin logsRules: this.logsRules.setup(core, plugins), metricsRules: this.metricsRules.setup(core, plugins), getStartServices: () => core.getStartServices(), + getAlertDetailsConfig: () => plugins.observability.getAlertDetailsConfig(), logger: this.logger, basePath: core.http.basePath, alertsLocator: plugins.share.url.locators.get(alertsLocatorID), From 16528cf28994b30642262a018c89c5217c28f884 Mon Sep 17 00:00:00 2001 From: Wafaa Nasr Date: Wed, 5 Jul 2023 11:44:03 +0100 Subject: [PATCH 11/89] [Security Solution] [Exceptions] Amend `Rule Exception's Comment` text (#161092) ## Summary - Addresses Docs team comment https://github.com/elastic/kibana/pull/159908#discussion_r1247964658 --- .../e2e/exceptions/alerts_table_flow/rule_exceptions.cy.ts | 4 ++-- .../components/add_exception_flyout/translations.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions.cy.ts index 59af4592d2eb..0861bf1231e2 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions.cy.ts @@ -169,7 +169,7 @@ describe('Rule Exceptions workflows from Alert', () => { */ validateExceptionCommentCountAndText( 1, - 'Exception conditions are pre-filled with relevant data from alert with "id"' + 'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id):' ); addExceptionFlyoutItemName(ITEM_NAME); @@ -207,7 +207,7 @@ describe('Rule Exceptions workflows from Alert', () => { */ validateExceptionCommentCountAndText( 1, - 'Exception conditions are pre-filled with relevant data from alert with "id"' + 'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id):' ); addExceptionFlyoutItemName(ITEM_NAME); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/translations.ts index 215bb3fc2992..d27d7994bb37 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/translations.ts @@ -90,6 +90,6 @@ export const ADD_RULE_EXCEPTION_FROM_ALERT_COMMENT = (alertId: string) => { values: { alertId }, defaultMessage: - 'Exception conditions are pre-filled with relevant data from alert with "id" {alertId}.', + 'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id): {alertId}.', } ); From f43601d294a3d789c0aa40892c80e9e40859cab6 Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 5 Jul 2023 13:33:09 +0200 Subject: [PATCH 12/89] [Cases] Validate page and perPage parameters in find APIs (#161111) Connected to https://github.com/elastic/kibana/issues/146945 ## Summary | Description | Limit | Done? | Documented? | ------------- | ---- | :---: | ---- | | Total number of cases/user actions/comments per page | 100 | :white_check_mark: | No | N/A | | Maximum number of cases/user actions/comments returned from the API | 10.000 | :white_check_mark: | No | N/A | ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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 ### Release Notes Max value for perPage parameter in find Cases API is now 100. Max value for perPage parameter in find User Actions API is now 100. Max value for perPage parameter in find Comments API is now 100. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: lcawl --- .../cases/case-apis-passthru.asciidoc | 17 +- x-pack/plugins/cases/common/api/cases/case.ts | 218 +++++++++--------- .../cases/common/api/cases/comment/index.ts | 32 ++- .../api/cases/user_actions/operations/find.ts | 20 +- .../plugins/cases/common/constants/index.ts | 2 + .../plugins/cases/common/schema/index.test.ts | 164 +++++++++++-- x-pack/plugins/cases/common/schema/index.ts | 42 ++++ x-pack/plugins/cases/common/schema/types.ts | 20 ++ .../plugins/cases/docs/openapi/bundled.json | 45 ++-- .../plugins/cases/docs/openapi/bundled.yaml | 24 +- .../components/parameters/page_size.yaml | 3 +- ...id}@api@cases@{caseid}@comments@_find.yaml | 9 +- .../server/client/attachments/get.test.ts | 6 +- .../cases/server/client/attachments/get.ts | 3 - .../client/attachments/validators.test.ts | 52 ----- .../server/client/attachments/validators.ts | 24 +- .../cases/server/client/cases/find.test.ts | 23 +- .../server/client/user_actions/find.test.ts | 41 +++- .../tests/common/cases/find_cases.ts | 79 ++++--- .../tests/common/comments/find_comments.ts | 4 +- .../common/user_actions/find_user_actions.ts | 19 ++ 21 files changed, 505 insertions(+), 342 deletions(-) create mode 100644 x-pack/plugins/cases/common/schema/types.ts delete mode 100644 x-pack/plugins/cases/server/client/attachments/validators.test.ts diff --git a/docs/api-generated/cases/case-apis-passthru.asciidoc b/docs/api-generated/cases/case-apis-passthru.asciidoc index f35863d23eb8..897018655903 100644 --- a/docs/api-generated/cases/case-apis-passthru.asciidoc +++ b/docs/api-generated/cases/case-apis-passthru.asciidoc @@ -859,7 +859,7 @@ Any modifications made to this file will be overwritten.
Query Parameter — The page number to return. default: 1
perPage (optional)
-
Query Parameter — The number of items to return. default: 20
sortOrder (optional)
+
Query Parameter — The number of items to return. Limited to 100 items. default: 20
sortOrder (optional)
Query Parameter — Determines the sort order. default: desc
types (optional)
@@ -951,7 +951,7 @@ Any modifications made to this file will be overwritten.
Query Parameter — The page number to return. default: 1
perPage (optional)
-
Query Parameter — The number of items to return. default: 20
sortOrder (optional)
+
Query Parameter — The number of items to return. Limited to 100 items. default: 20
sortOrder (optional)
Query Parameter — Determines the sort order. default: desc
types (optional)
@@ -1278,7 +1278,7 @@ Any modifications made to this file will be overwritten.
Query Parameter — The page number to return. default: 1
perPage (optional)
-
Query Parameter — The number of items to return. default: 20
reporters (optional)
+
Query Parameter — The number of items to return. Limited to 100 items. default: 20
reporters (optional)
Query Parameter — Filters the returned cases by the user name of the reporter. default: null
search (optional)
@@ -1471,7 +1471,7 @@ Any modifications made to this file will be overwritten.
Query Parameter — The page number to return. default: 1
perPage (optional)
-
Query Parameter — The number of items to return. default: 20
reporters (optional)
+
Query Parameter — The number of items to return. Limited to 100 items. default: 20
reporters (optional)
Query Parameter — Filters the returned cases by the user name of the reporter. default: null
search (optional)
@@ -4156,7 +4156,6 @@ Any modifications made to this file will be overwritten.
  • findCaseConnectorsDefaultSpace_200_response_inner_config -
  • findCasesDefaultSpace_200_response -
  • findCasesDefaultSpace_assignees_parameter -
  • -
  • findCasesDefaultSpace_category_parameter -
  • findCasesDefaultSpace_owner_parameter -
  • findCasesDefaultSpace_searchFields_parameter -
  • getCaseCommentDefaultSpace_200_response -
  • @@ -4580,6 +4579,7 @@ Any modifications made to this file will be overwritten.
    settings
    severity (optional)
    tags
    array[String] The words and phrases that help categorize cases. It can be an empty array.
    +
    category (optional)
    String Category for the case. It could be a word or a phrase to categorize the case.
    title
    String A title for the case.

    @@ -4659,12 +4659,6 @@ Any modifications made to this file will be overwritten.
    -

    findCasesDefaultSpace_owner_parameter - Up

    @@ -5056,6 +5050,7 @@ Any modifications made to this file will be overwritten.
    severity (optional)
    status (optional)
    tags (optional)
    array[String] The words and phrases that help categorize cases.
    +
    category (optional)
    String Category for the case. It could be a word or a phrase to categorize the case.
    title (optional)
    String A title for the case.
    version
    String The current version of the case. To determine this value, use the get case or find cases APIs.
    diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 977819546543..ff6b8416f24d 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -7,13 +7,17 @@ import * as rt from 'io-ts'; -import { NumberFromString } from '../saved_object'; import { UserRt } from '../user'; import { CommentRt } from './comment'; import { CasesStatusResponseRt, CaseStatusRt } from './status'; import { CaseConnectorRt } from '../connectors/connector'; import { CaseAssigneesRt } from './assignee'; -import { limitedArraySchema, limitedStringSchema, NonEmptyString } from '../../schema'; +import { + limitedArraySchema, + limitedStringSchema, + NonEmptyString, + paginationSchema, +} from '../../schema'; import { MAX_DELETE_IDS_LENGTH, MAX_DESCRIPTION_LENGTH, @@ -25,6 +29,7 @@ import { MAX_REPORTERS_FILTER_LENGTH, MAX_TAGS_FILTER_LENGTH, MAX_BULK_GET_CASES, + MAX_CASES_PER_PAGE, } from '../../constants'; export const AttachmentTotalsRt = rt.strict({ @@ -227,110 +232,113 @@ const CasesFindRequestSearchFieldsRt = rt.keyof({ 'updated_by.profile_uid': null, }); -export const CasesFindRequestRt = rt.exact( - rt.partial({ - /** - * Tags to filter by - */ - tags: rt.union([ - limitedArraySchema({ - codec: rt.string, - fieldName: 'tags', - min: 0, - max: MAX_TAGS_FILTER_LENGTH, - }), - rt.string, - ]), - /** - * The status of the case (open, closed, in-progress) - */ - status: CaseStatusRt, - /** - * The severity of the case - */ - severity: CaseSeverityRt, - /** - * The uids of the user profiles to filter by - */ - assignees: rt.union([ - limitedArraySchema({ - codec: rt.string, - fieldName: 'assignees', - min: 0, - max: MAX_ASSIGNEES_FILTER_LENGTH, - }), - rt.string, - ]), - /** - * The reporters to filter by - */ - reporters: rt.union([ - limitedArraySchema({ - codec: rt.string, - fieldName: 'reporters', - min: 0, - max: MAX_REPORTERS_FILTER_LENGTH, - }), - rt.string, - ]), - /** - * Operator to use for the `search` field - */ - defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]), - /** - * A KQL date. If used all cases created after (gte) the from date will be returned - */ - from: rt.string, - /** - * The page of objects to return - */ - page: NumberFromString, - /** - * The number of objects to include in each page - */ - perPage: NumberFromString, - /** - * An Elasticsearch simple_query_string - */ - search: rt.string, - /** - * The fields to perform the simple_query_string parsed query against - */ - searchFields: rt.union([ - rt.array(CasesFindRequestSearchFieldsRt), - CasesFindRequestSearchFieldsRt, - ]), - /** - * The root fields to perform the simple_query_string parsed query against - */ - rootSearchFields: rt.array(rt.string), - /** - * The field to use for sorting the found objects. - * - */ - sortField: rt.string, - /** - * The order to sort by - */ - sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]), +export const CasesFindRequestRt = rt.intersection([ + rt.exact( + rt.partial({ + /** + * Tags to filter by + */ + tags: rt.union([ + limitedArraySchema({ + codec: rt.string, + fieldName: 'tags', + min: 0, + max: MAX_TAGS_FILTER_LENGTH, + }), + rt.string, + ]), + /** + * The status of the case (open, closed, in-progress) + */ + status: CaseStatusRt, + /** + * The severity of the case + */ + severity: CaseSeverityRt, + /** + * The uids of the user profiles to filter by + */ + assignees: rt.union([ + limitedArraySchema({ + codec: rt.string, + fieldName: 'assignees', + min: 0, + max: MAX_ASSIGNEES_FILTER_LENGTH, + }), + rt.string, + ]), + /** + * The reporters to filter by + */ + reporters: rt.union([ + limitedArraySchema({ + codec: rt.string, + fieldName: 'reporters', + min: 0, + max: MAX_REPORTERS_FILTER_LENGTH, + }), + rt.string, + ]), + /** + * Operator to use for the `search` field + */ + defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]), + /** + * A KQL date. If used all cases created after (gte) the from date will be returned + */ + from: rt.string, + /** + * The page of objects to return + */ + // page: rt.union([rt.number, NumberFromString]), + /** + * The number of objects to include in each page + */ + // perPage: rt.union([rt.number, NumberFromString]), + /** + * An Elasticsearch simple_query_string + */ + search: rt.string, + /** + * The fields to perform the simple_query_string parsed query against + */ + searchFields: rt.union([ + rt.array(CasesFindRequestSearchFieldsRt), + CasesFindRequestSearchFieldsRt, + ]), + /** + * The root fields to perform the simple_query_string parsed query against + */ + rootSearchFields: rt.array(rt.string), + /** + * The field to use for sorting the found objects. + * + */ + sortField: rt.string, + /** + * The order to sort by + */ + sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]), - /** - * A KQL date. If used all cases created before (lte) the to date will be returned. - */ - to: rt.string, - /** - * The owner(s) to filter by. The user making the request must have privileges to retrieve cases of that - * ownership or they will be ignored. If no owner is included, then all ownership types will be included in the response - * that the user has access to. - */ + /** + * A KQL date. If used all cases created before (lte) the to date will be returned. + */ + to: rt.string, + /** + * The owner(s) to filter by. The user making the request must have privileges to retrieve cases of that + * ownership or they will be ignored. If no owner is included, then all ownership types will be included in the response + * that the user has access to. + */ - owner: rt.union([rt.array(rt.string), rt.string]), - /** - * The category of the case. - */ - category: rt.union([rt.array(rt.string), rt.string]), - }) -); + owner: rt.union([rt.array(rt.string), rt.string]), + /** + * The category of the case. + */ + category: rt.union([rt.array(rt.string), rt.string]), + }) + ), + paginationSchema({ maxPerPage: MAX_CASES_PER_PAGE }), +]); export const CasesDeleteRequestRt = limitedArraySchema({ codec: NonEmptyString, @@ -532,8 +540,8 @@ export type Case = rt.TypeOf; export type CaseResolveResponse = rt.TypeOf; export type Cases = rt.TypeOf; export type CasesDeleteRequest = rt.TypeOf; -export type CasesFindRequest = rt.TypeOf; export type CasesByAlertIDRequest = rt.TypeOf; +export type CasesFindRequest = rt.TypeOf; export type CasesFindResponse = rt.TypeOf; export type CasePatchRequest = rt.TypeOf; export type CasesPatchRequest = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/cases/comment/index.ts b/x-pack/plugins/cases/common/api/cases/comment/index.ts index 1662631ae2b7..f53e7d882893 100644 --- a/x-pack/plugins/cases/common/api/cases/comment/index.ts +++ b/x-pack/plugins/cases/common/api/cases/comment/index.ts @@ -6,10 +6,9 @@ */ import * as rt from 'io-ts'; -import { MAX_BULK_GET_ATTACHMENTS } from '../../../constants'; -import { limitedArraySchema } from '../../../schema'; +import { MAX_BULK_GET_ATTACHMENTS, MAX_COMMENTS_PER_PAGE } from '../../../constants'; +import { limitedArraySchema, paginationSchema } from '../../../schema'; import { jsonValueRt } from '../../runtime_types'; -import { NumberFromString } from '../../saved_object'; import { UserRt } from '../../user'; @@ -289,22 +288,17 @@ export const CommentsFindResponseRt = rt.strict({ export const CommentsRt = rt.array(CommentRt); -export const FindCommentsQueryParamsRt = rt.exact( - rt.partial({ - /** - * The page of objects to return - */ - page: rt.union([rt.number, NumberFromString]), - /** - * The number of objects to return for a page - */ - perPage: rt.union([rt.number, NumberFromString]), - /** - * Order to sort the response - */ - sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]), - }) -); +export const FindCommentsQueryParamsRt = rt.intersection([ + rt.exact( + rt.partial({ + /** + * Order to sort the response + */ + sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]), + }) + ), + paginationSchema({ maxPerPage: MAX_COMMENTS_PER_PAGE }), +]); export const BulkCreateCommentRequestRt = rt.array(CommentRequestRt); diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts b/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts index 336c94a7a4fe..90dc408b5e88 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts @@ -6,9 +6,10 @@ */ import * as rt from 'io-ts'; +import { MAX_USER_ACTIONS_PER_PAGE } from '../../../../constants'; import { UserActionsRt } from '../response'; import { ActionTypes } from '../common'; -import { NumberFromString } from '../../../saved_object'; +import { paginationSchema } from '../../../../schema'; const AdditionalFilterTypes = { action: 'action', @@ -26,14 +27,15 @@ const FindTypeFieldRt = rt.keyof(FindTypes); export type FindTypeField = rt.TypeOf; -export const UserActionFindRequestRt = rt.exact( - rt.partial({ - types: rt.array(FindTypeFieldRt), - sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]), - page: NumberFromString, - perPage: NumberFromString, - }) -); +export const UserActionFindRequestRt = rt.intersection([ + rt.exact( + rt.partial({ + types: rt.array(FindTypeFieldRt), + sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]), + }) + ), + paginationSchema({ maxPerPage: MAX_USER_ACTIONS_PER_PAGE }), +]); export type UserActionFindRequest = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/constants/index.ts b/x-pack/plugins/cases/common/constants/index.ts index b7b498704249..ffd627c8de86 100644 --- a/x-pack/plugins/cases/common/constants/index.ts +++ b/x-pack/plugins/cases/common/constants/index.ts @@ -105,6 +105,8 @@ export const MAX_BULK_GET_ATTACHMENTS = 100 as const; export const MAX_CONCURRENT_SEARCHES = 10 as const; export const MAX_BULK_GET_CASES = 1000 as const; export const MAX_COMMENTS_PER_PAGE = 100 as const; +export const MAX_CASES_PER_PAGE = 100 as const; +export const MAX_USER_ACTIONS_PER_PAGE = 100 as const; export const MAX_CATEGORY_FILTER_LENGTH = 100 as const; export const MAX_TAGS_FILTER_LENGTH = 100 as const; export const MAX_ASSIGNEES_FILTER_LENGTH = 100 as const; diff --git a/x-pack/plugins/cases/common/schema/index.test.ts b/x-pack/plugins/cases/common/schema/index.test.ts index 1a810c8003ff..008cbc86cae8 100644 --- a/x-pack/plugins/cases/common/schema/index.test.ts +++ b/x-pack/plugins/cases/common/schema/index.test.ts @@ -7,7 +7,8 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; -import { limitedArraySchema, limitedStringSchema, NonEmptyString } from '.'; +import { limitedArraySchema, limitedStringSchema, NonEmptyString, paginationSchema } from '.'; +import { MAX_DOCS_PER_PAGE } from '../constants'; describe('schema', () => { describe('limitedArraySchema', () => { @@ -19,10 +20,10 @@ describe('schema', () => { limitedArraySchema({ codec: NonEmptyString, fieldName, min: 1, max: 1 }).decode(['']) ) ).toMatchInlineSnapshot(` - Array [ - "string must have length >= 1", - ] - `); + Array [ + "string must have length >= 1", + ] + `); }); it('fails when given an empty array', () => { @@ -31,10 +32,10 @@ describe('schema', () => { limitedArraySchema({ codec: NonEmptyString, fieldName, min: 1, max: 1 }).decode([]) ) ).toMatchInlineSnapshot(` - Array [ - "The length of the field foobar is too short. Array must be of length >= 1.", - ] - `); + Array [ + "The length of the field foobar is too short. Array must be of length >= 1.", + ] + `); }); it('fails when given an array larger than the limit of one item', () => { @@ -46,10 +47,10 @@ describe('schema', () => { ]) ) ).toMatchInlineSnapshot(` - Array [ - "The length of the field foobar is too long. Array must be of length <= 1.", - ] - `); + Array [ + "The length of the field foobar is too long. Array must be of length <= 1.", + ] + `); }); it('succeeds when given an array of 1 item with a non-empty string', () => { @@ -58,10 +59,10 @@ describe('schema', () => { limitedArraySchema({ codec: NonEmptyString, fieldName, min: 1, max: 1 }).decode(['a']) ) ).toMatchInlineSnapshot(` - Array [ - "No errors!", - ] - `); + Array [ + "No errors!", + ] + `); }); it('succeeds when given an array of 0 item with a non-empty string when the min is 0', () => { @@ -70,10 +71,10 @@ describe('schema', () => { limitedArraySchema({ codec: NonEmptyString, fieldName, min: 0, max: 2 }).decode([]) ) ).toMatchInlineSnapshot(` - Array [ - "No errors!", - ] - `); + Array [ + "No errors!", + ] + `); }); }); @@ -84,7 +85,7 @@ describe('schema', () => { expect(PathReporter.report(limitedStringSchema({ fieldName, min: 2, max: 1 }).decode('a'))) .toMatchInlineSnapshot(` Array [ - "The length of the ${fieldName} is too short. The minimum length is 2.", + "The length of the foo is too short. The minimum length is 2.", ] `); }); @@ -93,7 +94,7 @@ describe('schema', () => { expect(PathReporter.report(limitedStringSchema({ fieldName, min: 1, max: 1 }).decode(''))) .toMatchInlineSnapshot(` Array [ - "The ${fieldName} field cannot be an empty string.", + "The foo field cannot be an empty string.", ] `); }); @@ -102,7 +103,7 @@ describe('schema', () => { expect(PathReporter.report(limitedStringSchema({ fieldName, min: 1, max: 1 }).decode(' '))) .toMatchInlineSnapshot(` Array [ - "The ${fieldName} field cannot be an empty string.", + "The foo field cannot be an empty string.", ] `); }); @@ -114,7 +115,7 @@ describe('schema', () => { ) ).toMatchInlineSnapshot(` Array [ - "The length of the ${fieldName} is too long. The maximum length is 5.", + "The length of the foo is too long. The maximum length is 5.", ] `); }); @@ -167,4 +168,117 @@ describe('schema', () => { `); }); }); + + describe('paginationSchema', () => { + it('succeeds when no page or perPage passed', () => { + expect(PathReporter.report(paginationSchema({ maxPerPage: 1 }).decode({}))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when only valid page is passed', () => { + expect(PathReporter.report(paginationSchema({ maxPerPage: 2 }).decode({ page: 0 }))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when only valid perPage is passed', () => { + expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ perPage: 1 }))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when page and perPage are passed and valid', () => { + expect( + PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: 1, perPage: 2 })) + ).toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('fails when perPage > maxPerPage', () => { + expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ perPage: 4 }))) + .toMatchInlineSnapshot(` + Array [ + "The provided perPage value is too high. The maximum allowed perPage value is 3.", + ] + `); + }); + + it(`fails when page > ${MAX_DOCS_PER_PAGE}`, () => { + expect( + PathReporter.report( + paginationSchema({ maxPerPage: 3 }).decode({ page: MAX_DOCS_PER_PAGE + 1 }) + ) + ).toMatchInlineSnapshot(` + Array [ + "The number of documents is too high. Paginating through more than 10000 documents is not possible.", + ] + `); + }); + + it(`fails when page * perPage > ${MAX_DOCS_PER_PAGE}`, () => { + expect( + PathReporter.report( + paginationSchema({ maxPerPage: 3 }).decode({ page: MAX_DOCS_PER_PAGE, perPage: 2 }) + ) + ).toMatchInlineSnapshot(` + Array [ + "The number of documents is too high. Paginating through more than 10000 documents is not possible.", + ] + `); + }); + + it('validate params as strings work correctly', () => { + expect( + PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: '1', perPage: '2' })) + ).toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('invalid NumberFromString work correctly', () => { + expect( + PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: 'a', perPage: 'b' })) + ).toMatchInlineSnapshot(` + Array [ + "Invalid value \\"a\\" supplied to : Pagination/page: (number | NumberFromString)/0: number", + "cannot parse to a number", + "Invalid value \\"b\\" supplied to : Pagination/perPage: (number | NumberFromString)/0: number", + "cannot parse to a number", + ] + `); + }); + + it.skip('fails when page number is negative', () => { + expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: -1 }))) + .toMatchInlineSnapshot(` + Array [ + "The provided page value is too low. The minimum allowed page value is 0.", + ] + `); + }); + + it.skip('fails when perPage number is negative', () => { + expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ perPage: -1 }))) + .toMatchInlineSnapshot(` + Array [ + "The provided perPage value is too low. The minimum allowed perPage value is 0.", + ] + `); + }); + }); }); diff --git a/x-pack/plugins/cases/common/schema/index.ts b/x-pack/plugins/cases/common/schema/index.ts index 9003360ae11b..764119b06159 100644 --- a/x-pack/plugins/cases/common/schema/index.ts +++ b/x-pack/plugins/cases/common/schema/index.ts @@ -8,6 +8,10 @@ import * as rt from 'io-ts'; import { either } from 'fp-ts/lib/Either'; +import { MAX_DOCS_PER_PAGE } from '../constants'; +import type { PartialPaginationType } from './types'; +import { PaginationSchemaRt } from './types'; + export interface LimitedSchemaType { fieldName: string; min: number; @@ -92,3 +96,41 @@ export const limitedArraySchema = ({ }), rt.identity ); + +export const paginationSchema = ({ maxPerPage }: { maxPerPage: number }) => + new rt.PartialType( + 'Pagination', + PaginationSchemaRt.is, + (u, c) => + either.chain(PaginationSchemaRt.validate(u, c), (params) => { + if (params.page == null && params.perPage == null) { + return rt.success(params); + } + + const pageAsNumber = params.page ?? 0; + const perPageAsNumber = params.perPage ?? 0; + + if (perPageAsNumber > maxPerPage) { + return rt.failure( + u, + c, + `The provided perPage value is too high. The maximum allowed perPage value is ${maxPerPage}.` + ); + } + + if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > MAX_DOCS_PER_PAGE) { + return rt.failure( + u, + c, + `The number of documents is too high. Paginating through more than ${MAX_DOCS_PER_PAGE} documents is not possible.` + ); + } + + return rt.success({ + ...(params.page != null && { page: pageAsNumber }), + ...(params.perPage != null && { perPage: perPageAsNumber }), + }); + }), + rt.identity, + undefined + ); diff --git a/x-pack/plugins/cases/common/schema/types.ts b/x-pack/plugins/cases/common/schema/types.ts new file mode 100644 index 000000000000..d2ece2650425 --- /dev/null +++ b/x-pack/plugins/cases/common/schema/types.ts @@ -0,0 +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. + */ + +import * as rt from 'io-ts'; +import { NumberFromString } from '../api/saved_object'; + +const PageTypeRt = rt.union([rt.number, NumberFromString]); +type PageNumberType = rt.TypeOf; + +export interface Pagination { + page: PageNumberType; + perPage: PageNumberType; +} + +export const PaginationSchemaRt = rt.exact(rt.partial({ page: PageTypeRt, perPage: PageTypeRt })); +export type PartialPaginationType = Partial; diff --git a/x-pack/plugins/cases/docs/openapi/bundled.json b/x-pack/plugins/cases/docs/openapi/bundled.json index 4d6905006c08..c504d966cb9d 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.json +++ b/x-pack/plugins/cases/docs/openapi/bundled.json @@ -12,26 +12,18 @@ "url": "https://www.elastic.co/licensing/elastic-license" } }, - "servers": [ - { - "url": "http://localhost:5601", - "description": "local" - } - ], - "security": [ - { - "basicAuth": [] - }, - { - "apiKeyAuth": [] - } - ], "tags": [ { "name": "cases", "description": "Case APIs enable you to open and track issues." } ], + "servers": [ + { + "url": "http://localhost:5601", + "description": "local" + } + ], "paths": { "/api/cases": { "post": { @@ -3415,15 +3407,7 @@ "$ref": "#/components/parameters/page_index" }, { - "name": "perPage", - "in": "query", - "description": "The number of items to return. Limited to 100 items.", - "required": false, - "schema": { - "type": "integer", - "default": 20, - "maximum": 100 - } + "$ref": "#/components/parameters/page_size" }, { "$ref": "#/components/parameters/sort_order" @@ -3916,11 +3900,12 @@ "page_size": { "in": "query", "name": "perPage", - "description": "The number of items to return.", + "description": "The number of items to return. Limited to 100 items.", "required": false, "schema": { "type": "integer", - "default": 20 + "default": 20, + "maximum": 100 } }, "reporters": { @@ -7161,5 +7146,13 @@ ] } } - } + }, + "security": [ + { + "basicAuth": [] + }, + { + "apiKeyAuth": [] + } + ] } \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml index 7608a44f3e45..d393da4d334a 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.yaml +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -8,15 +8,12 @@ info: license: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license -servers: - - url: http://localhost:5601 - description: local -security: - - basicAuth: [] - - apiKeyAuth: [] tags: - name: cases description: Case APIs enable you to open and track issues. +servers: + - url: http://localhost:5601 + description: local paths: /api/cases: post: @@ -2085,14 +2082,7 @@ paths: parameters: - $ref: '#/components/parameters/case_id' - $ref: '#/components/parameters/page_index' - - name: perPage - in: query - description: The number of items to return. Limited to 100 items. - required: false - schema: - type: integer - default: 20 - maximum: 100 + - $ref: '#/components/parameters/page_size' - $ref: '#/components/parameters/sort_order' - $ref: '#/components/parameters/space_id' responses: @@ -2382,11 +2372,12 @@ components: page_size: in: query name: perPage - description: The number of items to return. + description: The number of items to return. Limited to 100 items. required: false schema: type: integer default: 20 + maximum: 100 reporters: in: query name: reporters @@ -4772,3 +4763,6 @@ components: isPreconfigured: false isDeprecated: false referencedByCount: 0 +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/page_size.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/page_size.yaml index f59ff5301f9c..0c78946a39fc 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/page_size.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/page_size.yaml @@ -1,7 +1,8 @@ in: query name: perPage -description: The number of items to return. +description: The number of items to return. Limited to 100 items. required: false schema: type: integer default: 20 + maximum: 100 diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments@_find.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments@_find.yaml index bb43f4dcc0b2..b1dd32a65951 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments@_find.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@comments@_find.yaml @@ -10,14 +10,7 @@ get: parameters: - $ref: '../components/parameters/case_id.yaml' - $ref: '../components/parameters/page_index.yaml' - - name: perPage - in: query - description: The number of items to return. Limited to 100 items. - required: false - schema: - type: integer - default: 20 - maximum: 100 + - $ref: '../components/parameters/page_size.yaml' - $ref: '../components/parameters/sort_order.yaml' - $ref: '../components/parameters/space_id.yaml' responses: diff --git a/x-pack/plugins/cases/server/client/attachments/get.test.ts b/x-pack/plugins/cases/server/client/attachments/get.test.ts index c2c3423b2388..d093793417c7 100644 --- a/x-pack/plugins/cases/server/client/attachments/get.test.ts +++ b/x-pack/plugins/cases/server/client/attachments/get.test.ts @@ -20,7 +20,7 @@ describe('get', () => { await expect(() => findComment({ caseID: 'mock-id', findQueryParams: { page: 209, perPage: 100 } }, clientArgs) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to find comments case id: mock-id: Error: The number of documents is too high. Paginating through more than 10,000 documents is not possible."` + `"Failed to find comments case id: mock-id: Error: The number of documents is too high. Paginating through more than 10000 documents is not possible."` ); }); @@ -28,7 +28,7 @@ describe('get', () => { await expect(() => findComment({ caseID: 'mock-id', findQueryParams: { page: 2, perPage: 9001 } }, clientArgs) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to find comments case id: mock-id: Error: The provided perPage value was too high. The maximum allowed perPage value is 100."` + `"Failed to find comments case id: mock-id: Error: The provided perPage value is too high. The maximum allowed perPage value is 100."` ); }); @@ -36,7 +36,7 @@ describe('get', () => { await expect( findComment( // @ts-expect-error: excess attribute - { caseID: 'mock-id', findQueryParams: { page: 2, perPage: 9001, foo: 'bar' } }, + { caseID: 'mock-id', findQueryParams: { page: 2, perPage: 9, foo: 'bar' } }, clientArgs ) ).rejects.toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/plugins/cases/server/client/attachments/get.ts b/x-pack/plugins/cases/server/client/attachments/get.ts index 62f647434e89..7e4bc311a657 100644 --- a/x-pack/plugins/cases/server/client/attachments/get.ts +++ b/x-pack/plugins/cases/server/client/attachments/get.ts @@ -39,7 +39,6 @@ import { createCaseError } from '../../common/error'; import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../routes/api'; import { buildFilter, combineFilters } from '../utils'; import { Operations } from '../../authorization'; -import { validateFindCommentsPagination } from './validators'; import { decodeOrThrow } from '../../../common/api/runtime_types'; const normalizeAlertResponse = (alerts: Array>): AlertResponse => @@ -124,8 +123,6 @@ export async function find( try { const queryParams = decodeWithExcessOrThrow(FindCommentsQueryParamsRt)(findQueryParams); - validateFindCommentsPagination(queryParams); - const { filter: authorizationFilter, ensureSavedObjectsAreAuthorized } = await authorization.getAuthorizationFilter(Operations.findComments); diff --git a/x-pack/plugins/cases/server/client/attachments/validators.test.ts b/x-pack/plugins/cases/server/client/attachments/validators.test.ts deleted file mode 100644 index ce6c43a665a1..000000000000 --- a/x-pack/plugins/cases/server/client/attachments/validators.test.ts +++ /dev/null @@ -1,52 +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 { validateFindCommentsPagination } from './validators'; -import { MAX_COMMENTS_PER_PAGE } from '../../../common/constants'; - -const ERROR_MSG = - 'The number of documents is too high. Paginating through more than 10,000 documents is not possible.'; - -const ERROR_MSG_PER_PAGE = `The provided perPage value was too high. The maximum allowed perPage value is ${MAX_COMMENTS_PER_PAGE}.`; - -describe('validators', () => { - describe('validateFindCommentsPagination', () => { - it('does not throw if only page is undefined', () => { - expect(() => validateFindCommentsPagination({ perPage: 100 })).not.toThrowError(); - }); - - it('does not throw if only perPage is undefined', () => { - expect(() => validateFindCommentsPagination({ page: 100 })).not.toThrowError(); - }); - - it('does not throw if page and perPage are defined and valid', () => { - expect(() => validateFindCommentsPagination({ page: 2, perPage: 100 })).not.toThrowError(); - }); - - it('returns if page and perPage are undefined', () => { - expect(() => validateFindCommentsPagination({})).not.toThrowError(); - }); - - it('returns if perPage < 0', () => { - expect(() => validateFindCommentsPagination({ perPage: -1 })).not.toThrowError(); - }); - - it('throws if page > 10k', () => { - expect(() => validateFindCommentsPagination({ page: 10001 })).toThrow(ERROR_MSG); - }); - - it('throws if perPage > 100', () => { - expect(() => - validateFindCommentsPagination({ perPage: MAX_COMMENTS_PER_PAGE + 1 }) - ).toThrowError(ERROR_MSG_PER_PAGE); - }); - - it('throws if page * perPage > 10k', () => { - expect(() => validateFindCommentsPagination({ page: 101, perPage: 100 })).toThrow(ERROR_MSG); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/client/attachments/validators.ts b/x-pack/plugins/cases/server/client/attachments/validators.ts index ea38fa71e702..4a66d32c31c1 100644 --- a/x-pack/plugins/cases/server/client/attachments/validators.ts +++ b/x-pack/plugins/cases/server/client/attachments/validators.ts @@ -6,12 +6,11 @@ */ import Boom from '@hapi/boom'; -import { MAX_DOCS_PER_PAGE, MAX_COMMENTS_PER_PAGE } from '../../../common/constants'; import { isCommentRequestTypeExternalReference, isCommentRequestTypePersistableState, } from '../../../common/utils/attachments'; -import type { CommentRequest, FindCommentsQueryParams } from '../../../common/api'; +import type { CommentRequest } from '../../../common/api'; import type { ExternalReferenceAttachmentTypeRegistry } from '../../attachment_framework/external_reference_registry'; import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; @@ -42,24 +41,3 @@ export const validateRegisteredAttachments = ({ ); } }; - -export const validateFindCommentsPagination = (params?: FindCommentsQueryParams) => { - if (params?.page == null && params?.perPage == null) { - return; - } - - const pageAsNumber = params.page ?? 0; - const perPageAsNumber = params.perPage ?? 0; - - if (perPageAsNumber > MAX_COMMENTS_PER_PAGE) { - throw Boom.badRequest( - `The provided perPage value was too high. The maximum allowed perPage value is ${MAX_COMMENTS_PER_PAGE}.` - ); - } - - if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > MAX_DOCS_PER_PAGE) { - throw Boom.badRequest( - 'The number of documents is too high. Paginating through more than 10,000 documents is not possible.' - ); - } -}; diff --git a/x-pack/plugins/cases/server/client/cases/find.test.ts b/x-pack/plugins/cases/server/client/cases/find.test.ts index b7d9d9117ad7..988aebd33e2a 100644 --- a/x-pack/plugins/cases/server/client/cases/find.test.ts +++ b/x-pack/plugins/cases/server/client/cases/find.test.ts @@ -10,7 +10,9 @@ import type { Case } from '../../../common/api'; import { MAX_ASSIGNEES_FILTER_LENGTH, + MAX_CASES_PER_PAGE, MAX_CATEGORY_FILTER_LENGTH, + MAX_DOCS_PER_PAGE, MAX_REPORTERS_FILTER_LENGTH, MAX_TAGS_FILTER_LENGTH, } from '../../../common/constants'; @@ -81,7 +83,7 @@ describe('find', () => { }); }); - describe('searchFields errors', () => { + describe('errors', () => { const clientArgs = createCasesClientMockArgs(); beforeEach(() => { @@ -149,5 +151,24 @@ describe('find', () => { `Error: The length of the field reporters is too long. Array must be of length <= ${MAX_REPORTERS_FILTER_LENGTH}.` ); }); + + it('Invalid total items results in error', async () => { + const findRequest = createCasesClientMockFindRequest({ page: 209, perPage: 100 }); + + await expect(find(findRequest, clientArgs)).rejects.toThrowError( + `Error: The number of documents is too high. Paginating through more than ${MAX_DOCS_PER_PAGE} documents is not possible.` + ); + }); + + it('Invalid perPage items results in error', async () => { + const findRequest = createCasesClientMockFindRequest({ + page: 1, + perPage: MAX_CASES_PER_PAGE + 1, + }); + + await expect(find(findRequest, clientArgs)).rejects.toThrowError( + `Error: The provided perPage value is too high. The maximum allowed perPage value is ${MAX_CASES_PER_PAGE}.` + ); + }); }); }); diff --git a/x-pack/plugins/cases/server/client/user_actions/find.test.ts b/x-pack/plugins/cases/server/client/user_actions/find.test.ts index a8df2782117c..e0655bc1d2d5 100644 --- a/x-pack/plugins/cases/server/client/user_actions/find.test.ts +++ b/x-pack/plugins/cases/server/client/user_actions/find.test.ts @@ -5,11 +5,12 @@ * 2.0. */ +import { MAX_DOCS_PER_PAGE, MAX_USER_ACTIONS_PER_PAGE } from '../../../common/constants'; import { createMockClient } from '../metrics/test_utils/client'; import { createCasesClientMockArgs } from '../mocks'; import { find } from './find'; -describe('addComment', () => { +describe('findUserActions', () => { const client = createMockClient(); const clientArgs = createCasesClientMockArgs(); @@ -17,10 +18,38 @@ describe('addComment', () => { jest.clearAllMocks(); }); - it('throws with excess fields', async () => { - await expect( - // @ts-expect-error: excess attribute - find({ caseId: 'test-case', params: { foo: 'bar' } }, client, clientArgs) - ).rejects.toThrow('invalid keys "foo"'); + describe('errors', () => { + it('throws with excess fields', async () => { + await expect( + // @ts-expect-error: excess attribute + find({ caseId: 'test-case', params: { foo: 'bar' } }, client, clientArgs) + ).rejects.toThrow('invalid keys "foo"'); + }); + + it(`throws when trying to fetch more than ${MAX_DOCS_PER_PAGE} items`, async () => { + await expect( + find({ caseId: 'test-case', params: { page: 209, perPage: 100 } }, client, clientArgs) + ).rejects.toThrow( + `Error: The number of documents is too high. Paginating through more than ${MAX_DOCS_PER_PAGE} documents is not possible.` + ); + }); + + it(`throws when perPage > ${MAX_USER_ACTIONS_PER_PAGE}`, async () => { + await expect( + find( + { + caseId: 'test-case', + params: { + page: 1, + perPage: MAX_USER_ACTIONS_PER_PAGE + 1, + }, + }, + client, + clientArgs + ) + ).rejects.toThrow( + `Error: The provided perPage value is too high. The maximum allowed perPage value is ${MAX_USER_ACTIONS_PER_PAGE}.` + ); + }); }); }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts index 6875bc043a6c..f4c044415190 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts @@ -11,6 +11,7 @@ import expect from '@kbn/expect'; import { CASES_URL, MAX_ASSIGNEES_FILTER_LENGTH, + MAX_CASES_PER_PAGE, MAX_CATEGORY_FILTER_LENGTH, MAX_REPORTERS_FILTER_LENGTH, MAX_TAGS_FILTER_LENGTH, @@ -341,39 +342,6 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - describe('errors', () => { - it('unhappy path - 400s when bad query supplied', async () => { - await findCases({ supertest, query: { perPage: true }, expectedHttpCode: 400 }); - }); - - for (const field of ['owner', 'tags', 'severity', 'status']) { - it(`should return a 400 when attempting to query a keyword field [${field}] when using a wildcard query`, async () => { - await findCases({ - supertest, - query: { searchFields: [field], search: 'some search string*' }, - expectedHttpCode: 400, - }); - }); - } - - for (const scenario of [ - { fieldName: 'category', sizeLimit: MAX_CATEGORY_FILTER_LENGTH }, - { fieldName: 'tags', sizeLimit: MAX_TAGS_FILTER_LENGTH }, - { fieldName: 'assignees', sizeLimit: MAX_ASSIGNEES_FILTER_LENGTH }, - { fieldName: 'reporters', sizeLimit: MAX_REPORTERS_FILTER_LENGTH }, - ]) { - it(`unhappy path - 400s when the field ${scenario.fieldName} exceeds the size limit`, async () => { - const value = Array(scenario.sizeLimit + 1).fill('foobar'); - - await findCases({ - supertest, - query: { [scenario.fieldName]: value }, - expectedHttpCode: 400, - }); - }); - } - }); - describe('search and searchField', () => { beforeEach(async () => { await createCase(supertest, postCaseReq); @@ -459,6 +427,51 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + describe('errors', () => { + it('unhappy path - 400s when bad query supplied', async () => { + await findCases({ supertest, query: { perPage: true }, expectedHttpCode: 400 }); + }); + + for (const field of ['owner', 'tags', 'severity', 'status']) { + it(`should return a 400 when attempting to query a keyword field [${field}] when using a wildcard query`, async () => { + await findCases({ + supertest, + query: { searchFields: [field], search: 'some search string*' }, + expectedHttpCode: 400, + }); + }); + } + + for (const scenario of [ + { fieldName: 'category', sizeLimit: MAX_CATEGORY_FILTER_LENGTH }, + { fieldName: 'tags', sizeLimit: MAX_TAGS_FILTER_LENGTH }, + { fieldName: 'assignees', sizeLimit: MAX_ASSIGNEES_FILTER_LENGTH }, + { fieldName: 'reporters', sizeLimit: MAX_REPORTERS_FILTER_LENGTH }, + ]) { + it(`unhappy path - 400s when the field ${scenario.fieldName} exceeds the size limit`, async () => { + const value = Array(scenario.sizeLimit + 1).fill('foobar'); + + await findCases({ + supertest, + query: { [scenario.fieldName]: value }, + expectedHttpCode: 400, + }); + }); + } + + it(`400s when perPage > ${MAX_CASES_PER_PAGE} supplied`, async () => { + await findCases({ + supertest, + query: { perPage: MAX_CASES_PER_PAGE + 1 }, + expectedHttpCode: 400, + }); + }); + + it('400s when trying to fetch more than 10,000 documents', async () => { + await findCases({ supertest, query: { page: 209, perPage: 100 }, expectedHttpCode: 400 }); + }); + }); + describe('alerts', () => { const defaultSignalsIndex = '.siem-signals-default-000001'; const signalID = '4679431ee0ba3209b6fcd60a255a696886fe0a7d18f5375de510ff5b68fa6b78'; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts index 761959db29f6..0256a0ac1e56 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/find_comments.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { CASES_URL } from '@kbn/cases-plugin/common/constants'; +import { CASES_URL, MAX_COMMENTS_PER_PAGE } from '@kbn/cases-plugin/common/constants'; import { CommentType } from '@kbn/cases-plugin/common/api'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { @@ -114,7 +114,7 @@ export default ({ getService }: FtrProviderContext): void => { { name: 'field is wrong type', queryParams: { perPage: true } }, { name: 'field is unknown', queryParams: { foo: 'bar' } }, { name: 'page > 10k', queryParams: { page: 10001 } }, - { name: 'perPage > 100', queryParams: { perPage: 101 } }, + { name: 'perPage > 100', queryParams: { perPage: MAX_COMMENTS_PER_PAGE + 1 } }, { name: 'page * perPage > 10k', queryParams: { page: 2, perPage: 9001 } }, ]) { it(`400s when ${errorScenario.name}`, async () => { diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/find_user_actions.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/find_user_actions.ts index 28bab81a70be..d5340beea28d 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/find_user_actions.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/find_user_actions.ts @@ -15,6 +15,7 @@ import { ConnectorTypes, FindTypes, } from '@kbn/cases-plugin/common/api'; +import { MAX_USER_ACTIONS_PER_PAGE } from '@kbn/cases-plugin/common/constants'; import { globalRead, noKibanaPrivileges, @@ -237,6 +238,24 @@ export default ({ getService }: FtrProviderContext): void => { expect(response.userActions[0].type).to.eql('create_case'); expect(response.userActions[0].action).to.eql('create'); }); + + it(`400s when perPage > ${MAX_USER_ACTIONS_PER_PAGE} supplied`, async () => { + await findCaseUserActions({ + caseID: theCase.id, + supertest, + options: { perPage: MAX_USER_ACTIONS_PER_PAGE + 1 }, + expectedHttpCode: 400, + }); + }); + + it('400s when trying to fetch more than 10,000 documents', async () => { + await findCaseUserActions({ + caseID: theCase.id, + supertest, + options: { page: 209, perPage: 100 }, + expectedHttpCode: 400, + }); + }); }); describe('filters using the type query parameter', () => { From f2e773d435a50215e952385dc2b86834364a0d1e Mon Sep 17 00:00:00 2001 From: Sergi Massaneda Date: Wed, 5 Jul 2023 13:51:49 +0200 Subject: [PATCH 13/89] [SecuritySolution] Rename security solution plugins (#161153) ## Summary closes: https://github.com/elastic/kibana/issues/159685 - Renaming _x-pack/plugins_: `serverless_security` -> `security_solution_serverless` `ess_security` -> `security_solution_ess` - All the related configurations and types have also been renamed. - i18n translation prefixes updated - relocation of internal `security_solution_serverless` directories to be consistent with `security_solution_ess` ### Eslint I also added the plugins in the `.eslintrc` configuration, defining the same rules as the `security_solution` plugin. All eslint errors have been addressed (mainly _type_ imports errors) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .eslintrc.js | 14 +++ .github/CODEOWNERS | 13 ++- config/serverless.security.yml | 6 +- config/serverless.yml | 2 +- docs/developer/plugin-list.asciidoc | 17 ++-- package.json | 4 +- packages/kbn-optimizer/limits.yml | 4 +- tsconfig.base.json | 8 +- x-pack/.i18nrc.json | 4 +- .../public/__mocks__/services.tsx | 23 ----- .../ess_security/public/get_started/index.tsx | 21 ----- .../ess_security/public/jest.config.js | 25 ----- x-pack/plugins/ess_security/public/plugin.ts | 49 ---------- .../plugins/security_solution/common/index.ts | 2 + .../.gitignore | 0 .../README.md | 4 +- .../common/index.ts | 4 +- .../jest.config.dev.js | 2 +- .../kibana.jsonc | 6 +- .../package.json | 4 +- .../public/breadcrumbs/breadcrumbs.test.ts | 0 .../public/breadcrumbs/breadcrumbs.ts | 12 +-- .../public/breadcrumbs/index.ts | 0 .../public/common/__mocks__/services.mock.ts | 15 +++ .../public/common/__mocks__/services.tsx | 10 ++ .../common/hooks/__mocks__/use_variation.ts | 0 .../public/common/hooks/use_variation.test.ts | 0 .../public/common/hooks/use_variation.ts | 0 .../public/common}/services.tsx | 19 ++-- .../public/get_started/images/cloud1.svg | 0 .../public/get_started/images/endpoint1.svg | 0 .../public/get_started/images/siem1.svg | 0 .../public/get_started/index.tsx | 19 ++++ .../public/get_started/landing_cards.test.tsx | 2 +- .../public/get_started/landing_cards.tsx | 4 +- .../public/get_started/lazy.tsx | 0 .../public/get_started/translations.tsx | 0 .../public/index.ts | 8 +- .../public/jest.config.js | 26 ++++++ .../security_solution_ess/public/plugin.ts | 49 ++++++++++ .../public/types.ts | 10 +- .../server/config.ts | 4 +- .../server/constants.ts | 0 .../server/index.ts | 8 +- .../server/plugin.ts | 26 +++--- .../server/types.ts | 10 +- .../tsconfig.json | 4 +- .../.gitignore | 0 .../security_solution_serverless/README.mdx | 4 + .../common/config.ts | 14 ++- .../common/index.ts | 4 +- .../common/jest.config.js | 28 ++++++ .../common/pli/pli_config.ts | 0 .../common/pli/pli_features.test.ts | 12 +-- .../common/pli/pli_features.ts | 0 .../common/product.ts | 17 ++++ .../jest.config.dev.js | 12 +++ .../kibana.jsonc | 9 +- .../package.json | 2 +- .../common/__mocks__}/services.mock.tsx | 18 ++-- .../public/common/__mocks__/services.tsx | 19 ++++ .../common}/hooks/__mocks__/use_link_props.ts | 0 .../common}/hooks/use_link_props.test.tsx | 27 +++--- .../public/common}/hooks/use_link_props.ts | 2 +- .../public/common}/hooks/use_nav_links.ts | 2 +- .../public/common}/lib/__mocks__/storage.ts | 0 .../public/common}/lib/storage.ts | 0 .../public/common/services.tsx | 10 +- .../get_started/__mocks__/card_step.tsx | 0 .../get_started/__mocks__/product_switch.tsx | 0 .../public}/get_started/__mocks__/storage.ts | 0 .../get_started/__mocks__/toggle_panel.tsx | 0 .../get_started/__mocks__/welcome_panel.tsx | 0 .../public}/get_started/card_item.test.tsx | 5 +- .../public}/get_started/card_item.tsx | 18 ++-- .../public}/get_started/card_step.test.tsx | 5 +- .../public}/get_started/card_step.tsx | 2 +- .../public}/get_started/get_started.test.tsx | 4 +- .../public}/get_started/get_started.tsx | 10 +- .../public}/get_started/helpers.test.ts | 8 +- .../public}/get_started/helpers.ts | 6 +- .../public}/get_started/images/invite.svg | 0 .../public}/get_started/images/progress.svg | 0 .../public}/get_started/images/protect.svg | 0 .../public}/get_started/images/respond.svg | 0 .../public}/get_started/images/step.svg | 0 .../public}/get_started/index.tsx | 20 ++-- .../public}/get_started/lazy.tsx | 4 +- .../get_started/product_switch.test.tsx | 4 +- .../public}/get_started/product_switch.tsx | 6 +- .../public}/get_started/reducer.test.ts | 12 +-- .../public}/get_started/reducer.tsx | 12 +-- .../public}/get_started/sections.tsx | 7 +- .../public}/get_started/storage.test.ts | 10 +- .../public}/get_started/storage.ts | 17 ++-- .../public}/get_started/toggle_panel.test.tsx | 7 +- .../public}/get_started/toggle_panel.tsx | 4 +- .../public}/get_started/translations.ts | 93 ++++++++++--------- .../public}/get_started/types.ts | 11 +-- .../get_started/use_setup_cards.test.tsx | 6 +- .../public}/get_started/use_setup_cards.tsx | 6 +- .../get_started/use_toggle_panel.test.tsx | 7 +- .../public}/get_started/use_toggle_panel.tsx | 8 +- .../get_started/welcome_panel.test.tsx | 2 +- .../public}/get_started/welcome_panel.tsx | 2 +- .../public/index.ts | 11 ++- .../public/jest.config.js | 29 ++++++ .../public}/navigation/breadcrumbs.ts | 2 +- .../public/navigation/index.ts | 29 ++++++ .../public}/navigation/links/index.ts | 0 .../public}/navigation/links/nav_links.ts | 0 .../public}/navigation/links/types.ts | 0 .../navigation/navigation_tree.test.ts | 47 +++++----- .../public}/navigation/navigation_tree.ts | 2 +- .../__mocks__/use_side_nav_items.ts | 0 .../navigation}/side_navigation/categories.ts | 0 .../navigation}/side_navigation/index.tsx | 15 +-- .../navigation}/side_navigation/lazy.tsx | 0 .../side_navigation/side_navigation.test.tsx | 17 ++-- .../side_navigation/side_navigation.tsx | 19 ++-- .../use_side_nav_items.test.tsx | 35 +++---- .../side_navigation}/use_side_nav_items.ts | 6 +- .../public/plugin.ts | 62 +++++++++++++ .../public/types.ts | 10 +- .../hooks/use_product_type_by_pli.ts | 2 +- .../public}/upselling/index.ts | 0 .../pages/generic_upselling_page.tsx | 6 +- .../pages/generic_upselling_section.tsx | 6 +- .../upselling/register_upsellings.test.tsx | 13 +-- .../public}/upselling/register_upsellings.tsx | 6 +- .../server/config.ts | 4 +- .../server/index.ts | 11 ++- .../server/plugin.ts | 24 ++--- .../server/types.ts | 12 +-- .../tsconfig.json | 2 +- x-pack/plugins/serverless_security/README.mdx | 3 - .../serverless_security/common/jest.config.js | 26 ------ .../public/common/jest.config.js | 28 ------ .../public/components/jest.config.js | 28 ------ .../public/hooks/jest.config.js | 28 ------ .../serverless_security/public/jest.config.js | 27 ------ .../public/lib/jest.config.js | 26 ------ .../serverless_security/public/plugin.ts | 71 -------------- .../test_suites/security/cypress/package.json | 2 +- yarn.lock | 16 ++-- 145 files changed, 759 insertions(+), 780 deletions(-) delete mode 100644 x-pack/plugins/ess_security/public/__mocks__/services.tsx delete mode 100644 x-pack/plugins/ess_security/public/get_started/index.tsx delete mode 100644 x-pack/plugins/ess_security/public/jest.config.js delete mode 100644 x-pack/plugins/ess_security/public/plugin.ts rename x-pack/plugins/{ess_security => security_solution_ess}/.gitignore (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/README.md (51%) rename x-pack/plugins/{ess_security => security_solution_ess}/common/index.ts (72%) rename x-pack/plugins/{serverless_security => security_solution_ess}/jest.config.dev.js (78%) rename x-pack/plugins/{ess_security => security_solution_ess}/kibana.jsonc (73%) rename x-pack/plugins/{serverless_security => security_solution_ess}/package.json (85%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/breadcrumbs/breadcrumbs.test.ts (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/breadcrumbs/breadcrumbs.ts (67%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/breadcrumbs/index.ts (100%) create mode 100644 x-pack/plugins/security_solution_ess/public/common/__mocks__/services.mock.ts create mode 100644 x-pack/plugins/security_solution_ess/public/common/__mocks__/services.tsx rename x-pack/plugins/{ess_security => security_solution_ess}/public/common/hooks/__mocks__/use_variation.ts (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/common/hooks/use_variation.test.ts (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/common/hooks/use_variation.ts (100%) rename x-pack/plugins/{ess_security/public => security_solution_ess/public/common}/services.tsx (59%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/images/cloud1.svg (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/images/endpoint1.svg (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/images/siem1.svg (100%) create mode 100644 x-pack/plugins/security_solution_ess/public/get_started/index.tsx rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/landing_cards.test.tsx (98%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/landing_cards.tsx (98%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/lazy.tsx (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/get_started/translations.tsx (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/public/index.ts (62%) create mode 100644 x-pack/plugins/security_solution_ess/public/jest.config.js create mode 100644 x-pack/plugins/security_solution_ess/public/plugin.ts rename x-pack/plugins/{ess_security => security_solution_ess}/public/types.ts (69%) rename x-pack/plugins/{ess_security => security_solution_ess}/server/config.ts (80%) rename x-pack/plugins/{ess_security => security_solution_ess}/server/constants.ts (100%) rename x-pack/plugins/{ess_security => security_solution_ess}/server/index.ts (64%) rename x-pack/plugins/{ess_security => security_solution_ess}/server/plugin.ts (50%) rename x-pack/plugins/{ess_security => security_solution_ess}/server/types.ts (73%) rename x-pack/plugins/{ess_security => security_solution_ess}/tsconfig.json (92%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/.gitignore (100%) create mode 100755 x-pack/plugins/security_solution_serverless/README.mdx rename x-pack/plugins/{serverless_security => security_solution_serverless}/common/config.ts (77%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/common/index.ts (69%) create mode 100644 x-pack/plugins/security_solution_serverless/common/jest.config.js rename x-pack/plugins/{serverless_security => security_solution_serverless}/common/pli/pli_config.ts (100%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/common/pli/pli_features.test.ts (80%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/common/pli/pli_features.ts (100%) create mode 100644 x-pack/plugins/security_solution_serverless/common/product.ts create mode 100644 x-pack/plugins/security_solution_serverless/jest.config.dev.js rename x-pack/plugins/{serverless_security => security_solution_serverless}/kibana.jsonc (74%) rename x-pack/plugins/{ess_security => security_solution_serverless}/package.json (83%) rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public/common/__mocks__}/services.mock.tsx (75%) create mode 100644 x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.tsx rename x-pack/plugins/{serverless_security/public => security_solution_serverless/public/common}/hooks/__mocks__/use_link_props.ts (100%) rename x-pack/plugins/{serverless_security/public => security_solution_serverless/public/common}/hooks/use_link_props.test.tsx (82%) rename x-pack/plugins/{serverless_security/public => security_solution_serverless/public/common}/hooks/use_link_props.ts (96%) rename x-pack/plugins/{serverless_security/public => security_solution_serverless/public/common}/hooks/use_nav_links.ts (92%) rename x-pack/plugins/{serverless_security/public => security_solution_serverless/public/common}/lib/__mocks__/storage.ts (100%) rename x-pack/plugins/{serverless_security/public => security_solution_serverless/public/common}/lib/storage.ts (100%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/public/common/services.tsx (74%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/__mocks__/card_step.tsx (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/__mocks__/product_switch.tsx (100%) rename x-pack/plugins/{serverless_security/public/lib => security_solution_serverless/public}/get_started/__mocks__/storage.ts (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/__mocks__/toggle_panel.tsx (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/__mocks__/welcome_panel.tsx (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/card_item.test.tsx (92%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/card_item.tsx (90%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/card_step.test.tsx (94%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/card_step.tsx (97%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/get_started.test.tsx (94%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/get_started.tsx (93%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/helpers.test.ts (98%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/helpers.ts (92%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/images/invite.svg (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/images/progress.svg (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/images/protect.svg (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/images/respond.svg (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/images/step.svg (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/index.tsx (53%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/lazy.tsx (80%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/product_switch.test.tsx (95%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/product_switch.tsx (91%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/reducer.test.ts (96%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/reducer.tsx (93%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/sections.tsx (95%) rename x-pack/plugins/{serverless_security/public/lib => security_solution_serverless/public}/get_started/storage.test.ts (95%) rename x-pack/plugins/{serverless_security/public/lib => security_solution_serverless/public}/get_started/storage.ts (77%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/toggle_panel.test.tsx (93%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/toggle_panel.tsx (93%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/translations.ts (58%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/types.ts (88%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/use_setup_cards.test.tsx (94%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/use_setup_cards.tsx (93%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/use_toggle_panel.test.tsx (97%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/use_toggle_panel.tsx (90%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/welcome_panel.test.tsx (96%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/get_started/welcome_panel.tsx (98%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/public/index.ts (58%) create mode 100644 x-pack/plugins/security_solution_serverless/public/jest.config.js rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public}/navigation/breadcrumbs.ts (90%) create mode 100644 x-pack/plugins/security_solution_serverless/public/navigation/index.ts rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public}/navigation/links/index.ts (100%) rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public}/navigation/links/nav_links.ts (100%) rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public}/navigation/links/types.ts (100%) rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public}/navigation/navigation_tree.test.ts (86%) rename x-pack/plugins/{serverless_security/public/common => security_solution_serverless/public}/navigation/navigation_tree.ts (98%) rename x-pack/plugins/{serverless_security/public/hooks => security_solution_serverless/public/navigation/side_navigation}/__mocks__/use_side_nav_items.ts (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public/navigation}/side_navigation/categories.ts (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public/navigation}/side_navigation/index.tsx (73%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public/navigation}/side_navigation/lazy.tsx (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public/navigation}/side_navigation/side_navigation.test.tsx (85%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public/navigation}/side_navigation/side_navigation.tsx (79%) rename x-pack/plugins/{serverless_security/public/hooks => security_solution_serverless/public/navigation/side_navigation}/use_side_nav_items.test.tsx (82%) rename x-pack/plugins/{serverless_security/public/hooks => security_solution_serverless/public/navigation/side_navigation}/use_side_nav_items.ts (95%) create mode 100644 x-pack/plugins/security_solution_serverless/public/plugin.ts rename x-pack/plugins/{serverless_security => security_solution_serverless}/public/types.ts (78%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/upselling/hooks/use_product_type_by_pli.ts (93%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/upselling/index.ts (100%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/upselling/pages/generic_upselling_page.tsx (83%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/upselling/pages/generic_upselling_section.tsx (83%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/upselling/register_upsellings.test.tsx (81%) rename x-pack/plugins/{serverless_security/public/components => security_solution_serverless/public}/upselling/register_upsellings.tsx (91%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/server/config.ts (83%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/server/index.ts (60%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/server/plugin.ts (63%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/server/types.ts (73%) rename x-pack/plugins/{serverless_security => security_solution_serverless}/tsconfig.json (95%) delete mode 100755 x-pack/plugins/serverless_security/README.mdx delete mode 100644 x-pack/plugins/serverless_security/common/jest.config.js delete mode 100644 x-pack/plugins/serverless_security/public/common/jest.config.js delete mode 100644 x-pack/plugins/serverless_security/public/components/jest.config.js delete mode 100644 x-pack/plugins/serverless_security/public/hooks/jest.config.js delete mode 100644 x-pack/plugins/serverless_security/public/jest.config.js delete mode 100644 x-pack/plugins/serverless_security/public/lib/jest.config.js delete mode 100644 x-pack/plugins/serverless_security/public/plugin.ts diff --git a/.eslintrc.js b/.eslintrc.js index be80d7550253..dacfa2d470e0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -986,7 +986,11 @@ module.exports = { 'x-pack/packages/kbn-elastic-assistant/**/*.{js,mjs,ts,tsx}', 'x-pack/packages/security-solution/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/security_solution/public/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_ess/public/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_serverless/public/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/security_solution/common/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_ess/common/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_serverless/common/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/timelines/public/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/timelines/common/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/cases/public/**/*.{js,mjs,ts,tsx}', @@ -1014,6 +1018,8 @@ module.exports = { 'x-pack/packages/kbn-elastic-assistant/**/*.{ts,tsx}', 'x-pack/packages/security-solution/**/*.{ts,tsx}', 'x-pack/plugins/security_solution/**/*.{ts,tsx}', + 'x-pack/plugins/security_solution_ess/**/*.{ts,tsx}', + 'x-pack/plugins/security_solution_serverless/**/*.{ts,tsx}', 'x-pack/plugins/timelines/**/*.{ts,tsx}', 'x-pack/plugins/cases/**/*.{ts,tsx}', ], @@ -1022,6 +1028,8 @@ module.exports = { 'x-pack/packages/kbn-elastic-assistant/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/packages/security-solution/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/plugins/security_solution/**/*.{test,mock,test_helper}.{ts,tsx}', + 'x-pack/plugins/security_solution_ess/**/*.{test,mock,test_helper}.{ts,tsx}', + 'x-pack/plugins/security_solution_serverless/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/plugins/timelines/**/*.{test,mock,test_helper}.{ts,tsx}', 'x-pack/plugins/cases/**/*.{test,mock,test_helper}.{ts,tsx}', ], @@ -1036,6 +1044,8 @@ module.exports = { 'x-pack/packages/kbn-elastic-assistant/**/*.{ts,tsx}', 'x-pack/packages/security-solution/**/*.{ts,tsx}', 'x-pack/plugins/security_solution/**/*.{ts,tsx}', + 'x-pack/plugins/security_solution_ess/**/*.{ts,tsx}', + 'x-pack/plugins/security_solution_serverless/**/*.{ts,tsx}', 'x-pack/plugins/timelines/**/*.{ts,tsx}', 'x-pack/plugins/cases/**/*.{ts,tsx}', ], @@ -1069,6 +1079,8 @@ module.exports = { 'x-pack/packages/kbn-elastic-assistant/**/*.{js,mjs,ts,tsx}', 'x-pack/packages/security-solution/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_ess/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_serverless/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/timelines/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/cases/**/*.{js,mjs,ts,tsx}', ], @@ -1163,6 +1175,8 @@ module.exports = { { files: [ 'x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_ess/**/*.{js,mjs,ts,tsx}', + 'x-pack/plugins/security_solution_serverless/**/*.{js,mjs,ts,tsx}', 'x-pack/plugins/cases/**/*.{js,mjs,ts,tsx}', ], rules: { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4248bd86c7d5..00dd762c7160 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -359,7 +359,6 @@ packages/kbn-eslint-plugin-eslint @elastic/kibana-operations packages/kbn-eslint-plugin-imports @elastic/kibana-operations packages/kbn-eslint-plugin-telemetry @elastic/actionable-observability x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security -x-pack/plugins/ess_security @elastic/security-solution src/plugins/event_annotation @elastic/kibana-visualizations x-pack/test/plugin_api_integration/plugins/event_log @elastic/response-ops x-pack/plugins/event_log @elastic/response-ops @@ -580,8 +579,10 @@ examples/search_examples @elastic/kibana-data-discovery x-pack/plugins/searchprofiler @elastic/platform-deployment-management x-pack/test/security_api_integration/packages/helpers @elastic/kibana-core x-pack/plugins/security @elastic/kibana-security +x-pack/plugins/security_solution_ess @elastic/security-solution x-pack/test/cases_api_integration/common/plugins/security_solution @elastic/response-ops x-pack/plugins/security_solution @elastic/security-solution +x-pack/plugins/security_solution_serverless @elastic/security-solution x-pack/packages/security-solution/side_nav @elastic/security-threat-hunting-explore x-pack/packages/security-solution/storybook/config @elastic/security-threat-hunting-explore x-pack/test/security_functional/plugins/test_endpoints @elastic/kibana-security @@ -609,7 +610,6 @@ x-pack/plugins/serverless @elastic/appex-sharedux x-pack/plugins/serverless_observability @elastic/appex-sharedux @elastic/apm-ui packages/serverless/project_switcher @elastic/appex-sharedux x-pack/plugins/serverless_search @elastic/enterprise-search-frontend -x-pack/plugins/serverless_security @elastic/security-solution packages/serverless/storybook/config @elastic/appex-sharedux packages/serverless/types @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core @@ -824,7 +824,7 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations #CC# /src/plugins/home/server/services/ @elastic/appex-sharedux #CC# /src/plugins/home/ @elastic/appex-sharedux #CC# /x-pack/plugins/reporting/ @elastic/appex-sharedux -#CC# /x-pack/plugins/serverless_security/ @elastic/appex-sharedux +#CC# /x-pack/plugins/security_solution_serverless/ @elastic/appex-sharedux ### Observability Plugins @@ -1034,6 +1034,11 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /x-pack/test/api_integration/apis/security_solution @elastic/security-solution #CC# /x-pack/plugins/security_solution/ @elastic/security-solution +# Security Solution Offering plugins +# TODO: assign sub directories to sub teams +/x-pack/plugins/security_solution_ess/ @elastic/security-solution +/x-pack/plugins/security_solution_serverless/ @elastic/security-solution + # Security Solution sub teams ## Security Solution sub teams - Threat Hunting Investigations @@ -1259,6 +1264,8 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience # Security design /x-pack/plugins/endpoint/**/*.scss @elastic/security-design /x-pack/plugins/security_solution/**/*.scss @elastic/security-design +/x-pack/plugins/security_solution_ess/**/*.scss @elastic/security-design +/x-pack/plugins/security_solution_serverless/**/*.scss @elastic/security-design # Logstash #CC# /x-pack/plugins/logstash/ @elastic/logstash diff --git a/config/serverless.security.yml b/config/serverless.security.yml index 5e7cd22a3500..bad71f38f24a 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -6,9 +6,9 @@ xpack.apm.enabled: false xpack.observability.enabled: false xpack.uptime.enabled: false -## Enable the Serverless Security plugin -xpack.serverless.security.enabled: true -xpack.serverless.security.productTypes: +## Enable the Security Solution Serverless plugin +xpack.securitySolutionServerless.enabled: true +xpack.securitySolutionServerless.productTypes: [ { product_line: 'security', product_tier: 'complete' }, { product_line: 'endpoint', product_tier: 'complete' }, diff --git a/config/serverless.yml b/config/serverless.yml index e3ecaaa44a8d..897b168340cd 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -16,7 +16,7 @@ migrations.zdt: runOnRoles: ["ui"] # Ess plugins -xpack.ess.security.enabled: false +xpack.securitySolutionEss.enabled: false # Management team plugins xpack.upgrade_assistant.enabled: false diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 8e001dcd6fdc..e29f0fa2ac0e 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -538,10 +538,6 @@ security and spaces filtering. |This plugin provides Kibana user interfaces for managing the Enterprise Search solution and its products, App Search and Workplace Search. -|{kib-repo}blob/{branch}/x-pack/plugins/ess_security/README.md[essSecurity] -|This plugin contains the ESS/on-prem deployments (non-serverless) customizations for Security Solution. - - |{kib-repo}blob/{branch}/x-pack/plugins/event_log/README.md[eventLog] |The event log plugin provides a persistent history of alerting and action activities. @@ -730,6 +726,15 @@ Kibana. |Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. +|{kib-repo}blob/{branch}/x-pack/plugins/security_solution_ess/README.md[securitySolutionEss] +|This plugin contains the ESS/on-prem deployments (non-serverless) specific logic for Security Solution. + + +|{kib-repo}blob/{branch}/x-pack/plugins/security_solution_serverless/README.mdx[securitySolutionServerless] +|This plugin contains configuration and code used to create a Serverless Security project. +It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. + + |{kib-repo}blob/{branch}/x-pack/plugins/serverless/README.mdx[serverless] | @@ -742,10 +747,6 @@ Kibana. |This plugin contains configuration and code used to create a Serverless Search project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. -|{kib-repo}blob/{branch}/x-pack/plugins/serverless_security/README.mdx[serverlessSecurity] -|This plugin contains configuration and code used to create a Serverless Security project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. - - |{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView] |Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time. diff --git a/package.json b/package.json index 7658626741ea..bed23b10b7cf 100644 --- a/package.json +++ b/package.json @@ -393,7 +393,6 @@ "@kbn/es-types": "link:packages/kbn-es-types", "@kbn/es-ui-shared-plugin": "link:src/plugins/es_ui_shared", "@kbn/eso-plugin": "link:x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin", - "@kbn/ess-security": "link:x-pack/plugins/ess_security", "@kbn/event-annotation-plugin": "link:src/plugins/event_annotation", "@kbn/event-log-fixture-plugin": "link:x-pack/test/plugin_api_integration/plugins/event_log", "@kbn/event-log-plugin": "link:x-pack/plugins/event_log", @@ -582,8 +581,10 @@ "@kbn/search-examples-plugin": "link:examples/search_examples", "@kbn/searchprofiler-plugin": "link:x-pack/plugins/searchprofiler", "@kbn/security-plugin": "link:x-pack/plugins/security", + "@kbn/security-solution-ess": "link:x-pack/plugins/security_solution_ess", "@kbn/security-solution-fixtures-plugin": "link:x-pack/test/cases_api_integration/common/plugins/security_solution", "@kbn/security-solution-plugin": "link:x-pack/plugins/security_solution", + "@kbn/security-solution-serverless": "link:x-pack/plugins/security_solution_serverless", "@kbn/security-solution-side-nav": "link:x-pack/packages/security-solution/side_nav", "@kbn/security-solution-storybook-config": "link:x-pack/packages/security-solution/storybook/config", "@kbn/security-test-endpoints-plugin": "link:x-pack/test/security_functional/plugins/test_endpoints", @@ -611,7 +612,6 @@ "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/serverless-project-switcher": "link:packages/serverless/project_switcher", "@kbn/serverless-search": "link:x-pack/plugins/serverless_search", - "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/serverless-types": "link:packages/serverless/types", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index c0a9f99b09d4..ae6543232fa8 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -40,7 +40,6 @@ pageLoadAssetSize: embeddable: 87309 embeddableEnhanced: 22107 enterpriseSearch: 50858 - essSecurity: 16573 esUiShared: 326654 eventAnnotation: 48565 exploratoryView: 74673 @@ -119,10 +118,11 @@ pageLoadAssetSize: searchprofiler: 67080 security: 65433 securitySolution: 66738 + securitySolutionEss: 16573 + securitySolutionServerless: 40000 serverless: 16573 serverlessObservability: 68747 serverlessSearch: 71995 - serverlessSecurity: 40000 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/tsconfig.base.json b/tsconfig.base.json index 1f70f8471564..8b4c52078c35 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -712,8 +712,6 @@ "@kbn/eslint-plugin-telemetry/*": ["packages/kbn-eslint-plugin-telemetry/*"], "@kbn/eso-plugin": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin"], "@kbn/eso-plugin/*": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin/*"], - "@kbn/ess-security": ["x-pack/plugins/ess_security"], - "@kbn/ess-security/*": ["x-pack/plugins/ess_security/*"], "@kbn/event-annotation-plugin": ["src/plugins/event_annotation"], "@kbn/event-annotation-plugin/*": ["src/plugins/event_annotation/*"], "@kbn/event-log-fixture-plugin": ["x-pack/test/plugin_api_integration/plugins/event_log"], @@ -1154,10 +1152,14 @@ "@kbn/security-api-integration-helpers/*": ["x-pack/test/security_api_integration/packages/helpers/*"], "@kbn/security-plugin": ["x-pack/plugins/security"], "@kbn/security-plugin/*": ["x-pack/plugins/security/*"], + "@kbn/security-solution-ess": ["x-pack/plugins/security_solution_ess"], + "@kbn/security-solution-ess/*": ["x-pack/plugins/security_solution_ess/*"], "@kbn/security-solution-fixtures-plugin": ["x-pack/test/cases_api_integration/common/plugins/security_solution"], "@kbn/security-solution-fixtures-plugin/*": ["x-pack/test/cases_api_integration/common/plugins/security_solution/*"], "@kbn/security-solution-plugin": ["x-pack/plugins/security_solution"], "@kbn/security-solution-plugin/*": ["x-pack/plugins/security_solution/*"], + "@kbn/security-solution-serverless": ["x-pack/plugins/security_solution_serverless"], + "@kbn/security-solution-serverless/*": ["x-pack/plugins/security_solution_serverless/*"], "@kbn/security-solution-side-nav": ["x-pack/packages/security-solution/side_nav"], "@kbn/security-solution-side-nav/*": ["x-pack/packages/security-solution/side_nav/*"], "@kbn/security-solution-storybook-config": ["x-pack/packages/security-solution/storybook/config"], @@ -1212,8 +1214,6 @@ "@kbn/serverless-project-switcher/*": ["packages/serverless/project_switcher/*"], "@kbn/serverless-search": ["x-pack/plugins/serverless_search"], "@kbn/serverless-search/*": ["x-pack/plugins/serverless_search/*"], - "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], - "@kbn/serverless-security/*": ["x-pack/plugins/serverless_security/*"], "@kbn/serverless-storybook-config": ["packages/serverless/storybook/config"], "@kbn/serverless-storybook-config/*": ["packages/serverless/storybook/config/*"], "@kbn/serverless-types": ["packages/serverless/types"], diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 2fa461531b49..6fbc0f935b94 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -74,9 +74,9 @@ "xpack.serverless": "plugins/serverless", "xpack.serverlessSearch": "plugins/serverless_search", "xpack.serverlessObservability": "plugins/serverless_observability", - "xpack.serverlessSecurity": "plugins/serverless_security", "xpack.securitySolution": "plugins/security_solution", - "xpack.securitySolutionEss": "plugins/ess_security", + "xpack.securitySolutionEss": "plugins/security_solution_ess", + "xpack.securitySolutionServerless": "plugins/security_solution_serverless", "xpack.sessionView": "plugins/session_view", "xpack.snapshotRestore": "plugins/snapshot_restore", "xpack.spaces": "plugins/spaces", diff --git a/x-pack/plugins/ess_security/public/__mocks__/services.tsx b/x-pack/plugins/ess_security/public/__mocks__/services.tsx deleted file mode 100644 index e24367947f89..000000000000 --- a/x-pack/plugins/ess_security/public/__mocks__/services.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'; - -export const KibanaServicesProvider = jest - .fn() - .mockImplementation(({ children }) =>
    {children}
    ); - -export const useKibana = jest.fn().mockReturnValue({ - services: { - http: { - basePath: { - prepend: jest.fn().mockImplementation((path: string) => path), - }, - }, - cloudExperiments: {}, - }, -}); diff --git a/x-pack/plugins/ess_security/public/get_started/index.tsx b/x-pack/plugins/ess_security/public/get_started/index.tsx deleted file mode 100644 index b06d1c0b2c84..000000000000 --- a/x-pack/plugins/ess_security/public/get_started/index.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 from 'react'; - -import { CoreStart } from '@kbn/core/public'; -import { KibanaServicesProvider } from '../services'; -import { EssSecurityPluginStartDependencies } from '../types'; -import { GetStarted } from './lazy'; - -export const getSecurityGetStartedComponent = - (core: CoreStart, pluginsStart: EssSecurityPluginStartDependencies): React.ComponentType => - () => - ( - - - - ); diff --git a/x-pack/plugins/ess_security/public/jest.config.js b/x-pack/plugins/ess_security/public/jest.config.js deleted file mode 100644 index ffee6062ec59..000000000000 --- a/x-pack/plugins/ess_security/public/jest.config.js +++ /dev/null @@ -1,25 +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. - */ -module.exports = { - preset: '@kbn/test', - rootDir: '../../../..', - /** all nested directories have their own Jest config file */ - testMatch: ['/x-pack/plugins/ess_security/public/**/*.test.{js,mjs,ts,tsx}'], - roots: ['/x-pack/plugins/ess_security/public'], - coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/ess_security/public', - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/x-pack/plugins/ess_security/public/**/*.{ts,tsx}', - '!/x-pack/plugins/ess_security/public/*.test.{ts,tsx}', - '!/x-pack/plugins/ess_security/public/{__test__,__snapshots__,__examples__,*mock*,tests,test_helpers,integration_tests,types}/**/*', - '!/x-pack/plugins/ess_security/public/*mock*.{ts,tsx}', - '!/x-pack/plugins/ess_security/public/*.test.{ts,tsx}', - '!/x-pack/plugins/ess_security/public/*.d.ts', - '!/x-pack/plugins/ess_security/public/*.config.ts', - '!/x-pack/plugins/ess_security/public/index.{js,ts,tsx}', - ], -}; diff --git a/x-pack/plugins/ess_security/public/plugin.ts b/x-pack/plugins/ess_security/public/plugin.ts deleted file mode 100644 index 52d75c01f811..000000000000 --- a/x-pack/plugins/ess_security/public/plugin.ts +++ /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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; -import { subscribeBreadcrumbs } from './breadcrumbs'; -import { getSecurityGetStartedComponent } from './get_started'; -import { - EssSecurityPluginSetup, - EssSecurityPluginStart, - EssSecurityPluginSetupDependencies, - EssSecurityPluginStartDependencies, -} from './types'; - -export class EssSecurityPlugin - implements - Plugin< - EssSecurityPluginSetup, - EssSecurityPluginStart, - EssSecurityPluginSetupDependencies, - EssSecurityPluginStartDependencies - > -{ - constructor() {} - - public setup( - _core: CoreSetup, - _setupDeps: EssSecurityPluginSetupDependencies - ): EssSecurityPluginSetup { - return {}; - } - - public start( - core: CoreStart, - startDeps: EssSecurityPluginStartDependencies - ): EssSecurityPluginStart { - const { securitySolution } = startDeps; - - subscribeBreadcrumbs(securitySolution, core); - securitySolution.setGetStartedPage(getSecurityGetStartedComponent(core, startDeps)); - - return {}; - } - - public stop() {} -} diff --git a/x-pack/plugins/security_solution/common/index.ts b/x-pack/plugins/security_solution/common/index.ts index ebe102e29ce2..24beb602305d 100644 --- a/x-pack/plugins/security_solution/common/index.ts +++ b/x-pack/plugins/security_solution/common/index.ts @@ -13,6 +13,8 @@ export { APP_ID, CASES_FEATURE_ID, SERVER_APP_ID, + APP_PATH, + MANAGE_PATH, ADD_DATA_PATH, SecurityPageName, } from './constants'; diff --git a/x-pack/plugins/ess_security/.gitignore b/x-pack/plugins/security_solution_ess/.gitignore similarity index 100% rename from x-pack/plugins/ess_security/.gitignore rename to x-pack/plugins/security_solution_ess/.gitignore diff --git a/x-pack/plugins/ess_security/README.md b/x-pack/plugins/security_solution_ess/README.md similarity index 51% rename from x-pack/plugins/ess_security/README.md rename to x-pack/plugins/security_solution_ess/README.md index c207b6d2671d..de7339892f75 100755 --- a/x-pack/plugins/ess_security/README.md +++ b/x-pack/plugins/security_solution_ess/README.md @@ -1,3 +1,3 @@ -# essSecurity +# securitySolutionEss -This plugin contains the ESS/on-prem deployments (non-serverless) customizations for Security Solution. \ No newline at end of file +This plugin contains the ESS/on-prem deployments (non-serverless) specific logic for Security Solution. \ No newline at end of file diff --git a/x-pack/plugins/ess_security/common/index.ts b/x-pack/plugins/security_solution_ess/common/index.ts similarity index 72% rename from x-pack/plugins/ess_security/common/index.ts rename to x-pack/plugins/security_solution_ess/common/index.ts index 0b3ef135ddc1..b3541827fd11 100644 --- a/x-pack/plugins/ess_security/common/index.ts +++ b/x-pack/plugins/security_solution_ess/common/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export const PLUGIN_ID = 'essSecurity'; -export const PLUGIN_NAME = 'essSecurity'; +export const PLUGIN_ID = 'securitySolutionEss'; +export const PLUGIN_NAME = 'securitySolutionEss'; diff --git a/x-pack/plugins/serverless_security/jest.config.dev.js b/x-pack/plugins/security_solution_ess/jest.config.dev.js similarity index 78% rename from x-pack/plugins/serverless_security/jest.config.dev.js rename to x-pack/plugins/security_solution_ess/jest.config.dev.js index f759355f0e0e..6ac2f96f95f0 100644 --- a/x-pack/plugins/serverless_security/jest.config.dev.js +++ b/x-pack/plugins/security_solution_ess/jest.config.dev.js @@ -8,5 +8,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../../../', - projects: ['/x-pack/plugins/serverless_security/public/*/jest.config.js'], + projects: ['/x-pack/plugins/security_solution_ess/public/jest.config.js'], }; diff --git a/x-pack/plugins/ess_security/kibana.jsonc b/x-pack/plugins/security_solution_ess/kibana.jsonc similarity index 73% rename from x-pack/plugins/ess_security/kibana.jsonc rename to x-pack/plugins/security_solution_ess/kibana.jsonc index eee71237a2a7..cf1edb3f571c 100644 --- a/x-pack/plugins/ess_security/kibana.jsonc +++ b/x-pack/plugins/security_solution_ess/kibana.jsonc @@ -1,13 +1,13 @@ { "type": "plugin", - "id": "@kbn/ess-security", + "id": "@kbn/security-solution-ess", "owner": "@elastic/security-solution", "description": "ESS customizations for Security Solution.", "plugin": { - "id": "essSecurity", + "id": "securitySolutionEss", "server": true, "browser": true, - "configPath": ["xpack", "ess", "security"], + "configPath": ["xpack", "securitySolutionEss"], "requiredPlugins": [ "securitySolution" ], diff --git a/x-pack/plugins/serverless_security/package.json b/x-pack/plugins/security_solution_ess/package.json similarity index 85% rename from x-pack/plugins/serverless_security/package.json rename to x-pack/plugins/security_solution_ess/package.json index 0154207c22d6..e7ab99edd6a2 100644 --- a/x-pack/plugins/serverless_security/package.json +++ b/x-pack/plugins/security_solution_ess/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/serverless-security", + "name": "@kbn/security-solution-ess", "version": "1.0.0", "license": "Elastic License 2.0", "private": true, @@ -8,4 +8,4 @@ "plugin-helpers": "node ../../../scripts/plugin_helpers", "kbn": "node ../../../scripts/kbn" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ess_security/public/breadcrumbs/breadcrumbs.test.ts b/x-pack/plugins/security_solution_ess/public/breadcrumbs/breadcrumbs.test.ts similarity index 100% rename from x-pack/plugins/ess_security/public/breadcrumbs/breadcrumbs.test.ts rename to x-pack/plugins/security_solution_ess/public/breadcrumbs/breadcrumbs.test.ts diff --git a/x-pack/plugins/ess_security/public/breadcrumbs/breadcrumbs.ts b/x-pack/plugins/security_solution_ess/public/breadcrumbs/breadcrumbs.ts similarity index 67% rename from x-pack/plugins/ess_security/public/breadcrumbs/breadcrumbs.ts rename to x-pack/plugins/security_solution_ess/public/breadcrumbs/breadcrumbs.ts index 8fa8107226f4..ca76dc304ab9 100644 --- a/x-pack/plugins/ess_security/public/breadcrumbs/breadcrumbs.ts +++ b/x-pack/plugins/security_solution_ess/public/breadcrumbs/breadcrumbs.ts @@ -5,17 +5,15 @@ * 2.0. */ -import type { PluginStart as SecuritySolutionPluginStart } from '@kbn/security-solution-plugin/public'; -import { ChromeBreadcrumb, CoreStart } from '@kbn/core/public'; +import type { ChromeBreadcrumb } from '@kbn/core/public'; +import type { Services } from '../common/services'; -export const subscribeBreadcrumbs = ( - securitySolution: SecuritySolutionPluginStart, - core: CoreStart -) => { +export const subscribeBreadcrumbs = (services: Services) => { + const { chrome, securitySolution } = services; securitySolution.getBreadcrumbsNav$().subscribe((breadcrumbsNav) => { const breadcrumbs = [...breadcrumbsNav.leading, ...breadcrumbsNav.trailing]; if (breadcrumbs.length > 0) { - core.chrome.setBreadcrumbs(emptyLastBreadcrumbUrl(breadcrumbs)); + chrome.setBreadcrumbs(emptyLastBreadcrumbUrl(breadcrumbs)); } }); }; diff --git a/x-pack/plugins/ess_security/public/breadcrumbs/index.ts b/x-pack/plugins/security_solution_ess/public/breadcrumbs/index.ts similarity index 100% rename from x-pack/plugins/ess_security/public/breadcrumbs/index.ts rename to x-pack/plugins/security_solution_ess/public/breadcrumbs/index.ts diff --git a/x-pack/plugins/security_solution_ess/public/common/__mocks__/services.mock.ts b/x-pack/plugins/security_solution_ess/public/common/__mocks__/services.mock.ts new file mode 100644 index 000000000000..9e9909b45894 --- /dev/null +++ b/x-pack/plugins/security_solution_ess/public/common/__mocks__/services.mock.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { coreMock } from '@kbn/core/public/mocks'; +import { securitySolutionMock } from '@kbn/security-solution-plugin/public/mocks'; +import type { Services } from '../services'; + +export const mockServices: Services = { + ...coreMock.createStart(), + securitySolution: securitySolutionMock.createStart(), +}; diff --git a/x-pack/plugins/security_solution_ess/public/common/__mocks__/services.tsx b/x-pack/plugins/security_solution_ess/public/common/__mocks__/services.tsx new file mode 100644 index 000000000000..710a9b378ada --- /dev/null +++ b/x-pack/plugins/security_solution_ess/public/common/__mocks__/services.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockServices } from './services.mock'; + +export const useKibana = jest.fn(() => ({ services: mockServices })); diff --git a/x-pack/plugins/ess_security/public/common/hooks/__mocks__/use_variation.ts b/x-pack/plugins/security_solution_ess/public/common/hooks/__mocks__/use_variation.ts similarity index 100% rename from x-pack/plugins/ess_security/public/common/hooks/__mocks__/use_variation.ts rename to x-pack/plugins/security_solution_ess/public/common/hooks/__mocks__/use_variation.ts diff --git a/x-pack/plugins/ess_security/public/common/hooks/use_variation.test.ts b/x-pack/plugins/security_solution_ess/public/common/hooks/use_variation.test.ts similarity index 100% rename from x-pack/plugins/ess_security/public/common/hooks/use_variation.test.ts rename to x-pack/plugins/security_solution_ess/public/common/hooks/use_variation.test.ts diff --git a/x-pack/plugins/ess_security/public/common/hooks/use_variation.ts b/x-pack/plugins/security_solution_ess/public/common/hooks/use_variation.ts similarity index 100% rename from x-pack/plugins/ess_security/public/common/hooks/use_variation.ts rename to x-pack/plugins/security_solution_ess/public/common/hooks/use_variation.ts diff --git a/x-pack/plugins/ess_security/public/services.tsx b/x-pack/plugins/security_solution_ess/public/common/services.tsx similarity index 59% rename from x-pack/plugins/ess_security/public/services.tsx rename to x-pack/plugins/security_solution_ess/public/common/services.tsx index ef8c0db8e792..d00fd10350a7 100644 --- a/x-pack/plugins/ess_security/public/services.tsx +++ b/x-pack/plugins/security_solution_ess/public/common/services.tsx @@ -5,22 +5,27 @@ * 2.0. */ -import { CoreStart } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import React from 'react'; import { KibanaContextProvider, useKibana as useKibanaReact, } from '@kbn/kibana-react-plugin/public'; -import type { EssSecurityPluginStartDependencies } from './types'; +import type { SecuritySolutionEssPluginStartDeps } from '../types'; -export type Services = CoreStart & EssSecurityPluginStartDependencies; +export type Services = CoreStart & SecuritySolutionEssPluginStartDeps; export const KibanaServicesProvider: React.FC<{ - core: CoreStart; - pluginsStart: EssSecurityPluginStartDependencies; -}> = ({ core, pluginsStart, children }) => { - const services: Services = { ...core, ...pluginsStart }; + services: Services; +}> = ({ services, children }) => { return {children}; }; export const useKibana = () => useKibanaReact(); + +export const createServices = ( + core: CoreStart, + pluginsStart: SecuritySolutionEssPluginStartDeps +): Services => { + return { ...core, ...pluginsStart }; +}; diff --git a/x-pack/plugins/ess_security/public/get_started/images/cloud1.svg b/x-pack/plugins/security_solution_ess/public/get_started/images/cloud1.svg similarity index 100% rename from x-pack/plugins/ess_security/public/get_started/images/cloud1.svg rename to x-pack/plugins/security_solution_ess/public/get_started/images/cloud1.svg diff --git a/x-pack/plugins/ess_security/public/get_started/images/endpoint1.svg b/x-pack/plugins/security_solution_ess/public/get_started/images/endpoint1.svg similarity index 100% rename from x-pack/plugins/ess_security/public/get_started/images/endpoint1.svg rename to x-pack/plugins/security_solution_ess/public/get_started/images/endpoint1.svg diff --git a/x-pack/plugins/ess_security/public/get_started/images/siem1.svg b/x-pack/plugins/security_solution_ess/public/get_started/images/siem1.svg similarity index 100% rename from x-pack/plugins/ess_security/public/get_started/images/siem1.svg rename to x-pack/plugins/security_solution_ess/public/get_started/images/siem1.svg diff --git a/x-pack/plugins/security_solution_ess/public/get_started/index.tsx b/x-pack/plugins/security_solution_ess/public/get_started/index.tsx new file mode 100644 index 000000000000..f85d20afe0c0 --- /dev/null +++ b/x-pack/plugins/security_solution_ess/public/get_started/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import { KibanaServicesProvider, type Services } from '../common/services'; +import { GetStarted } from './lazy'; + +export const getSecurityGetStartedComponent = (services: Services): React.ComponentType => + function GetStartedComponent() { + return ( + + + + ); + }; diff --git a/x-pack/plugins/ess_security/public/get_started/landing_cards.test.tsx b/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.test.tsx similarity index 98% rename from x-pack/plugins/ess_security/public/get_started/landing_cards.test.tsx rename to x-pack/plugins/security_solution_ess/public/get_started/landing_cards.test.tsx index 05ef9d5cf23f..153d279a6a06 100644 --- a/x-pack/plugins/ess_security/public/get_started/landing_cards.test.tsx +++ b/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.test.tsx @@ -12,7 +12,7 @@ import { ADD_DATA_PATH } from '@kbn/security-solution-plugin/common'; import { useVariation } from '../common/hooks/use_variation'; jest.mock('../common/hooks/use_variation'); -jest.mock('../services'); +jest.mock('../common/services'); describe('LandingCards component', () => { beforeEach(() => { diff --git a/x-pack/plugins/ess_security/public/get_started/landing_cards.tsx b/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.tsx similarity index 98% rename from x-pack/plugins/ess_security/public/get_started/landing_cards.tsx rename to x-pack/plugins/security_solution_ess/public/get_started/landing_cards.tsx index eacf3c380a29..91e3c2c5f1fc 100644 --- a/x-pack/plugins/ess_security/public/get_started/landing_cards.tsx +++ b/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.tsx @@ -12,8 +12,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiPageHeader, - EuiThemeComputed, useEuiTheme, + type EuiThemeComputed, } from '@elastic/eui'; import { css } from '@emotion/react'; import { ADD_DATA_PATH } from '@kbn/security-solution-plugin/common'; @@ -22,7 +22,7 @@ import * as i18n from './translations'; import endpointSvg from './images/endpoint1.svg'; import cloudSvg from './images/cloud1.svg'; import siemSvg from './images/siem1.svg'; -import { useKibana } from '../services'; +import { useKibana } from '../common/services'; const imgUrls = { cloud: cloudSvg, diff --git a/x-pack/plugins/ess_security/public/get_started/lazy.tsx b/x-pack/plugins/security_solution_ess/public/get_started/lazy.tsx similarity index 100% rename from x-pack/plugins/ess_security/public/get_started/lazy.tsx rename to x-pack/plugins/security_solution_ess/public/get_started/lazy.tsx diff --git a/x-pack/plugins/ess_security/public/get_started/translations.tsx b/x-pack/plugins/security_solution_ess/public/get_started/translations.tsx similarity index 100% rename from x-pack/plugins/ess_security/public/get_started/translations.tsx rename to x-pack/plugins/security_solution_ess/public/get_started/translations.tsx diff --git a/x-pack/plugins/ess_security/public/index.ts b/x-pack/plugins/security_solution_ess/public/index.ts similarity index 62% rename from x-pack/plugins/ess_security/public/index.ts rename to x-pack/plugins/security_solution_ess/public/index.ts index 730b776d97b2..3a26496d3692 100644 --- a/x-pack/plugins/ess_security/public/index.ts +++ b/x-pack/plugins/security_solution_ess/public/index.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { PluginInitializerContext } from '@kbn/core/public'; -import { EssSecurityPlugin } from './plugin'; +import type { PluginInitializerContext } from '@kbn/core/public'; +import { SecuritySolutionEssPlugin } from './plugin'; // This exports static code and TypeScript types, // as well as, Kibana Platform `plugin()` initializer. export function plugin(_initializerContext: PluginInitializerContext) { - return new EssSecurityPlugin(); + return new SecuritySolutionEssPlugin(); } -export type { EssSecurityPluginSetup, EssSecurityPluginStart } from './types'; +export type { SecuritySolutionEssPluginSetup, SecuritySolutionEssPluginStart } from './types'; diff --git a/x-pack/plugins/security_solution_ess/public/jest.config.js b/x-pack/plugins/security_solution_ess/public/jest.config.js new file mode 100644 index 000000000000..fac3d4ceba42 --- /dev/null +++ b/x-pack/plugins/security_solution_ess/public/jest.config.js @@ -0,0 +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. + */ +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + /** all nested directories have their own Jest config file */ + testMatch: ['/x-pack/plugins/security_solution_ess/public/**/*.test.{js,mjs,ts,tsx}'], + roots: ['/x-pack/plugins/security_solution_ess/public'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution_ess/public', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution_ess/public/**/*.{ts,tsx}', + '!/x-pack/plugins/security_solution_ess/public/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution_ess/public/{__test__,__snapshots__,__examples__,*mock*,tests,test_helpers,integration_tests,types}/**/*', + '!/x-pack/plugins/security_solution_ess/public/*mock*.{ts,tsx}', + '!/x-pack/plugins/security_solution_ess/public/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution_ess/public/*.d.ts', + '!/x-pack/plugins/security_solution_ess/public/*.config.ts', + '!/x-pack/plugins/security_solution_ess/public/index.{js,ts,tsx}', + ], +}; diff --git a/x-pack/plugins/security_solution_ess/public/plugin.ts b/x-pack/plugins/security_solution_ess/public/plugin.ts new file mode 100644 index 000000000000..7224a46f682e --- /dev/null +++ b/x-pack/plugins/security_solution_ess/public/plugin.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { subscribeBreadcrumbs } from './breadcrumbs'; +import { createServices } from './common/services'; +import { getSecurityGetStartedComponent } from './get_started'; +import type { + SecuritySolutionEssPluginSetup, + SecuritySolutionEssPluginStart, + SecuritySolutionEssPluginSetupDeps, + SecuritySolutionEssPluginStartDeps, +} from './types'; + +export class SecuritySolutionEssPlugin + implements + Plugin< + SecuritySolutionEssPluginSetup, + SecuritySolutionEssPluginStart, + SecuritySolutionEssPluginSetupDeps, + SecuritySolutionEssPluginStartDeps + > +{ + public setup( + _core: CoreSetup, + _setupDeps: SecuritySolutionEssPluginSetupDeps + ): SecuritySolutionEssPluginSetup { + return {}; + } + + public start( + core: CoreStart, + startDeps: SecuritySolutionEssPluginStartDeps + ): SecuritySolutionEssPluginStart { + const { securitySolution } = startDeps; + const services = createServices(core, startDeps); + + securitySolution.setGetStartedPage(getSecurityGetStartedComponent(services)); + subscribeBreadcrumbs(services); + + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/ess_security/public/types.ts b/x-pack/plugins/security_solution_ess/public/types.ts similarity index 69% rename from x-pack/plugins/ess_security/public/types.ts rename to x-pack/plugins/security_solution_ess/public/types.ts index 3ba22c5f0ff3..2bdeeffebf72 100644 --- a/x-pack/plugins/ess_security/public/types.ts +++ b/x-pack/plugins/security_solution_ess/public/types.ts @@ -9,19 +9,19 @@ import type { PluginSetup as SecuritySolutionPluginSetup, PluginStart as SecuritySolutionPluginStart, } from '@kbn/security-solution-plugin/public'; -import { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/common'; +import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/common'; // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface EssSecurityPluginSetup {} +export interface SecuritySolutionEssPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface EssSecurityPluginStart {} +export interface SecuritySolutionEssPluginStart {} -export interface EssSecurityPluginSetupDependencies { +export interface SecuritySolutionEssPluginSetupDeps { securitySolution: SecuritySolutionPluginSetup; } -export interface EssSecurityPluginStartDependencies { +export interface SecuritySolutionEssPluginStartDeps { securitySolution: SecuritySolutionPluginStart; cloudExperiments?: CloudExperimentsPluginStart; } diff --git a/x-pack/plugins/ess_security/server/config.ts b/x-pack/plugins/security_solution_ess/server/config.ts similarity index 80% rename from x-pack/plugins/ess_security/server/config.ts rename to x-pack/plugins/security_solution_ess/server/config.ts index 1d03beec8e26..48e96989859f 100644 --- a/x-pack/plugins/ess_security/server/config.ts +++ b/x-pack/plugins/security_solution_ess/server/config.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { PluginConfigDescriptor } from '@kbn/core/server'; +import { schema, type TypeOf } from '@kbn/config-schema'; +import type { PluginConfigDescriptor } from '@kbn/core/server'; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), diff --git a/x-pack/plugins/ess_security/server/constants.ts b/x-pack/plugins/security_solution_ess/server/constants.ts similarity index 100% rename from x-pack/plugins/ess_security/server/constants.ts rename to x-pack/plugins/security_solution_ess/server/constants.ts diff --git a/x-pack/plugins/ess_security/server/index.ts b/x-pack/plugins/security_solution_ess/server/index.ts similarity index 64% rename from x-pack/plugins/ess_security/server/index.ts rename to x-pack/plugins/security_solution_ess/server/index.ts index a6dbcaa2893b..ebb28fda2cf0 100644 --- a/x-pack/plugins/ess_security/server/index.ts +++ b/x-pack/plugins/security_solution_ess/server/index.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { PluginInitializerContext } from '@kbn/core/server'; +import type { PluginInitializerContext } from '@kbn/core/server'; -import { EssSecurityPlugin } from './plugin'; +import { SecuritySolutionEssPlugin } from './plugin'; export { config } from './config'; // This exports static code and TypeScript types, // as well as, Kibana Platform `plugin()` initializer. export function plugin(_initializerContext: PluginInitializerContext) { - return new EssSecurityPlugin(); + return new SecuritySolutionEssPlugin(); } -export type { EssSecurityPluginSetup, EssSecurityPluginStart } from './types'; +export type { SecuritySolutionEssPluginSetup, SecuritySolutionEssPluginStart } from './types'; diff --git a/x-pack/plugins/ess_security/server/plugin.ts b/x-pack/plugins/security_solution_ess/server/plugin.ts similarity index 50% rename from x-pack/plugins/ess_security/server/plugin.ts rename to x-pack/plugins/security_solution_ess/server/plugin.ts index d845216ad722..0083fe1314cf 100644 --- a/x-pack/plugins/ess_security/server/plugin.ts +++ b/x-pack/plugins/security_solution_ess/server/plugin.ts @@ -5,28 +5,26 @@ * 2.0. */ -import { Plugin, CoreSetup } from '@kbn/core/server'; +import type { Plugin, CoreSetup } from '@kbn/core/server'; import { DEFAULT_APP_FEATURES } from './constants'; -import { - EssSecurityPluginSetup, - EssSecurityPluginStart, - EssSecurityPluginSetupDependencies, - EssSecurityPluginStartDependencies, +import type { + SecuritySolutionEssPluginSetup, + SecuritySolutionEssPluginStart, + SecuritySolutionEssPluginSetupDeps, + SecuritySolutionEssPluginStartDeps, } from './types'; -export class EssSecurityPlugin +export class SecuritySolutionEssPlugin implements Plugin< - EssSecurityPluginSetup, - EssSecurityPluginStart, - EssSecurityPluginSetupDependencies, - EssSecurityPluginStartDependencies + SecuritySolutionEssPluginSetup, + SecuritySolutionEssPluginStart, + SecuritySolutionEssPluginSetupDeps, + SecuritySolutionEssPluginStartDeps > { - constructor() {} - - public setup(_coreSetup: CoreSetup, pluginsSetup: EssSecurityPluginSetupDependencies) { + public setup(_coreSetup: CoreSetup, pluginsSetup: SecuritySolutionEssPluginSetupDeps) { pluginsSetup.securitySolution.setAppFeatures(DEFAULT_APP_FEATURES); return {}; } diff --git a/x-pack/plugins/ess_security/server/types.ts b/x-pack/plugins/security_solution_ess/server/types.ts similarity index 73% rename from x-pack/plugins/ess_security/server/types.ts rename to x-pack/plugins/security_solution_ess/server/types.ts index d90729e77be4..37dd38d76ed7 100644 --- a/x-pack/plugins/ess_security/server/types.ts +++ b/x-pack/plugins/security_solution_ess/server/types.ts @@ -5,20 +5,20 @@ * 2.0. */ -import { +import type { PluginSetup as SecuritySolutionPluginSetup, PluginStart as SecuritySolutionPluginStart, } from '@kbn/security-solution-plugin/server'; // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface EssSecurityPluginSetup {} +export interface SecuritySolutionEssPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface EssSecurityPluginStart {} +export interface SecuritySolutionEssPluginStart {} -export interface EssSecurityPluginSetupDependencies { +export interface SecuritySolutionEssPluginSetupDeps { securitySolution: SecuritySolutionPluginSetup; } -export interface EssSecurityPluginStartDependencies { +export interface SecuritySolutionEssPluginStartDeps { securitySolution: SecuritySolutionPluginStart; } diff --git a/x-pack/plugins/ess_security/tsconfig.json b/x-pack/plugins/security_solution_ess/tsconfig.json similarity index 92% rename from x-pack/plugins/ess_security/tsconfig.json rename to x-pack/plugins/security_solution_ess/tsconfig.json index f28c81c6c59e..c37fc7779025 100644 --- a/x-pack/plugins/ess_security/tsconfig.json +++ b/x-pack/plugins/security_solution_ess/tsconfig.json @@ -11,9 +11,7 @@ "server/**/*.ts", "../../../typings/**/*" ], - "exclude": [ - "target/**/*" - ], + "exclude": ["target/**/*"], "kbn_references": [ "@kbn/core", "@kbn/config-schema", diff --git a/x-pack/plugins/serverless_security/.gitignore b/x-pack/plugins/security_solution_serverless/.gitignore similarity index 100% rename from x-pack/plugins/serverless_security/.gitignore rename to x-pack/plugins/security_solution_serverless/.gitignore diff --git a/x-pack/plugins/security_solution_serverless/README.mdx b/x-pack/plugins/security_solution_serverless/README.mdx new file mode 100755 index 000000000000..a9e20bf8147a --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/README.mdx @@ -0,0 +1,4 @@ +# Serverless Security project plugin + +This plugin contains configuration and code used to create a Serverless Security project. +It leverages universal configuration and other APIs in the [`serverless`](../serverless/README.mdx) plugin to configure Kibana. \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/common/config.ts b/x-pack/plugins/security_solution_serverless/common/config.ts similarity index 77% rename from x-pack/plugins/serverless_security/common/config.ts rename to x-pack/plugins/security_solution_serverless/common/config.ts index 63b173ff3930..cc661da4af1a 100644 --- a/x-pack/plugins/serverless_security/common/config.ts +++ b/x-pack/plugins/security_solution_serverless/common/config.ts @@ -5,13 +5,8 @@ * 2.0. */ -import { schema, TypeOf } from '@kbn/config-schema'; - -export enum ProductLine { - security = 'security', - cloud = 'cloud', - endpoint = 'endpoint', -} +import { schema, type TypeOf } from '@kbn/config-schema'; +import { ProductLine, ProductTier } from './product'; export const productLine = schema.oneOf([ schema.literal(ProductLine.security), @@ -21,7 +16,10 @@ export const productLine = schema.oneOf([ export type SecurityProductLine = TypeOf; -export const productTier = schema.oneOf([schema.literal('essentials'), schema.literal('complete')]); +export const productTier = schema.oneOf([ + schema.literal(ProductTier.essentials), + schema.literal(ProductTier.complete), +]); export type SecurityProductTier = TypeOf; export const productType = schema.object({ diff --git a/x-pack/plugins/serverless_security/common/index.ts b/x-pack/plugins/security_solution_serverless/common/index.ts similarity index 69% rename from x-pack/plugins/serverless_security/common/index.ts rename to x-pack/plugins/security_solution_serverless/common/index.ts index 0dc5be6ddf9b..e6a263165b00 100644 --- a/x-pack/plugins/serverless_security/common/index.ts +++ b/x-pack/plugins/security_solution_serverless/common/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export const PLUGIN_ID = 'serverlessSecurity'; -export const PLUGIN_NAME = 'serverlessSecurity'; +export const PLUGIN_ID = 'securitySolutionServerless'; +export const PLUGIN_NAME = 'securitySolutionServerless'; diff --git a/x-pack/plugins/security_solution_serverless/common/jest.config.js b/x-pack/plugins/security_solution_serverless/common/jest.config.js new file mode 100644 index 000000000000..01ea1139d6e4 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/common/jest.config.js @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/plugins/security_solution_serverless/common'], + testMatch: [ + '/x-pack/plugins/security_solution_serverless/common/**/*.test.{js,mjs,ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution_serverless/common', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution_serverless/common/**/*.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/common/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/common/{__test__,__snapshots__,__examples__,*mock*,tests,test_helpers,integration_tests,types}/**/*', + '!/x-pack/plugins/security_solution_serverless/common/*mock*.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/common/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/common/*.d.ts', + '!/x-pack/plugins/security_solution_serverless/common/*.config.ts', + '!/x-pack/plugins/security_solution_serverless/common/index.{js,ts,tsx}', + ], +}; diff --git a/x-pack/plugins/serverless_security/common/pli/pli_config.ts b/x-pack/plugins/security_solution_serverless/common/pli/pli_config.ts similarity index 100% rename from x-pack/plugins/serverless_security/common/pli/pli_config.ts rename to x-pack/plugins/security_solution_serverless/common/pli/pli_config.ts diff --git a/x-pack/plugins/serverless_security/common/pli/pli_features.test.ts b/x-pack/plugins/security_solution_serverless/common/pli/pli_features.test.ts similarity index 80% rename from x-pack/plugins/serverless_security/common/pli/pli_features.test.ts rename to x-pack/plugins/security_solution_serverless/common/pli/pli_features.test.ts index c00f8d7fdd9e..3358ee69a425 100644 --- a/x-pack/plugins/serverless_security/common/pli/pli_features.test.ts +++ b/x-pack/plugins/security_solution_serverless/common/pli/pli_features.test.ts @@ -6,7 +6,7 @@ */ import { getProductAppFeatures } from './pli_features'; import * as pliConfig from './pli_config'; -import { ProductLine } from '../config'; +import { ProductLine, ProductTier } from '../product'; describe('getProductAppFeatures', () => { it('should return the essentials PLIs features', () => { @@ -19,7 +19,7 @@ describe('getProductAppFeatures', () => { }; const appFeatureKeys = getProductAppFeatures([ - { product_line: ProductLine.security, product_tier: 'essentials' }, + { product_line: ProductLine.security, product_tier: ProductTier.essentials }, ]); expect(appFeatureKeys).toEqual(['foo']); @@ -35,7 +35,7 @@ describe('getProductAppFeatures', () => { }; const appFeatureKeys = getProductAppFeatures([ - { product_line: ProductLine.security, product_tier: 'complete' }, + { product_line: ProductLine.security, product_tier: ProductTier.complete }, ]); expect(appFeatureKeys).toEqual(['foo', 'baz']); @@ -59,9 +59,9 @@ describe('getProductAppFeatures', () => { }; const appFeatureKeys = getProductAppFeatures([ - { product_line: ProductLine.security, product_tier: 'essentials' }, - { product_line: ProductLine.endpoint, product_tier: 'complete' }, - { product_line: ProductLine.cloud, product_tier: 'essentials' }, + { product_line: ProductLine.security, product_tier: ProductTier.essentials }, + { product_line: ProductLine.endpoint, product_tier: ProductTier.complete }, + { product_line: ProductLine.cloud, product_tier: ProductTier.essentials }, ]); expect(appFeatureKeys).toEqual(['foo', 'bar', 'repeated', 'qux', 'quux', 'corge', 'garply']); diff --git a/x-pack/plugins/serverless_security/common/pli/pli_features.ts b/x-pack/plugins/security_solution_serverless/common/pli/pli_features.ts similarity index 100% rename from x-pack/plugins/serverless_security/common/pli/pli_features.ts rename to x-pack/plugins/security_solution_serverless/common/pli/pli_features.ts diff --git a/x-pack/plugins/security_solution_serverless/common/product.ts b/x-pack/plugins/security_solution_serverless/common/product.ts new file mode 100644 index 000000000000..d5095eea3163 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/common/product.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export enum ProductLine { + security = 'security', + endpoint = 'endpoint', + cloud = 'cloud', +} + +export enum ProductTier { + essentials = 'essentials', + complete = 'complete', +} diff --git a/x-pack/plugins/security_solution_serverless/jest.config.dev.js b/x-pack/plugins/security_solution_serverless/jest.config.dev.js new file mode 100644 index 000000000000..2591fcc775ab --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/jest.config.dev.js @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../', + projects: ['/x-pack/plugins/security_solution_serverless/public/jest.config.js'], +}; diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/security_solution_serverless/kibana.jsonc similarity index 74% rename from x-pack/plugins/serverless_security/kibana.jsonc rename to x-pack/plugins/security_solution_serverless/kibana.jsonc index 306918ed7069..c9895ac040f3 100644 --- a/x-pack/plugins/serverless_security/kibana.jsonc +++ b/x-pack/plugins/security_solution_serverless/kibana.jsonc @@ -1,16 +1,15 @@ { "type": "plugin", - "id": "@kbn/serverless-security", + "id": "@kbn/security-solution-serverless", "owner": "@elastic/security-solution", "description": "Serverless customizations for security.", "plugin": { - "id": "serverlessSecurity", + "id": "securitySolutionServerless", "server": true, "browser": true, "configPath": [ "xpack", - "serverless", - "security" + "securitySolutionServerless", ], "requiredPlugins": [ "kibanaReact", @@ -21,7 +20,7 @@ "serverless" ], "optionalPlugins": [ - "essSecurity" + "securitySolutionEss" ], "requiredBundles": ["kibanaUtils"] } diff --git a/x-pack/plugins/ess_security/package.json b/x-pack/plugins/security_solution_serverless/package.json similarity index 83% rename from x-pack/plugins/ess_security/package.json rename to x-pack/plugins/security_solution_serverless/package.json index 1ea768291aff..745afbe7d97c 100644 --- a/x-pack/plugins/ess_security/package.json +++ b/x-pack/plugins/security_solution_serverless/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/ess-security", + "name": "@kbn/security-solution-serverless", "version": "1.0.0", "license": "Elastic License 2.0", "private": true, diff --git a/x-pack/plugins/serverless_security/public/common/services.mock.tsx b/x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.mock.tsx similarity index 75% rename from x-pack/plugins/serverless_security/public/common/services.mock.tsx rename to x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.mock.tsx index a3aba806af18..e4b1c777191e 100644 --- a/x-pack/plugins/serverless_security/public/common/services.mock.tsx +++ b/x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.mock.tsx @@ -13,12 +13,12 @@ import { securityMock } from '@kbn/security-plugin/public/mocks'; import { securitySolutionMock } from '@kbn/security-solution-plugin/public/mocks'; import { BehaviorSubject } from 'rxjs'; import { managementPluginMock } from '@kbn/management-plugin/public/mocks'; -import type { ProjectNavigationLink } from './navigation/links'; -import type { Services } from './services'; +import type { ProjectNavigationLink } from '../../navigation/links'; +import type { Services } from '../services'; export const mockProjectNavLinks = jest.fn((): ProjectNavigationLink[] => []); -export const servicesMocks: Services = { +export const mockServices: Services = { ...coreMock.createStart(), serverless: serverlessMock.createStart(), security: securityMock.createStart(), @@ -27,8 +27,10 @@ export const servicesMocks: Services = { management: managementPluginMock.createStartContract(), }; -export const KibanaServicesProvider = React.memo(({ children }) => ( - - {children} - -)); +export const ServicesWrapper = React.memo(function ServicesWrapper({ children }) { + return ( + + {children} + + ); +}); diff --git a/x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.tsx b/x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.tsx new file mode 100644 index 000000000000..5c6d2f1117c6 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/common/__mocks__/services.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { I18nProvider } from '@kbn/i18n-react'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { mockServices } from './services.mock'; + +export const KibanaServicesProvider: React.FC = ({ children }) => ( + + {children} + +); + +export const useKibana = jest.fn(() => ({ services: mockServices })); diff --git a/x-pack/plugins/serverless_security/public/hooks/__mocks__/use_link_props.ts b/x-pack/plugins/security_solution_serverless/public/common/hooks/__mocks__/use_link_props.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/hooks/__mocks__/use_link_props.ts rename to x-pack/plugins/security_solution_serverless/public/common/hooks/__mocks__/use_link_props.ts diff --git a/x-pack/plugins/serverless_security/public/hooks/use_link_props.test.tsx b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_link_props.test.tsx similarity index 82% rename from x-pack/plugins/serverless_security/public/hooks/use_link_props.test.tsx rename to x-pack/plugins/security_solution_serverless/public/common/hooks/use_link_props.test.tsx index 2a20ca97ef85..5b4d215e331e 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_link_props.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_link_props.test.tsx @@ -5,15 +5,18 @@ * 2.0. */ -import { MouseEvent } from 'react'; +import type { MouseEvent } from 'react'; import { renderHook } from '@testing-library/react-hooks'; import { APP_UI_ID, SecurityPageName } from '@kbn/security-solution-plugin/common'; -import { KibanaServicesProvider, servicesMocks } from '../common/services.mock'; +import { mockServices } from '../__mocks__/services.mock'; import { useGetLinkProps, useLinkProps } from './use_link_props'; -const { getUrlForApp, navigateToUrl: mockNavigateToUrl } = servicesMocks.application; +jest.mock('../services'); const href = '/app/security/test'; + +const { getUrlForApp, navigateToUrl } = mockServices.application; +const mockNavigateToUrl = navigateToUrl as jest.MockedFunction; const mockGetUrlForApp = getUrlForApp as jest.MockedFunction; mockGetUrlForApp.mockReturnValue(href); @@ -23,7 +26,7 @@ describe('useLinkProps', () => { }); it('should return link props', async () => { - const { result } = renderHook(useLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useLinkProps); const linkProps = result.current; @@ -37,7 +40,7 @@ describe('useLinkProps', () => { it('should call navigate when clicked normally', async () => { const ev = { preventDefault: jest.fn() } as unknown as MouseEvent; - const { result } = renderHook(useLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useLinkProps); const { onClick } = result.current; onClick(ev); @@ -48,7 +51,7 @@ describe('useLinkProps', () => { it('should not call navigate when clicked with modifiers', async () => { const ev = { preventDefault: jest.fn(), ctrlKey: true } as unknown as MouseEvent; - const { result } = renderHook(useLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useLinkProps); const { onClick } = result.current; onClick(ev); @@ -58,7 +61,6 @@ describe('useLinkProps', () => { it('should return link props passing deepLink', async () => { const { result } = renderHook(useLinkProps, { - wrapper: KibanaServicesProvider, initialProps: { deepLinkId: SecurityPageName.alerts }, }); @@ -74,7 +76,6 @@ describe('useLinkProps', () => { it('should return link props passing deepLink and path', async () => { const { result } = renderHook(useLinkProps, { - wrapper: KibanaServicesProvider, initialProps: { deepLinkId: SecurityPageName.alerts, path: '/test' }, }); @@ -95,7 +96,7 @@ describe('useGetLinkProps', () => { }); it('should return link props', async () => { - const { result } = renderHook(useGetLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useGetLinkProps); const linkProps = result.current({}); @@ -109,7 +110,7 @@ describe('useGetLinkProps', () => { it('should call navigate when clicked normally', async () => { const ev = { preventDefault: jest.fn() } as unknown as MouseEvent; - const { result } = renderHook(useGetLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useGetLinkProps); const { onClick } = result.current({}); onClick(ev); @@ -120,7 +121,7 @@ describe('useGetLinkProps', () => { it('should not call navigate when clicked with modifiers', async () => { const ev = { preventDefault: jest.fn(), ctrlKey: true } as unknown as MouseEvent; - const { result } = renderHook(useGetLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useGetLinkProps); const { onClick } = result.current({}); onClick(ev); @@ -129,7 +130,7 @@ describe('useGetLinkProps', () => { }); it('should return link props passing deepLink', async () => { - const { result } = renderHook(useGetLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useGetLinkProps); const linkProps = result.current({ deepLinkId: SecurityPageName.alerts }); @@ -142,7 +143,7 @@ describe('useGetLinkProps', () => { }); it('should return link props passing deepLink and path', async () => { - const { result } = renderHook(useGetLinkProps, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useGetLinkProps); const linkProps = result.current({ deepLinkId: SecurityPageName.alerts, path: '/test' }); diff --git a/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_link_props.ts similarity index 96% rename from x-pack/plugins/serverless_security/public/hooks/use_link_props.ts rename to x-pack/plugins/security_solution_serverless/public/common/hooks/use_link_props.ts index 6644afb7fdf0..3a1989dbdc79 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts +++ b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_link_props.ts @@ -7,7 +7,7 @@ import { APP_UI_ID, type SecurityPageName } from '@kbn/security-solution-plugin/common'; import { useMemo, useCallback, type MouseEventHandler, type MouseEvent } from 'react'; -import { useKibana, type Services } from '../common/services'; +import { useKibana, type Services } from '../services'; interface LinkProps { onClick: MouseEventHandler; diff --git a/x-pack/plugins/serverless_security/public/hooks/use_nav_links.ts b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_nav_links.ts similarity index 92% rename from x-pack/plugins/serverless_security/public/hooks/use_nav_links.ts rename to x-pack/plugins/security_solution_serverless/public/common/hooks/use_nav_links.ts index eaa643603f86..625e83622382 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_nav_links.ts +++ b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_nav_links.ts @@ -7,7 +7,7 @@ import { useMemo } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { useKibana } from '../common/services'; +import { useKibana } from '../services'; export const useNavLinks = () => { const { getProjectNavLinks$ } = useKibana().services; diff --git a/x-pack/plugins/serverless_security/public/lib/__mocks__/storage.ts b/x-pack/plugins/security_solution_serverless/public/common/lib/__mocks__/storage.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/lib/__mocks__/storage.ts rename to x-pack/plugins/security_solution_serverless/public/common/lib/__mocks__/storage.ts diff --git a/x-pack/plugins/serverless_security/public/lib/storage.ts b/x-pack/plugins/security_solution_serverless/public/common/lib/storage.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/lib/storage.ts rename to x-pack/plugins/security_solution_serverless/public/common/lib/storage.ts diff --git a/x-pack/plugins/serverless_security/public/common/services.tsx b/x-pack/plugins/security_solution_serverless/public/common/services.tsx similarity index 74% rename from x-pack/plugins/serverless_security/public/common/services.tsx rename to x-pack/plugins/security_solution_serverless/public/common/services.tsx index f3bfe1dbbfa1..799b2e5913c0 100644 --- a/x-pack/plugins/serverless_security/public/common/services.tsx +++ b/x-pack/plugins/security_solution_serverless/public/common/services.tsx @@ -5,20 +5,20 @@ * 2.0. */ -import { CoreStart } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import React from 'react'; import { KibanaContextProvider, useKibana as useKibanaReact, } from '@kbn/kibana-react-plugin/public'; -import type { ServerlessSecurityPluginStartDependencies } from '../types'; -import { getProjectNavLinks$, type ProjectNavLinks } from './navigation/links'; +import type { SecuritySolutionServerlessPluginStartDeps } from '../types'; +import { getProjectNavLinks$, type ProjectNavLinks } from '../navigation/links'; interface InternalServices { getProjectNavLinks$: () => ProjectNavLinks; } -export type Services = CoreStart & ServerlessSecurityPluginStartDependencies & InternalServices; +export type Services = CoreStart & SecuritySolutionServerlessPluginStartDeps & InternalServices; export const KibanaServicesProvider: React.FC<{ services: Services; @@ -30,7 +30,7 @@ export const useKibana = () => useKibanaReact(); export const createServices = ( core: CoreStart, - pluginsStart: ServerlessSecurityPluginStartDependencies + pluginsStart: SecuritySolutionServerlessPluginStartDeps ): Services => { const { securitySolution } = pluginsStart; const projectNavLinks$ = getProjectNavLinks$(securitySolution.getNavLinks$()); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/__mocks__/card_step.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/card_step.tsx similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/__mocks__/card_step.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/card_step.tsx diff --git a/x-pack/plugins/serverless_security/public/components/get_started/__mocks__/product_switch.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/product_switch.tsx similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/__mocks__/product_switch.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/product_switch.tsx diff --git a/x-pack/plugins/serverless_security/public/lib/get_started/__mocks__/storage.ts b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/lib/get_started/__mocks__/storage.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts diff --git a/x-pack/plugins/serverless_security/public/components/get_started/__mocks__/toggle_panel.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/toggle_panel.tsx similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/__mocks__/toggle_panel.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/toggle_panel.tsx diff --git a/x-pack/plugins/serverless_security/public/components/get_started/__mocks__/welcome_panel.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/welcome_panel.tsx similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/__mocks__/welcome_panel.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/welcome_panel.tsx diff --git a/x-pack/plugins/serverless_security/public/components/get_started/card_item.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx similarity index 92% rename from x-pack/plugins/serverless_security/public/components/get_started/card_item.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx index aacd3f520d9c..c132eb72bb35 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/card_item.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx @@ -7,8 +7,9 @@ import React from 'react'; import { render } from '@testing-library/react'; import { CardItem } from './card_item'; -import { CardId, GetSetUpCardId, IntroductionSteps, SectionId, StepId } from './types'; -import { EuiThemeComputed } from '@elastic/eui'; +import type { CardId, StepId } from './types'; +import { GetSetUpCardId, IntroductionSteps, SectionId } from './types'; +import type { EuiThemeComputed } from '@elastic/eui'; jest.mock('./card_step'); describe('CardItemComponent', () => { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/card_item.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx similarity index 90% rename from x-pack/plugins/serverless_security/public/components/get_started/card_item.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx index 4abb71354b64..2dbd6da34d99 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/card_item.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx @@ -5,18 +5,11 @@ * 2.0. */ -import { - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - EuiText, - EuiThemeComputed, - EuiTitle, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel, EuiText, EuiTitle } from '@elastic/eui'; +import type { EuiThemeComputed } from '@elastic/eui'; import React, { useCallback, useState } from 'react'; import { css } from '@emotion/react'; -import { CardId, SectionId, StepId } from './types'; +import type { CardId, SectionId, StepId } from './types'; import * as i18n from './translations'; import { CardStep } from './card_step'; import { getSections } from './sections'; @@ -102,7 +95,10 @@ const CardItemComponent: React.FC<{ {i18n.STEPS_LEFT(stepsLeft)} )} {timeInMins != null && timeInMins > 0 && ( - • {i18n.STEP_TIME_MIN(timeInMins)} + + {' • '} + {i18n.STEP_TIME_MIN(timeInMins)} + )} diff --git a/x-pack/plugins/serverless_security/public/components/get_started/card_step.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx similarity index 94% rename from x-pack/plugins/serverless_security/public/components/get_started/card_step.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx index d5fe2433a9fa..9e72370f179e 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/card_step.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.test.tsx @@ -7,7 +7,8 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { CardStep } from './card_step'; -import { GetSetUpCardId, IntroductionSteps, SectionId, StepId } from './types'; +import type { StepId } from './types'; +import { GetSetUpCardId, IntroductionSteps, SectionId } from './types'; describe('CardStepComponent', () => { const step = { @@ -18,7 +19,7 @@ describe('CardStepComponent', () => { { id: 'badge2', name: 'Badge 2' }, ], description: ['Description line 1', 'Description line 2'], - splitPanel:
    Split Panel
    , + splitPanel:
    {'Split Panel'}
    , }; const onStepClicked = jest.fn(); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/card_step.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx similarity index 97% rename from x-pack/plugins/serverless_security/public/components/get_started/card_step.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx index 67d683edaac5..137895b9bb07 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/card_step.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step.tsx @@ -18,7 +18,7 @@ import { import { css } from '@emotion/react'; import React, { useCallback, useState } from 'react'; -import { CardId, SectionId, Step, StepId } from './types'; +import type { CardId, SectionId, Step, StepId } from './types'; import step from './images/step.svg'; const CardStepComponent: React.FC<{ diff --git a/x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/get_started.test.tsx similarity index 94% rename from x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/get_started.test.tsx index ddf0b1798522..51896789c6b3 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/get_started.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { GetStartedComponent } from './get_started'; -import { SecurityProductTypes } from '../../../common/config'; +import type { SecurityProductTypes } from '../../common/config'; jest.mock('./toggle_panel'); jest.mock('./welcome_panel'); @@ -32,7 +32,7 @@ describe('GetStartedComponent', () => { const { getByText } = render(); const pageTitle = getByText('Welcome'); - const subtitle = getByText('Let’s get started'); + const subtitle = getByText(`Let's get started`); const description = getByText( `Set up your Elastic Security workspace. Use the toggles below to curate a list of tasks that best fits your environment` ); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx similarity index 93% rename from x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx index b29ba3b30d6a..55550495e6e1 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx @@ -17,15 +17,17 @@ import { GET_STARTED_PAGE_SUBTITLE, GET_STARTED_PAGE_TITLE, } from './translations'; -import { SecurityProductTypes } from '../../../common/config'; +import type { SecurityProductTypes } from '../../common/config'; import { ProductSwitch } from './product_switch'; import { useTogglePanel } from './use_toggle_panel'; const CONTENT_WIDTH = 1150; -export const GetStartedComponent: React.FC<{ productTypes: SecurityProductTypes }> = ({ - productTypes, -}) => { +export interface GetStartedProps { + productTypes: SecurityProductTypes; +} + +export const GetStartedComponent: React.FC = ({ productTypes }) => { const { euiTheme } = useEuiTheme(); const shadow = useEuiShadow('s'); const { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts similarity index 98% rename from x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts index f5511d4eaa85..050d42fbec57 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts @@ -12,19 +12,15 @@ import { setupCards, updateCard, } from './helpers'; +import type { ActiveCards, Card, CardId, Section, StepId } from './types'; import { - ActiveCards, - Card, - CardId, GetMoreFromElasticSecurityCardId, GetSetUpCardId, IntroductionSteps, - Section, SectionId, - StepId, } from './types'; import * as sectionsConfigs from './sections'; -import { ProductLine } from '../../../common/config'; +import { ProductLine } from '../../common/product'; const mockSections = jest.spyOn(sectionsConfigs, 'getSections'); describe('getCardTimeInMinutes', () => { it('should calculate the total time in minutes for a card correctly', () => { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/helpers.ts b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts similarity index 92% rename from x-pack/plugins/serverless_security/public/components/get_started/helpers.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts index 0042550a34c4..202e00dc1a40 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/helpers.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { ProductLine } from '../../../common/config'; +import type { ProductLine } from '../../common/product'; import { getSections } from './sections'; -import { ActiveCard, ActiveCards, Card, CardId, SectionId, StepId } from './types'; +import type { ActiveCard, ActiveCards, Card, CardId, SectionId, StepId } from './types'; export const getCardTimeInMinutes = (card: Card, stepsDone: Set) => card.steps?.reduce( (totalMin, { timeInMinutes, id: stepId }) => - (totalMin += stepsDone.has(stepId) ? 0 : timeInMinutes ?? 0), + totalMin + (stepsDone.has(stepId) ? 0 : timeInMinutes ?? 0), 0 ) ?? 0; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/images/invite.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/invite.svg similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/images/invite.svg rename to x-pack/plugins/security_solution_serverless/public/get_started/images/invite.svg diff --git a/x-pack/plugins/serverless_security/public/components/get_started/images/progress.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/progress.svg similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/images/progress.svg rename to x-pack/plugins/security_solution_serverless/public/get_started/images/progress.svg diff --git a/x-pack/plugins/serverless_security/public/components/get_started/images/protect.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/protect.svg similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/images/protect.svg rename to x-pack/plugins/security_solution_serverless/public/get_started/images/protect.svg diff --git a/x-pack/plugins/serverless_security/public/components/get_started/images/respond.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/respond.svg similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/images/respond.svg rename to x-pack/plugins/security_solution_serverless/public/get_started/images/respond.svg diff --git a/x-pack/plugins/serverless_security/public/components/get_started/images/step.svg b/x-pack/plugins/security_solution_serverless/public/get_started/images/step.svg similarity index 100% rename from x-pack/plugins/serverless_security/public/components/get_started/images/step.svg rename to x-pack/plugins/security_solution_serverless/public/get_started/images/step.svg diff --git a/x-pack/plugins/serverless_security/public/components/get_started/index.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/index.tsx similarity index 53% rename from x-pack/plugins/serverless_security/public/components/get_started/index.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/index.tsx index 0ee8c7a1ce62..c457d47a3da3 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/index.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/index.tsx @@ -7,18 +7,18 @@ import React from 'react'; -import { KibanaServicesProvider, type Services } from '../../common/services'; -import type { GetStartedComponent } from './types'; +import { KibanaServicesProvider, type Services } from '../common/services'; import { GetStarted } from './lazy'; -import { SecurityProductTypes } from '../../../common/config'; +import type { SecurityProductTypes } from '../../common/config'; export const getSecurityGetStartedComponent = ( services: Services, productTypes: SecurityProductTypes -): GetStartedComponent => { - return () => ( - - - - ); -}; +): React.ComponentType => + function GetStartedComponent() { + return ( + + + + ); + }; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/lazy.tsx similarity index 80% rename from x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/lazy.tsx index 3130bbe272f5..99f89ada0055 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/lazy.tsx @@ -6,13 +6,13 @@ */ import React, { lazy, Suspense } from 'react'; import { EuiLoadingLogo } from '@elastic/eui'; -import { SecurityProductTypes } from '../../../common/config'; +import type { GetStartedProps } from './get_started'; const GetStartedLazy = lazy(() => import('./get_started')); const centerLogoStyle = { display: 'flex', margin: 'auto' }; -export const GetStarted = ({ productTypes }: { productTypes: SecurityProductTypes }) => ( +export const GetStarted = ({ productTypes }: GetStartedProps) => ( }> diff --git a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx similarity index 95% rename from x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx index 29c01ddce537..5c54c87ccdd2 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { ProductSwitch } from './product_switch'; -import { EuiThemeComputed } from '@elastic/eui'; -import { ProductLine } from '../../../common/config'; +import type { EuiThemeComputed } from '@elastic/eui'; +import { ProductLine } from '../../common/product'; describe('ProductSwitch', () => { const onProductSwitchChangedMock = jest.fn(); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.tsx similarity index 91% rename from x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/product_switch.tsx index 822e5f3e69ca..62f58caaacd6 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.tsx @@ -5,12 +5,12 @@ * 2.0. */ -import { EuiPanel, EuiSwitch, EuiText, EuiThemeComputed, EuiTitle } from '@elastic/eui'; +import { EuiPanel, EuiSwitch, EuiText, EuiTitle, type EuiThemeComputed } from '@elastic/eui'; import { css } from '@emotion/react'; import React, { useMemo } from 'react'; -import { ProductLine } from '../../../common/config'; +import { ProductLine } from '../../common/product'; import * as i18n from './translations'; -import { Switch } from './types'; +import type { Switch } from './types'; const switches: Switch[] = [ { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts similarity index 96% rename from x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts index 259923080bfa..fe38c68a1a16 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ProductLine } from '../../../common/config'; +import { ProductLine } from '../../common/product'; import { reducer, getFinishedStepsInitialStates, @@ -13,16 +13,16 @@ import { getActiveCardsInitialStates, } from './reducer'; import { - ActiveCards, - CardId, GetSetUpCardId, GetStartedPageActions, IntroductionSteps, SectionId, - StepId, - ToggleProductAction, - AddFinishedStepAction, GetMoreFromElasticSecurityCardId, + type ActiveCards, + type CardId, + type StepId, + type ToggleProductAction, + type AddFinishedStepAction, } from './types'; describe('reducer', () => { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx similarity index 93% rename from x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx index 403bfb85e0a9..f59911aa1180 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx @@ -5,15 +5,15 @@ * 2.0. */ -import { ProductLine } from '../../../common/config'; +import type { ProductLine } from '../../common/product'; import { setupCards, updateCard } from './helpers'; import { - CardId, + type CardId, + type StepId, + type ToggleProductAction, + type TogglePanelReducer, + type AddFinishedStepAction, GetStartedPageActions, - StepId, - ToggleProductAction, - TogglePanelReducer, - AddFinishedStepAction, } from './types'; export const reducer = ( diff --git a/x-pack/plugins/serverless_security/public/components/get_started/sections.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx similarity index 95% rename from x-pack/plugins/serverless_security/public/components/get_started/sections.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx index 33dbb6f62aa4..11b15264d53e 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/sections.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx @@ -5,18 +5,17 @@ * 2.0. */ import React from 'react'; - import { - Section, SectionId, GetMoreFromElasticSecurityCardId, GetSetUpCardId, IntroductionSteps, + type Section, } from './types'; import * as i18n from './translations'; import respond from './images/respond.svg'; import protect from './images/protect.svg'; -import { ProductLine } from '../../../common/config'; +import { ProductLine } from '../../common/product'; export const introductionSteps = [ { @@ -61,7 +60,7 @@ export const sections: Section[] = [ id: GetSetUpCardId.introduction, steps: introductionSteps, timeInMins: introductionSteps.reduce( - (totalMin, { timeInMinutes }) => (totalMin += timeInMinutes), + (totalMin, { timeInMinutes }) => totalMin + timeInMinutes, 0 ), stepsLeft: introductionSteps.length, diff --git a/x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts b/x-pack/plugins/security_solution_serverless/public/get_started/storage.test.ts similarity index 95% rename from x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/storage.test.ts index e55c4ef28948..a5d013bd5a4e 100644 --- a/x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/storage.test.ts @@ -6,12 +6,12 @@ */ import { getStartedStorage } from './storage'; -import { GetSetUpCardId, IntroductionSteps, StepId } from '../../components/get_started/types'; -import { storage } from '../storage'; -import { MockStorage } from '../__mocks__/storage'; -import { ProductLine } from '../../../common/config'; +import { GetSetUpCardId, IntroductionSteps, type StepId } from './types'; +import { storage } from '../common/lib/storage'; +import type { MockStorage } from '../common/lib/__mocks__/storage'; +import { ProductLine } from '../../common/product'; -jest.mock('../storage'); +jest.mock('../common/lib/storage'); describe('useStorage', () => { const mockStorage = storage as unknown as MockStorage; diff --git a/x-pack/plugins/serverless_security/public/lib/get_started/storage.ts b/x-pack/plugins/security_solution_serverless/public/get_started/storage.ts similarity index 77% rename from x-pack/plugins/serverless_security/public/lib/get_started/storage.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/storage.ts index 1d9c52813c83..e01a0c7064df 100644 --- a/x-pack/plugins/serverless_security/public/lib/get_started/storage.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/storage.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { ProductLine } from '../../../common/config'; -import { CardId, StepId } from '../../components/get_started/types'; -import { storage } from '../storage'; +import type { ProductLine } from '../../common/product'; +import type { CardId, StepId } from './types'; +import { storage } from '../common/lib/storage'; export const ACTIVE_PRODUCTS_STORAGE_KEY = 'ACTIVE_PRODUCTS'; export const FINISHED_STEPS_STORAGE_KEY = 'FINISHED_STEPS'; @@ -15,11 +15,10 @@ export const FINISHED_STEPS_STORAGE_KEY = 'FINISHED_STEPS'; export const getStartedStorage = { getActiveProductsFromStorage: () => { const activeProducts: ProductLine[] = storage.get(ACTIVE_PRODUCTS_STORAGE_KEY); - return activeProducts ?? new Array(); + return activeProducts ?? []; }, toggleActiveProductsInStorage: (productId: ProductLine) => { - const activeProducts: ProductLine[] = - storage.get(ACTIVE_PRODUCTS_STORAGE_KEY) ?? new Array(); + const activeProducts: ProductLine[] = storage.get(ACTIVE_PRODUCTS_STORAGE_KEY) ?? []; const index = activeProducts.indexOf(productId); if (index < 0) { activeProducts.push(productId); @@ -31,7 +30,7 @@ export const getStartedStorage = { }, getFinishedStepsFromStorageByCardId: (cardId: CardId) => { const finishedSteps = storage.get(FINISHED_STEPS_STORAGE_KEY) ?? {}; - const card: StepId[] = finishedSteps[cardId] ?? new Array(); + const card: StepId[] = finishedSteps[cardId] ?? []; return card; }, getAllFinishedStepsFromStorage: () => { @@ -41,7 +40,7 @@ export const getStartedStorage = { }, addFinishedStepToStorage: (cardId: CardId, stepId: StepId) => { const finishedSteps: Record = storage.get(FINISHED_STEPS_STORAGE_KEY) ?? {}; - const card: StepId[] = finishedSteps[cardId] ?? new Array(); + const card: StepId[] = finishedSteps[cardId] ?? []; if (card.indexOf(stepId) < 0) { card.push(stepId); storage.set(FINISHED_STEPS_STORAGE_KEY, { ...finishedSteps, [cardId]: card }); @@ -49,7 +48,7 @@ export const getStartedStorage = { }, removeFinishedStepFromStorage: (cardId: CardId, stepId: StepId) => { const finishedSteps = storage.get(FINISHED_STEPS_STORAGE_KEY) ?? {}; - const steps: StepId[] = finishedSteps[cardId] ?? new Array(); + const steps: StepId[] = finishedSteps[cardId] ?? []; const index = steps.indexOf(stepId); if (index >= 0) { steps.splice(index, 1); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.test.tsx similarity index 93% rename from x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.test.tsx index 1a924b70fc45..57fead23ad57 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.test.tsx @@ -8,8 +8,9 @@ import React from 'react'; import { render } from '@testing-library/react'; import { TogglePanel } from './toggle_panel'; import { useSetUpCardSections } from './use_setup_cards'; -import { ActiveCards, CardId, GetSetUpCardId, IntroductionSteps, SectionId, StepId } from './types'; -import { ProductLine } from '../../../common/config'; +import type { ActiveCards, CardId, StepId } from './types'; +import { GetSetUpCardId, IntroductionSteps, SectionId } from './types'; +import { ProductLine } from '../../common/product'; jest.mock('@elastic/eui', () => ({ ...jest.requireActual('@elastic/eui'), @@ -17,8 +18,6 @@ jest.mock('@elastic/eui', () => ({ useEuiShadow: jest.fn(), })); -jest.mock('../../lib/get_started/storage'); - jest.mock('./use_setup_cards', () => ({ useSetUpCardSections: jest.fn(), })); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.tsx similarity index 93% rename from x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.tsx index af365b83bd80..a110b33f8754 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.tsx @@ -13,8 +13,8 @@ import { css } from '@emotion/react'; import * as i18n from './translations'; import { useSetUpCardSections } from './use_setup_cards'; -import { ActiveCards, CardId, IntroductionSteps, SectionId } from './types'; -import { ProductLine } from '../../../common/config'; +import type { ActiveCards, CardId, IntroductionSteps, SectionId } from './types'; +import type { ProductLine } from '../../common/product'; const TogglePanelComponent: React.FC<{ finishedSteps: Record>; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/translations.ts b/x-pack/plugins/security_solution_serverless/public/get_started/translations.ts similarity index 58% rename from x-pack/plugins/serverless_security/public/components/get_started/translations.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/translations.ts index 0f30a03a2ec0..5f30b5b135d1 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/translations.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/translations.ts @@ -7,206 +7,215 @@ import { i18n } from '@kbn/i18n'; -export const GET_STARTED_PAGE_TITLE = i18n.translate('xpack.serverlessSecurity.getStarted.title', { - defaultMessage: `Welcome`, -}); +export const GET_STARTED_PAGE_TITLE = i18n.translate( + 'xpack.securitySolutionServerless.getStarted.title', + { + defaultMessage: `Welcome`, + } +); export const GET_STARTED_PAGE_SUBTITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.subTitle', + 'xpack.securitySolutionServerless.getStarted.subTitle', { - defaultMessage: `Let’s get started`, + defaultMessage: `Let's get started`, } ); export const GET_STARTED_PAGE_DESCRIPTION = i18n.translate( - 'xpack.serverlessSecurity.getStarted.description', + 'xpack.securitySolutionServerless.getStarted.description', { defaultMessage: `Set up your Elastic Security workspace. Use the toggles below to curate a list of tasks that best fits your environment`, } ); export const WELCOME_PANEL_PROJECT_CREATED_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.welcomePanel.projectCreated.title', + 'xpack.securitySolutionServerless.getStarted.welcomePanel.projectCreated.title', { defaultMessage: `Project created`, } ); export const WELCOME_PANEL_PROJECT_CREATED_DESCRIPTION = i18n.translate( - 'xpack.serverlessSecurity.getStarted.welcomePanel.projectCreated.description', + 'xpack.securitySolutionServerless.getStarted.welcomePanel.projectCreated.description', { defaultMessage: `View all projects here.`, } ); export const WELCOME_PANEL_INVITE_YOUR_TEAM_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.welcomePanel.inviteYourTeam.title', + 'xpack.securitySolutionServerless.getStarted.welcomePanel.inviteYourTeam.title', { defaultMessage: 'Invite your team', } ); export const WELCOME_PANEL_INVITE_YOUR_TEAM_DESCRIPTION = i18n.translate( - 'xpack.serverlessSecurity.getStarted.welcomePanel.inviteYourTeam.description', + 'xpack.securitySolutionServerless.getStarted.welcomePanel.inviteYourTeam.description', { defaultMessage: `Boost security through collaboration`, } ); export const WELCOME_PANEL_PROGRESS_TRACKER_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.welcomePanel.progressTracker.title', + 'xpack.securitySolutionServerless.getStarted.welcomePanel.progressTracker.title', { defaultMessage: 'Progress tracker', } ); export const STEP_TIME_MIN = (min: number) => - i18n.translate('xpack.serverlessSecurity.getStarted.togglePanel.progressTracker.stepTimeMin', { - defaultMessage: 'About {min} {min, plural, =1 {min} other {mins}}', - values: { min }, - }); + i18n.translate( + 'xpack.securitySolutionServerless.getStarted.togglePanel.progressTracker.stepTimeMin', + { + defaultMessage: 'About {min} {min, plural, =1 {min} other {mins}}', + values: { min }, + } + ); export const STEPS_LEFT = (steps: number) => - i18n.translate('xpack.serverlessSecurity.getStarted.togglePanel.progressTracker.stepsLeft', { - defaultMessage: '{steps} {steps, plural, =1 {step} other {steps}} left', - values: { steps }, - }); + i18n.translate( + 'xpack.securitySolutionServerless.getStarted.togglePanel.progressTracker.stepsLeft', + { + defaultMessage: '{steps} {steps, plural, =1 {step} other {steps}} left', + values: { steps }, + } + ); export const GET_SET_UP_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.getSetUp.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.getSetUp.title', { defaultMessage: 'Get set up', } ); export const INTRODUCTION_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.introduction.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.introduction.title', { defaultMessage: 'introduction', } ); export const WATCH_OVERVIEW_VIDEO_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.watchOverviewVideo.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.watchOverviewVideo.title', { defaultMessage: 'Watch the overview video', } ); export const PRODUCT_BADGE_ANALYTICS = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.productBadge.analytics', + 'xpack.securitySolutionServerless.getStarted.togglePanel.productBadge.analytics', { defaultMessage: 'Analytics', } ); export const PRODUCT_BADGE_CLOUD = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.productBadge.cloud', + 'xpack.securitySolutionServerless.getStarted.togglePanel.productBadge.cloud', { defaultMessage: 'Cloud', } ); export const PRODUCT_BADGE_EDR = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.productBadge.edr', + 'xpack.securitySolutionServerless.getStarted.togglePanel.productBadge.edr', { defaultMessage: 'EDR', } ); export const WATCH_OVERVIEW_VIDEO_DESCRIPTION1 = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.watchOverviewVideo.description1', + 'xpack.securitySolutionServerless.getStarted.togglePanel.watchOverviewVideo.description1', { defaultMessage: `Elastic security keeps your organization’s data safe from attack. `, } ); export const WATCH_OVERVIEW_VIDEO_DESCRIPTION2 = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.watchOverviewVideo.description2', + 'xpack.securitySolutionServerless.getStarted.togglePanel.watchOverviewVideo.description2', { defaultMessage: `Our unified security platform combines Analytics, EDR, and cloud security capabilities into a single SaaS product, providing organizations with a comprehensive solution to protect against a wide range of security threats. With centralized management, real-time threat detection and response, and scalability, our platform can help organizations improve their security posture and reduce the risk of data breaches.`, } ); export const WATCH_OVERVIEW_VIDEO_DESCRIPTION3 = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.watchOverviewVideo.description3', + 'xpack.securitySolutionServerless.getStarted.togglePanel.watchOverviewVideo.description3', { defaultMessage: `Watch the video to explore the core features that allow you to keep your data safe.`, } ); export const WATCH_OVERVIEW_VIDEO_HEADER = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.watchOverviewVideo.header', + 'xpack.securitySolutionServerless.getStarted.togglePanel.watchOverviewVideo.header', { defaultMessage: 'Elastic Security', } ); export const BRING_IN_YOUR_DATA_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.bringInYourData.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.bringInYourData.title', { defaultMessage: 'Bring in your data', } ); export const ACTIVATE_AND_CREATE_RULES_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.activateAndCreateRules.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.activateAndCreateRules.title', { defaultMessage: 'Activate and create rules', } ); export const PROTECT_YOUR_ENVIRONMENT_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.protectYourEnvironmentInRealtime.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.protectYourEnvironmentInRealtime.title', { defaultMessage: 'Protect your environment in realtime', } ); export const GET_MORE_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.getMoreFromElasticSecurity.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.getMoreFromElasticSecurity.title', { defaultMessage: 'Get more from Elastic Security', } ); export const MASTER_THE_INVESTIGATION_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.masterTheInvestigationsWorkflow.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.masterTheInvestigationsWorkflow.title', { defaultMessage: 'Master the investigations workflow', } ); export const RESPOND_TO_THREATS_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.respondToThreatsWithAutomation.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.respondToThreatsWithAutomation.title', { defaultMessage: 'Respond to threats with automation', } ); export const OPTIMIZE_YOUR_WORKSPACE_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.optimizeYourWorkspace.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.optimizeYourWorkspace.title', { defaultMessage: 'Optimize your workspace', } ); export const TOGGLE_PANEL_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStartedProductLabel.title', + 'xpack.securitySolutionServerless.getStartedProductLabel.title', { defaultMessage: `Curate your Get Started experience:`, } ); export const ANALYTICS_SWITCH_LABEL = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.switch.analytics.label', + 'xpack.securitySolutionServerless.getStarted.togglePanel.switch.analytics.label', { defaultMessage: 'Analytics', } ); export const CLOUD_SWITCH_LABEL = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.switch.cloud.label', + 'xpack.securitySolutionServerless.getStarted.togglePanel.switch.cloud.label', { defaultMessage: 'Cloud', @@ -214,21 +223,21 @@ export const CLOUD_SWITCH_LABEL = i18n.translate( ); export const ENDPOINT_SWITCH_LABEL = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.switch.endpoint.label', + 'xpack.securitySolutionServerless.getStarted.togglePanel.switch.endpoint.label', { defaultMessage: 'Endpoint', } ); export const TOGGLE_PANEL_EMPTY_TITLE = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.empty.title', + 'xpack.securitySolutionServerless.getStarted.togglePanel.empty.title', { defaultMessage: `Hmm, there doesn't seem to be anything there`, } ); export const TOGGLE_PANEL_EMPTY_DESCRIPTION = i18n.translate( - 'xpack.serverlessSecurity.getStarted.togglePanel.empty.description', + 'xpack.securitySolutionServerless.getStarted.togglePanel.empty.description', { defaultMessage: `Switch on a toggle to continue your curated "Get Started" experience`, } diff --git a/x-pack/plugins/serverless_security/public/components/get_started/types.ts b/x-pack/plugins/security_solution_serverless/public/get_started/types.ts similarity index 88% rename from x-pack/plugins/serverless_security/public/components/get_started/types.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/types.ts index d19041432569..69c970f39813 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/types.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/types.ts @@ -5,14 +5,9 @@ * 2.0. */ -import { EuiIconProps } from '@elastic/eui'; -import React from 'react'; -import { ProductLine } from '../../../common/config'; - -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type GetStartedComponentProps = {}; - -export type GetStartedComponent = (props?: GetStartedComponentProps) => JSX.Element; +import type { EuiIconProps } from '@elastic/eui'; +import type React from 'react'; +import type { ProductLine } from '../../common/product'; export interface HeaderSection { description?: string; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/use_setup_cards.test.tsx similarity index 94% rename from x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/use_setup_cards.test.tsx index 6e726fd9a002..efb07e0ade09 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/use_setup_cards.test.tsx @@ -6,16 +6,14 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { EuiThemeComputed } from '@elastic/eui'; +import type { EuiThemeComputed } from '@elastic/eui'; import { useSetUpCardSections } from './use_setup_cards'; +import type { ActiveCards, CardId, StepId } from './types'; import { - ActiveCards, - CardId, GetMoreFromElasticSecurityCardId, GetSetUpCardId, IntroductionSteps, SectionId, - StepId, } from './types'; const mockEuiTheme: EuiThemeComputed = { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/use_setup_cards.tsx similarity index 93% rename from x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/use_setup_cards.tsx index bd762042196e..5c45e75ea4f9 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/use_setup_cards.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import { EuiSpacer, EuiThemeComputed } from '@elastic/eui'; +import type { EuiThemeComputed } from '@elastic/eui'; +import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; import React, { useCallback } from 'react'; import { css } from '@emotion/react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; -import { ActiveCards, CardId, SectionId, StepId } from './types'; +import type { ActiveCards, CardId, SectionId, StepId } from './types'; import { CardItem } from './card_item'; import { getSections } from './sections'; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.test.tsx similarity index 97% rename from x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.test.tsx index 620100939593..41131ec5c545 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.test.tsx @@ -6,8 +6,9 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; import { useTogglePanel } from './use_toggle_panel'; -import { getStartedStorage } from '../../lib/get_started/storage'; -import { ProductLine, SecurityProductTypes } from '../../../common/config'; +import { getStartedStorage } from './storage'; +import { ProductLine } from '../../common/product'; +import type { SecurityProductTypes } from '../../common/config'; import { GetMoreFromElasticSecurityCardId, GetSetUpCardId, @@ -15,7 +16,7 @@ import { SectionId, } from './types'; -jest.mock('../../lib/get_started/storage'); +jest.mock('./storage'); describe('useTogglePanel', () => { const productTypes = [ diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.tsx similarity index 90% rename from x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.tsx index f449478572c4..4ea767ae498b 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.tsx @@ -6,15 +6,17 @@ */ import { useCallback, useMemo, useReducer } from 'react'; -import { ProductLine, SecurityProductTypes } from '../../../common/config'; -import { getStartedStorage } from '../../lib/get_started/storage'; +import { ProductLine } from '../../common/product'; +import type { SecurityProductTypes } from '../../common/config'; +import { getStartedStorage } from './storage'; import { getActiveCardsInitialStates, getActiveSectionsInitialStates, getFinishedStepsInitialStates, reducer, } from './reducer'; -import { CardId, GetStartedPageActions, SectionId, StepId, Switch } from './types'; +import type { CardId, SectionId, StepId, Switch } from './types'; +import { GetStartedPageActions } from './types'; export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProductTypes }) => { const { diff --git a/x-pack/plugins/serverless_security/public/components/get_started/welcome_panel.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel.test.tsx similarity index 96% rename from x-pack/plugins/serverless_security/public/components/get_started/welcome_panel.test.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel.test.tsx index 27a68f640e17..a15c70df3342 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/welcome_panel.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { render, RenderResult } from '@testing-library/react'; +import { render, type RenderResult } from '@testing-library/react'; import { WelcomePanel } from './welcome_panel'; jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/welcome_panel.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel.tsx similarity index 98% rename from x-pack/plugins/serverless_security/public/components/get_started/welcome_panel.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel.tsx index c6e25a085998..79eecfbac7a3 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/welcome_panel.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel.tsx @@ -10,7 +10,7 @@ import { css } from '@emotion/react'; import React from 'react'; import progress from './images/progress.svg'; import invite from './images/invite.svg'; -import { HeaderSection } from './types'; +import type { HeaderSection } from './types'; import { WELCOME_PANEL_PROJECT_CREATED_TITLE, WELCOME_PANEL_PROJECT_CREATED_DESCRIPTION, diff --git a/x-pack/plugins/serverless_security/public/index.ts b/x-pack/plugins/security_solution_serverless/public/index.ts similarity index 58% rename from x-pack/plugins/serverless_security/public/index.ts rename to x-pack/plugins/security_solution_serverless/public/index.ts index 1a6466261b1b..20c00e698533 100644 --- a/x-pack/plugins/serverless_security/public/index.ts +++ b/x-pack/plugins/security_solution_serverless/public/index.ts @@ -5,13 +5,16 @@ * 2.0. */ -import { PluginInitializerContext } from '@kbn/core/public'; -import { ServerlessSecurityPlugin } from './plugin'; +import type { PluginInitializerContext } from '@kbn/core/public'; +import { SecuritySolutionServerlessPlugin } from './plugin'; // This exports static code and TypeScript types, // as well as, Kibana Platform `plugin()` initializer. export function plugin(initializerContext: PluginInitializerContext) { - return new ServerlessSecurityPlugin(initializerContext); + return new SecuritySolutionServerlessPlugin(initializerContext); } -export type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart } from './types'; +export type { + SecuritySolutionServerlessPluginSetup, + SecuritySolutionServerlessPluginStart, +} from './types'; diff --git a/x-pack/plugins/security_solution_serverless/public/jest.config.js b/x-pack/plugins/security_solution_serverless/public/jest.config.js new file mode 100644 index 000000000000..cf3ad9340c14 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/jest.config.js @@ -0,0 +1,29 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + /** all nested directories have their own Jest config file */ + testMatch: [ + '/x-pack/plugins/security_solution_serverless/public/**/*.test.{js,mjs,ts,tsx}', + ], + roots: ['/x-pack/plugins/security_solution_serverless/public'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution_serverless/public', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution_serverless/public/**/*.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/public/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/public/{__test__,__snapshots__,__examples__,*mock*,tests,test_helpers,integration_tests,types}/**/*', + '!/x-pack/plugins/security_solution_serverless/public/*mock*.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/public/*.test.{ts,tsx}', + '!/x-pack/plugins/security_solution_serverless/public/*.d.ts', + '!/x-pack/plugins/security_solution_serverless/public/*.config.ts', + '!/x-pack/plugins/security_solution_serverless/public/index.{js,ts,tsx}', + ], +}; diff --git a/x-pack/plugins/serverless_security/public/common/navigation/breadcrumbs.ts b/x-pack/plugins/security_solution_serverless/public/navigation/breadcrumbs.ts similarity index 90% rename from x-pack/plugins/serverless_security/public/common/navigation/breadcrumbs.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/breadcrumbs.ts index d4e9d13b3769..195a1a070abc 100644 --- a/x-pack/plugins/serverless_security/public/common/navigation/breadcrumbs.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/breadcrumbs.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Services } from '../services'; +import type { Services } from '../common/services'; export const subscribeBreadcrumbs = (services: Services) => { const { securitySolution, serverless } = services; diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/index.ts b/x-pack/plugins/security_solution_serverless/public/navigation/index.ts new file mode 100644 index 000000000000..226c20997ef5 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/navigation/index.ts @@ -0,0 +1,29 @@ +/* + * 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 { APP_PATH, MANAGE_PATH } from '@kbn/security-solution-plugin/common'; +import type { Services } from '../common/services'; +import { subscribeBreadcrumbs } from './breadcrumbs'; +import { subscribeNavigationTree } from './navigation_tree'; +import { getSecuritySideNavComponent } from './side_navigation'; + +const SECURITY_MANAGE_PATH = `${APP_PATH}${MANAGE_PATH}`; + +/** + * Configures the serverless project navigation + */ +export const setServerlessNavigation = (services: Services) => { + const { serverless, securitySolution, management } = services; + securitySolution.setIsSidebarEnabled(false); + management.setLandingPageRedirect(SECURITY_MANAGE_PATH); + + serverless.setProjectHome(APP_PATH); + serverless.setSideNavComponent(getSecuritySideNavComponent(services)); + + subscribeNavigationTree(services); + subscribeBreadcrumbs(services); +}; diff --git a/x-pack/plugins/serverless_security/public/common/navigation/links/index.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/index.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/common/navigation/links/index.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/links/index.ts diff --git a/x-pack/plugins/serverless_security/public/common/navigation/links/nav_links.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/common/navigation/links/nav_links.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts diff --git a/x-pack/plugins/serverless_security/public/common/navigation/links/types.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/types.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/common/navigation/links/types.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/links/types.ts diff --git a/x-pack/plugins/serverless_security/public/common/navigation/navigation_tree.test.ts b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree.test.ts similarity index 86% rename from x-pack/plugins/serverless_security/public/common/navigation/navigation_tree.test.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree.test.ts index 91d020c33270..b1e619a30fc1 100644 --- a/x-pack/plugins/serverless_security/public/common/navigation/navigation_tree.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree.test.ts @@ -6,10 +6,9 @@ */ import type { ChromeNavLink } from '@kbn/core/public'; import { APP_UI_ID, SecurityPageName } from '@kbn/security-solution-plugin/common'; -import { servicesMocks } from '../services.mock'; import { subscribeNavigationTree } from './navigation_tree'; import { BehaviorSubject } from 'rxjs'; -import { mockProjectNavLinks } from '../services.mock'; +import { mockServices, mockProjectNavLinks } from '../common/__mocks__/services.mock'; import type { ProjectNavigationLink } from './links'; const mockChromeNavLinks = jest.fn((): ChromeNavLink[] => []); @@ -21,12 +20,12 @@ const mockChromeNavLinksHas = jest.fn((id: string): boolean => mockChromeNavLinks().some((link) => link.id === id) ); -const mockServices = { - ...servicesMocks, +const testServices = { + ...mockServices, chrome: { - ...servicesMocks.chrome, + ...mockServices.chrome, navLinks: { - ...servicesMocks.chrome.navLinks, + ...mockServices.chrome.navLinks, get: mockChromeNavLinksGet, has: mockChromeNavLinksHas, getNavLinks$: mockChromeGetNavLinks, @@ -66,10 +65,10 @@ describe('subscribeNavigationTree', () => { it('should call serverless setNavigation', async () => { mockProjectNavLinks.mockReturnValueOnce([link1]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledWith({ + expect(testServices.serverless.setNavigation).toHaveBeenCalledWith({ navigationTree: [ { id: 'root', @@ -98,10 +97,10 @@ describe('subscribeNavigationTree', () => { mockChromeNavLinks.mockReturnValue([chromeNavLinkExpected]); mockProjectNavLinks.mockReturnValueOnce([externalLink]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledWith({ + expect(testServices.serverless.setNavigation).toHaveBeenCalledWith({ navigationTree: [ { id: 'root', @@ -124,10 +123,10 @@ describe('subscribeNavigationTree', () => { it('should call serverless setNavigation with nested children', async () => { mockProjectNavLinks.mockReturnValueOnce([{ ...link1, links: [link2] }]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledWith({ + expect(testServices.serverless.setNavigation).toHaveBeenCalledWith({ navigationTree: [ { id: 'root', @@ -158,20 +157,20 @@ describe('subscribeNavigationTree', () => { it('should not call serverless setNavigation when projectNavLinks is empty', async () => { mockProjectNavLinks.mockReturnValueOnce([]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).not.toHaveBeenCalled(); + expect(testServices.serverless.setNavigation).not.toHaveBeenCalled(); }); it('should not call serverless setNavigation when chrome navLinks is empty', async () => { mockChromeNavLinks.mockReturnValue([]); mockProjectNavLinks.mockReturnValueOnce([link1]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).not.toHaveBeenCalled(); + expect(testServices.serverless.setNavigation).not.toHaveBeenCalled(); }); it('should debounce updates', async () => { @@ -185,18 +184,18 @@ describe('subscribeNavigationTree', () => { mockChromeNavLinks.mockReturnValue([chromeNavLink1, chromeNavLink2, chromeNavLinkExpected]); mockProjectNavLinks.mockReturnValueOnce([linkExpected]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); chromeGetNavLinks$.next([chromeNavLink1]); chromeGetNavLinks$.next([chromeNavLink2]); chromeGetNavLinks$.next([chromeNavLinkExpected]); - expect(mockServices.serverless.setNavigation).not.toHaveBeenCalled(); + expect(testServices.serverless.setNavigation).not.toHaveBeenCalled(); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledTimes(1); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledWith({ + expect(testServices.serverless.setNavigation).toHaveBeenCalledTimes(1); + expect(testServices.serverless.setNavigation).toHaveBeenCalledWith({ navigationTree: [ { id: 'root', @@ -220,10 +219,10 @@ describe('subscribeNavigationTree', () => { mockChromeNavLinks.mockReturnValue([chromeNavLink2]); mockProjectNavLinks.mockReturnValueOnce([link1, link2]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledWith({ + expect(testServices.serverless.setNavigation).toHaveBeenCalledWith({ navigationTree: [ { id: 'root', @@ -254,10 +253,10 @@ describe('subscribeNavigationTree', () => { link2, ]); - subscribeNavigationTree(mockServices); + subscribeNavigationTree(testServices); await waitForDebounce(); - expect(mockServices.serverless.setNavigation).toHaveBeenCalledWith({ + expect(testServices.serverless.setNavigation).toHaveBeenCalledWith({ navigationTree: [ { id: 'root', diff --git a/x-pack/plugins/serverless_security/public/common/navigation/navigation_tree.ts b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree.ts similarity index 98% rename from x-pack/plugins/serverless_security/public/common/navigation/navigation_tree.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree.ts index 70464c0c53f5..82555ae54332 100644 --- a/x-pack/plugins/serverless_security/public/common/navigation/navigation_tree.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree.ts @@ -8,7 +8,7 @@ import type { ChromeNavLinks, ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; import { APP_UI_ID, SecurityPageName } from '@kbn/security-solution-plugin/common'; import { combineLatest, skipWhile, debounceTime } from 'rxjs'; -import type { Services } from '../services'; +import type { Services } from '../common/services'; import type { ProjectNavigationLink } from './links/types'; // We need to hide breadcrumbs for some pages (tabs) because they appear duplicated. diff --git a/x-pack/plugins/serverless_security/public/hooks/__mocks__/use_side_nav_items.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/__mocks__/use_side_nav_items.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/hooks/__mocks__/use_side_nav_items.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/__mocks__/use_side_nav_items.ts diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/categories.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/components/side_navigation/categories.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/index.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx similarity index 73% rename from x-pack/plugins/serverless_security/public/components/side_navigation/index.tsx rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx index 399d6ecab13d..c749131300b5 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/index.tsx +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx @@ -10,10 +10,11 @@ import type { SideNavComponent } from '@kbn/core-chrome-browser/src/project_navi import { SecuritySideNavigation } from './lazy'; import { KibanaServicesProvider, type Services } from '../../common/services'; -export const getSecuritySideNavComponent = (services: Services): SideNavComponent => { - return () => ( - - - - ); -}; +export const getSecuritySideNavComponent = (services: Services): SideNavComponent => + function SecuritySideNavComponent() { + return ( + + + + ); + }; diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/lazy.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/lazy.tsx similarity index 100% rename from x-pack/plugins/serverless_security/public/components/side_navigation/lazy.tsx rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/lazy.tsx diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.test.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation.test.tsx similarity index 85% rename from x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.test.tsx rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation.test.tsx index eef0ccb8da67..1df7e2ca3008 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation.test.tsx @@ -8,11 +8,12 @@ import React from 'react'; import { render } from '@testing-library/react'; import { SecuritySideNavigation } from './side_navigation'; -import { useSideNavItems, useSideNavSelectedId } from '../../hooks/use_side_nav_items'; +import { useSideNavItems, useSideNavSelectedId } from './use_side_nav_items'; import { SecurityPageName } from '@kbn/security-solution-plugin/common'; -import { KibanaServicesProvider } from '../../common/services.mock'; +import { ServicesWrapper } from '../../common/__mocks__/services.mock'; + +jest.mock('./use_side_nav_items'); -jest.mock('../../hooks/use_side_nav_items'); const mockUseSideNavItems = useSideNavItems as jest.Mock; const mockUseSideNavSelectedId = useSideNavSelectedId as jest.Mock; @@ -54,22 +55,22 @@ describe('SecuritySideNavigation', () => { it('should render loading when not items received', () => { mockUseSideNavItems.mockReturnValueOnce([]); - const component = render(, { wrapper: KibanaServicesProvider }); + const component = render(, { wrapper: ServicesWrapper }); expect(component.queryByTestId('sideNavLoader')).toBeInTheDocument(); }); it('should not render loading when items received', () => { - const component = render(, { wrapper: KibanaServicesProvider }); + const component = render(, { wrapper: ServicesWrapper }); expect(component.queryByTestId('sideNavLoader')).not.toBeInTheDocument(); }); it('should render the SideNav when items received', () => { - const component = render(, { wrapper: KibanaServicesProvider }); + const component = render(, { wrapper: ServicesWrapper }); expect(component.queryByTestId('solutionSideNav')).toBeInTheDocument(); }); it('should pass item props to the SolutionSideNav component', () => { - render(, { wrapper: KibanaServicesProvider }); + render(, { wrapper: ServicesWrapper }); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ @@ -79,7 +80,7 @@ describe('SecuritySideNavigation', () => { }); it('should selectedId the SolutionSideNav component', () => { - render(, { wrapper: KibanaServicesProvider }); + render(, { wrapper: ServicesWrapper }); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation.tsx similarity index 79% rename from x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation.tsx index b38ff02dd341..5ba20e26cac2 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiLoadingSpinner, useEuiTheme } from '@elastic/eui'; import { SolutionNav } from '@kbn/shared-ux-page-solution-nav'; import { SolutionSideNav } from '@kbn/security-solution-side-nav'; -import { useSideNavItems, useSideNavSelectedId } from '../../hooks/use_side_nav_items'; +import { useSideNavItems, useSideNavSelectedId } from './use_side_nav_items'; import { CATEGORIES } from './categories'; export const SecuritySideNavigation: React.FC = () => { @@ -26,19 +26,18 @@ export const SecuritySideNavigation: React.FC = () => { canBeCollapsed={false} name={'Security'} icon={'logoSecurity'} - children={ - - } closeFlyoutButtonPosition={'inside'} headingProps={{ 'data-test-subj': 'securitySolutionNavHeading', }} - /> + > + + ); }; diff --git a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.test.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.test.tsx similarity index 82% rename from x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.test.tsx rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.test.tsx index 22c0e7118ec7..2145961e36aa 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.test.tsx @@ -8,13 +8,10 @@ import { renderHook } from '@testing-library/react-hooks'; import { useSideNavItems, useSideNavSelectedId } from './use_side_nav_items'; import { SecurityPageName } from '@kbn/security-solution-plugin/common'; -import { - KibanaServicesProvider, - servicesMocks, - mockProjectNavLinks, -} from '../common/services.mock'; +import { mockServices, mockProjectNavLinks } from '../../common/__mocks__/services.mock'; -jest.mock('./use_link_props'); +jest.mock('../../common/hooks/use_link_props'); +jest.mock('../../common/services'); const mockUseLocation = jest.fn(() => ({ pathname: '/' })); jest.mock('react-router-dom', () => ({ @@ -28,12 +25,11 @@ describe('useSideNavItems', () => { }); it('should return empty items', async () => { - const { result } = renderHook(useSideNavItems, { wrapper: KibanaServicesProvider }); - + const { result } = renderHook(useSideNavItems); const items = result.current; expect(items).toEqual([]); - expect(servicesMocks.getProjectNavLinks$).toHaveBeenCalledTimes(1); + expect(mockServices.getProjectNavLinks$).toHaveBeenCalledTimes(1); }); it('should return main items', async () => { @@ -41,7 +37,7 @@ describe('useSideNavItems', () => { { id: SecurityPageName.alerts, title: 'Alerts' }, { id: SecurityPageName.case, title: 'Cases' }, ]); - const { result } = renderHook(useSideNavItems, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useSideNavItems); const items = result.current; expect(items).toEqual([ @@ -70,7 +66,7 @@ describe('useSideNavItems', () => { links: [{ id: SecurityPageName.detectionAndResponse, title: 'Detection & Response' }], }, ]); - const { result } = renderHook(useSideNavItems, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useSideNavItems); const items = result.current; expect(items).toEqual([ @@ -100,7 +96,7 @@ describe('useSideNavItems', () => { sideNavIcon: 'launch', }, ]); - const { result } = renderHook(useSideNavItems, { wrapper: KibanaServicesProvider }); + const { result } = renderHook(useSideNavItems); const items = result.current; @@ -139,10 +135,7 @@ describe('useSideNavSelectedId', () => { }, ]; - const { result } = renderHook(useSideNavSelectedId, { - wrapper: KibanaServicesProvider, - initialProps: items, - }); + const { result } = renderHook(useSideNavSelectedId, { initialProps: items }); const selectedId = result.current; expect(selectedId).toEqual(''); @@ -165,10 +158,7 @@ describe('useSideNavSelectedId', () => { }, ]; - const { result } = renderHook(useSideNavSelectedId, { - wrapper: KibanaServicesProvider, - initialProps: items, - }); + const { result } = renderHook(useSideNavSelectedId, { initialProps: items }); const selectedId = result.current; expect(selectedId).toEqual(SecurityPageName.alerts); @@ -193,10 +183,7 @@ describe('useSideNavSelectedId', () => { }, ]; - const { result } = renderHook(useSideNavSelectedId, { - wrapper: KibanaServicesProvider, - initialProps: items, - }); + const { result } = renderHook(useSideNavSelectedId, { initialProps: items }); const selectedId = result.current; expect(selectedId).toEqual(SecurityPageName.dashboards); diff --git a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts similarity index 95% rename from x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts rename to x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts index 7ec68683eb22..779dd86764d7 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts @@ -12,9 +12,9 @@ import { SolutionSideNavItemPosition, type SolutionSideNavItem, } from '@kbn/security-solution-side-nav'; -import { useKibana } from '../common/services'; -import { type GetLinkProps, useGetLinkProps } from './use_link_props'; -import { useNavLinks } from './use_nav_links'; +import { useKibana } from '../../common/services'; +import { type GetLinkProps, useGetLinkProps } from '../../common/hooks/use_link_props'; +import { useNavLinks } from '../../common/hooks/use_nav_links'; type NavigationLink = ReturnType[number]; diff --git a/x-pack/plugins/security_solution_serverless/public/plugin.ts b/x-pack/plugins/security_solution_serverless/public/plugin.ts new file mode 100644 index 000000000000..307a438c6e9c --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/plugin.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; + +import { getSecurityGetStartedComponent } from './get_started'; +import type { + SecuritySolutionServerlessPluginSetup, + SecuritySolutionServerlessPluginStart, + SecuritySolutionServerlessPluginSetupDeps, + SecuritySolutionServerlessPluginStartDeps, + ServerlessSecurityPublicConfig, +} from './types'; +import { registerUpsellings } from './upselling'; +import { createServices } from './common/services'; +import { setServerlessNavigation } from './navigation'; + +export class SecuritySolutionServerlessPlugin + implements + Plugin< + SecuritySolutionServerlessPluginSetup, + SecuritySolutionServerlessPluginStart, + SecuritySolutionServerlessPluginSetupDeps, + SecuritySolutionServerlessPluginStartDeps + > +{ + private config: ServerlessSecurityPublicConfig; + + constructor(private readonly initializerContext: PluginInitializerContext) { + this.config = this.initializerContext.config.get(); + } + + public setup( + _core: CoreSetup, + setupDeps: SecuritySolutionServerlessPluginSetupDeps + ): SecuritySolutionServerlessPluginSetup { + registerUpsellings(setupDeps.securitySolution.upselling, this.config.productTypes); + return {}; + } + + public start( + core: CoreStart, + startDeps: SecuritySolutionServerlessPluginStartDeps + ): SecuritySolutionServerlessPluginStart { + const { securitySolution } = startDeps; + const { productTypes } = this.config; + + const services = createServices(core, startDeps); + + securitySolution.setGetStartedPage(getSecurityGetStartedComponent(services, productTypes)); + + setServerlessNavigation(services); + + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/public/types.ts b/x-pack/plugins/security_solution_serverless/public/types.ts similarity index 78% rename from x-pack/plugins/serverless_security/public/types.ts rename to x-pack/plugins/security_solution_serverless/public/types.ts index 73052810127a..e65813aa68f6 100644 --- a/x-pack/plugins/serverless_security/public/types.ts +++ b/x-pack/plugins/security_solution_serverless/public/types.ts @@ -11,23 +11,23 @@ import type { PluginStart as SecuritySolutionPluginStart, } from '@kbn/security-solution-plugin/public'; import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; -import { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; +import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; import type { SecurityProductTypes } from '../common/config'; // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ServerlessSecurityPluginSetup {} +export interface SecuritySolutionServerlessPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ServerlessSecurityPluginStart {} +export interface SecuritySolutionServerlessPluginStart {} -export interface ServerlessSecurityPluginSetupDependencies { +export interface SecuritySolutionServerlessPluginSetupDeps { security: SecurityPluginSetup; securitySolution: SecuritySolutionPluginSetup; serverless: ServerlessPluginSetup; management: ManagementSetup; } -export interface ServerlessSecurityPluginStartDependencies { +export interface SecuritySolutionServerlessPluginStartDeps { security: SecurityPluginStart; securitySolution: SecuritySolutionPluginStart; serverless: ServerlessPluginStart; diff --git a/x-pack/plugins/serverless_security/public/components/upselling/hooks/use_product_type_by_pli.ts b/x-pack/plugins/security_solution_serverless/public/upselling/hooks/use_product_type_by_pli.ts similarity index 93% rename from x-pack/plugins/serverless_security/public/components/upselling/hooks/use_product_type_by_pli.ts rename to x-pack/plugins/security_solution_serverless/public/upselling/hooks/use_product_type_by_pli.ts index f1486469247c..df4a97e3b034 100644 --- a/x-pack/plugins/serverless_security/public/components/upselling/hooks/use_product_type_by_pli.ts +++ b/x-pack/plugins/security_solution_serverless/public/upselling/hooks/use_product_type_by_pli.ts @@ -6,7 +6,7 @@ */ import type { AppFeatureKey } from '@kbn/security-solution-plugin/common'; -import { PLI_APP_FEATURES } from '../../../../common/pli/pli_config'; +import { PLI_APP_FEATURES } from '../../../common/pli/pli_config'; export const useProductTypeByPLI = (requiredPLI: AppFeatureKey): string | null => { if (PLI_APP_FEATURES.security.essentials.includes(requiredPLI)) { diff --git a/x-pack/plugins/serverless_security/public/components/upselling/index.ts b/x-pack/plugins/security_solution_serverless/public/upselling/index.ts similarity index 100% rename from x-pack/plugins/serverless_security/public/components/upselling/index.ts rename to x-pack/plugins/security_solution_serverless/public/upselling/index.ts diff --git a/x-pack/plugins/serverless_security/public/components/upselling/pages/generic_upselling_page.tsx b/x-pack/plugins/security_solution_serverless/public/upselling/pages/generic_upselling_page.tsx similarity index 83% rename from x-pack/plugins/serverless_security/public/components/upselling/pages/generic_upselling_page.tsx rename to x-pack/plugins/security_solution_serverless/public/upselling/pages/generic_upselling_page.tsx index aed8d7689849..cc6440358da6 100644 --- a/x-pack/plugins/serverless_security/public/components/upselling/pages/generic_upselling_page.tsx +++ b/x-pack/plugins/security_solution_serverless/public/upselling/pages/generic_upselling_page.tsx @@ -11,16 +11,16 @@ import type { AppFeatureKey } from '@kbn/security-solution-plugin/common'; import { useProductTypeByPLI } from '../hooks/use_product_type_by_pli'; export const GenericUpsellingPage: React.FC<{ requiredPLI: AppFeatureKey }> = React.memo( - ({ requiredPLI }) => { + function GenericUpsellingPage({ requiredPLI }) { const productTypeRequired = useProductTypeByPLI(requiredPLI); return ( This is a testing component for a Serverless upselling prompt.} + title={<>{'This is a testing component for a Serverless upselling prompt.'}} body={ <> - Get {productTypeRequired} to enable this feature + {'Get'} {productTypeRequired} {'to enable this feature'}